Clientes
This commit is contained in:
parent
580beaba4c
commit
b3c14b061b
@ -1,6 +1,6 @@
|
||||
{
|
||||
"common": {
|
||||
"required": "required"
|
||||
"required": "•"
|
||||
},
|
||||
"components": {
|
||||
"taxes_multi_select": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"common": {
|
||||
"required": "requerido"
|
||||
"required": "•"
|
||||
},
|
||||
"components": {
|
||||
"taxes_multi_select": {
|
||||
|
||||
@ -47,9 +47,15 @@ export function TaxesMultiSelectField<TFormValues extends FieldValues>({
|
||||
render={({ field }) => (
|
||||
<FormItem className={cn("space-y-0", className)}>
|
||||
{label && (
|
||||
<div className='flex justify-between items-center'>
|
||||
<FormLabel className='m-0'>{label}</FormLabel>
|
||||
{required && <span className='text-xs text-destructive'>{t("common.required")}</span>}
|
||||
<div className='mb-1 flex justify-between gap-2'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<FormLabel htmlFor={name} className='m-0'>
|
||||
{label}
|
||||
</FormLabel>
|
||||
{required && (
|
||||
<span className='text-xs text-destructive'>{t("common.required")}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<FormControl>
|
||||
|
||||
@ -39,7 +39,7 @@ export class CustomerFullPresenter extends Presenter<Customer, GetCustomerByIdRe
|
||||
|
||||
legal_record: toEmptyString(customer.legalRecord, (value) => value.toPrimitive()),
|
||||
|
||||
default_taxes: customer.defaultTaxes.getAll().join(", "),
|
||||
default_taxes: customer.defaultTaxes.getAll().map((tax) => tax.toString()),
|
||||
|
||||
status: customer.isActive ? "active" : "inactive",
|
||||
language_code: customer.languageCode.toPrimitive(),
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
import { DomainError, ValidationErrorCollection, ValidationErrorDetail } from "@repo/rdx-ddd";
|
||||
import {
|
||||
DomainError,
|
||||
ValidationErrorCollection,
|
||||
ValidationErrorDetail,
|
||||
extractOrPushError,
|
||||
} from "@repo/rdx-ddd";
|
||||
import { UpdateCustomerByIdRequestDTO } from "../../../../common";
|
||||
import { CustomerPatchProps } from "../../../domain";
|
||||
|
||||
import {
|
||||
City,
|
||||
@ -34,7 +41,7 @@ import { Collection, Result, isNullishOrEmpty, toPatchField } from "@repo/rdx-ut
|
||||
*
|
||||
*/
|
||||
|
||||
export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerRequestDTO) {
|
||||
export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerByIdRequestDTO) {
|
||||
try {
|
||||
const errors: ValidationErrorDetail[] = [];
|
||||
const customerPatchProps: CustomerPatchProps = {};
|
||||
@ -83,18 +90,50 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerRequestDTO)
|
||||
);
|
||||
});
|
||||
|
||||
toPatchField(dto.email).ifSet((email) => {
|
||||
customerPatchProps.email = extractOrPushError(
|
||||
maybeFromNullableVO(email, (value) => EmailAddress.create(value)),
|
||||
"email",
|
||||
toPatchField(dto.email_primary).ifSet((email_primary) => {
|
||||
customerPatchProps.emailPrimary = extractOrPushError(
|
||||
maybeFromNullableVO(email_primary, (value) => EmailAddress.create(value)),
|
||||
"email_primary",
|
||||
errors
|
||||
);
|
||||
});
|
||||
|
||||
toPatchField(dto.phone).ifSet((phone) => {
|
||||
customerPatchProps.phone = extractOrPushError(
|
||||
maybeFromNullableVO(phone, (value) => PhoneNumber.create(value)),
|
||||
"phone",
|
||||
toPatchField(dto.email_secondary).ifSet((email_secondary) => {
|
||||
customerPatchProps.emailSecondary = extractOrPushError(
|
||||
maybeFromNullableVO(email_secondary, (value) => EmailAddress.create(value)),
|
||||
"email_secondary",
|
||||
errors
|
||||
);
|
||||
});
|
||||
|
||||
toPatchField(dto.mobile_primary).ifSet((mobile_primary) => {
|
||||
customerPatchProps.mobilePrimary = extractOrPushError(
|
||||
maybeFromNullableVO(mobile_primary, (value) => PhoneNumber.create(value)),
|
||||
"mobile_primary",
|
||||
errors
|
||||
);
|
||||
});
|
||||
|
||||
toPatchField(dto.mobile_secondary).ifSet((mobile_secondary) => {
|
||||
customerPatchProps.mobilePrimary = extractOrPushError(
|
||||
maybeFromNullableVO(mobile_secondary, (value) => PhoneNumber.create(value)),
|
||||
"mobile_secondary",
|
||||
errors
|
||||
);
|
||||
});
|
||||
|
||||
toPatchField(dto.phone_primary).ifSet((phone_primary) => {
|
||||
customerPatchProps.phonePrimary = extractOrPushError(
|
||||
maybeFromNullableVO(phone_primary, (value) => PhoneNumber.create(value)),
|
||||
"phone_primary",
|
||||
errors
|
||||
);
|
||||
});
|
||||
|
||||
toPatchField(dto.phone_secondary).ifSet((phone_secondary) => {
|
||||
customerPatchProps.phoneSecondary = extractOrPushError(
|
||||
maybeFromNullableVO(phone_secondary, (value) => PhoneNumber.create(value)),
|
||||
"phone_secondary",
|
||||
errors
|
||||
);
|
||||
});
|
||||
@ -158,7 +197,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerRequestDTO)
|
||||
return;
|
||||
}
|
||||
|
||||
defaultTaxes!.split(",").forEach((taxCode, index) => {
|
||||
defaultTaxes!.forEach((taxCode, index) => {
|
||||
const tax = extractOrPushError(TaxCode.create(taxCode), `default_taxes.${index}`, errors);
|
||||
if (tax && customerPatchProps.defaultTaxes) {
|
||||
customerPatchProps.defaultTaxes.add(tax);
|
||||
@ -173,7 +212,9 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerRequestDTO)
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
return Result.fail(new ValidationErrorCollection("Customer props mapping failed", errors));
|
||||
return Result.fail(
|
||||
new ValidationErrorCollection("Customer props mapping failed (update)", errors)
|
||||
);
|
||||
}
|
||||
|
||||
return Result.ok(customerPatchProps);
|
||||
@ -183,7 +224,7 @@ export function mapDTOToUpdateCustomerPatchProps(dto: UpdateCustomerRequestDTO)
|
||||
}
|
||||
|
||||
function mapDTOToUpdatePostalAddressPatchProps(
|
||||
dto: UpdateCustomerRequestDTO,
|
||||
dto: UpdateCustomerByIdRequestDTO,
|
||||
errors: ValidationErrorDetail[]
|
||||
): PostalAddressPatchProps | undefined {
|
||||
const postalAddressPatchProps: PostalAddressPatchProps = {};
|
||||
|
||||
@ -197,7 +197,9 @@ export class CustomerDomainMapper
|
||||
|
||||
// Si hubo errores de mapeo, devolvemos colección de validación
|
||||
if (errors.length > 0) {
|
||||
return Result.fail(new ValidationErrorCollection("Customer props mapping failed", errors));
|
||||
return Result.fail(
|
||||
new ValidationErrorCollection("Customer props mapping failed (CustomerMapper)", errors)
|
||||
);
|
||||
}
|
||||
|
||||
const customerProps: CustomerProps = {
|
||||
|
||||
@ -29,7 +29,7 @@ export const CreateCustomerRequestSchema = z.object({
|
||||
|
||||
legal_record: z.string().default(""),
|
||||
|
||||
default_taxes: z.string().default(""),
|
||||
default_taxes: z.array(z.string()).default([]),
|
||||
status: z.string().toLowerCase().default("active"),
|
||||
language_code: z.string().toLowerCase().default("es"),
|
||||
currency_code: z.string().toUpperCase().default("EUR"),
|
||||
|
||||
@ -11,6 +11,7 @@ export const UpdateCustomerByIdRequestSchema = z.object({
|
||||
name: z.string().optional(),
|
||||
trade_name: z.string().optional(),
|
||||
tin: z.string().optional(),
|
||||
default_taxes: z.array(z.string()).optional(), // completo (sustituye), o null => vaciar
|
||||
|
||||
street: z.string().optional(),
|
||||
street2: z.string().optional(),
|
||||
@ -31,7 +32,6 @@ export const UpdateCustomerByIdRequestSchema = z.object({
|
||||
|
||||
legal_record: z.string().optional(),
|
||||
|
||||
default_taxes: z.string().optional(), // completo (sustituye), o null => vaciar
|
||||
language_code: z.string().optional(),
|
||||
currency_code: z.string().optional(),
|
||||
});
|
||||
|
||||
@ -25,7 +25,7 @@ export const CreateCustomerResponseSchema = z.object({
|
||||
|
||||
legal_record: z.string(),
|
||||
|
||||
default_taxes: z.string(),
|
||||
default_taxes: z.array(z.string()),
|
||||
status: z.string(),
|
||||
language_code: z.string(),
|
||||
currency_code: z.string(),
|
||||
|
||||
@ -30,7 +30,7 @@ export const GetCustomerByIdResponseSchema = z.object({
|
||||
|
||||
legal_record: z.string(),
|
||||
|
||||
default_taxes: z.string(),
|
||||
default_taxes: z.array(z.string()),
|
||||
status: z.string(),
|
||||
language_code: z.string(),
|
||||
currency_code: z.string(),
|
||||
|
||||
@ -25,7 +25,7 @@ export const ListCustomersResponseSchema = createListViewResponseSchema(
|
||||
website: z.string(),
|
||||
|
||||
//legal_record: z.string(),
|
||||
//default_taxes: z.string(),
|
||||
//default_taxes: z.array(z.string()),
|
||||
|
||||
language_code: z.string(),
|
||||
currency_code: z.string(),
|
||||
|
||||
@ -30,7 +30,7 @@ export const UpdateCustomerByIdResponseSchema = z.object({
|
||||
|
||||
legal_record: z.string(),
|
||||
|
||||
default_taxes: z.string(),
|
||||
default_taxes: z.array(z.string()),
|
||||
language_code: z.string(),
|
||||
currency_code: z.string(),
|
||||
|
||||
|
||||
@ -79,7 +79,7 @@ export const CustomersListGrid = () => {
|
||||
size='icon'
|
||||
className='size-8'
|
||||
onClick={() => {
|
||||
navigate(`${data.id}/edit`);
|
||||
navigate(`${data.id}/edit`, { relative: "path" });
|
||||
}}
|
||||
>
|
||||
<ChevronRightIcon />
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { TaxesMultiSelectField } from "@erp/core/components";
|
||||
import { SelectField, TextAreaField } from "@repo/rdx-ui/components";
|
||||
import { SelectField } from "@repo/rdx-ui/components";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@ -23,41 +22,29 @@ export const CustomerAdditionalConfigFields = ({
|
||||
<CardTitle>{t("form_groups.preferences.title")}</CardTitle>
|
||||
<CardDescription>{t("form_groups.preferences.description")}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='grid grid-cols-1 gap-y-8 gap-x-6 @xl:grid-cols-2'>
|
||||
<TaxesMultiSelectField
|
||||
control={control}
|
||||
name='default_taxes'
|
||||
required
|
||||
label={t("form_fields.default_taxes.label")}
|
||||
placeholder={t("form_fields.default_taxes.placeholder")}
|
||||
description={t("form_fields.default_taxes.description")}
|
||||
/>
|
||||
<SelectField
|
||||
control={control}
|
||||
name='language_code'
|
||||
required
|
||||
label={t("form_fields.language_code.label")}
|
||||
placeholder={t("form_fields.language_code.placeholder")}
|
||||
description={t("form_fields.language_code.description")}
|
||||
items={[...LANGUAGE_OPTIONS]}
|
||||
/>
|
||||
<SelectField
|
||||
control={control}
|
||||
name='currency_code'
|
||||
required
|
||||
label={t("form_fields.currency_code.label")}
|
||||
placeholder={t("form_fields.currency_code.placeholder")}
|
||||
description={t("form_fields.currency_code.description")}
|
||||
items={[...CURRENCY_OPTIONS]}
|
||||
/>
|
||||
<TextAreaField
|
||||
control={control}
|
||||
name='legal_record'
|
||||
required
|
||||
label={t("form_fields.legal_record.label")}
|
||||
placeholder={t("form_fields.legal_record.placeholder")}
|
||||
description={t("form_fields.legal_record.description")}
|
||||
/>
|
||||
<CardContent>
|
||||
<div className='grid grid-cols-1 gap-6 lg:grid-cols-4 mb-12 '>
|
||||
<SelectField
|
||||
className='lg:col-span-2'
|
||||
control={control}
|
||||
name='language_code'
|
||||
required
|
||||
label={t("form_fields.language_code.label")}
|
||||
placeholder={t("form_fields.language_code.placeholder")}
|
||||
description={t("form_fields.language_code.description")}
|
||||
items={[...LANGUAGE_OPTIONS]}
|
||||
/>
|
||||
<SelectField
|
||||
className='lg:col-span-2'
|
||||
control={control}
|
||||
name='currency_code'
|
||||
required
|
||||
label={t("form_fields.currency_code.label")}
|
||||
placeholder={t("form_fields.currency_code.placeholder")}
|
||||
description={t("form_fields.currency_code.description")}
|
||||
items={[...CURRENCY_OPTIONS]}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@ -55,7 +55,7 @@ export const CustomerAddressFields = ({ control }: { control: Control<CustomerUp
|
||||
description={t("form_fields.postal_code.description")}
|
||||
/>
|
||||
</div>
|
||||
<div className='grid grid-cols-1 gap-6 lg:grid-cols-4 mb-12 '>
|
||||
<div className='grid grid-cols-1 gap-6 lg:grid-cols-4 mb-0 '>
|
||||
<TextField
|
||||
className='lg:col-span-2'
|
||||
control={control}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { TaxesMultiSelectField } from "@erp/core/components";
|
||||
import { TextField } from "@repo/rdx-ui/components";
|
||||
import { TextAreaField, TextField } from "@repo/rdx-ui/components";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@ -27,8 +27,8 @@ export const CustomerBasicInfoFields = ({ control }: { control: Control<Customer
|
||||
<CardTitle>Identificación</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className='grid grid-cols-1 gap-6 md:grid-cols-4'>
|
||||
<div className='sm:col-span-full'>
|
||||
<div className='grid grid-cols-1 gap-6 lg:grid-cols-4 mb-12 '>
|
||||
<div className='lg:col-span-full'>
|
||||
<FormField
|
||||
control={control}
|
||||
name='is_company'
|
||||
@ -64,8 +64,9 @@ export const CustomerBasicInfoFields = ({ control }: { control: Control<Customer
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='sm:col-span-2'>
|
||||
</div>
|
||||
<div className='grid grid-cols-1 gap-6 lg:grid-cols-4 mb-12 '>
|
||||
<div className='lg:col-span-2'>
|
||||
<TextField
|
||||
control={control}
|
||||
name='name'
|
||||
@ -76,7 +77,7 @@ export const CustomerBasicInfoFields = ({ control }: { control: Control<Customer
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='sm:col-span-2'>
|
||||
<div className='lg:col-span-2'>
|
||||
<TextField
|
||||
control={control}
|
||||
name='trade_name'
|
||||
@ -86,7 +87,7 @@ export const CustomerBasicInfoFields = ({ control }: { control: Control<Customer
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='sm:col-span-2'>
|
||||
<div className='lg:col-span-2'>
|
||||
<TaxesMultiSelectField
|
||||
control={control}
|
||||
name='default_taxes'
|
||||
@ -96,7 +97,7 @@ export const CustomerBasicInfoFields = ({ control }: { control: Control<Customer
|
||||
description={t("form_fields.default_taxes.description")}
|
||||
/>
|
||||
</div>
|
||||
<div className='col-auto'>
|
||||
<div className='lg:col-span-2'>
|
||||
<TextField
|
||||
control={control}
|
||||
name='reference'
|
||||
@ -105,6 +106,14 @@ export const CustomerBasicInfoFields = ({ control }: { control: Control<Customer
|
||||
description={t("form_fields.reference.description")}
|
||||
/>
|
||||
</div>
|
||||
<TextAreaField
|
||||
className='lg:col-span-full'
|
||||
control={control}
|
||||
name='legal_record'
|
||||
label={t("form_fields.legal_record.label")}
|
||||
placeholder={t("form_fields.legal_record.placeholder")}
|
||||
description={t("form_fields.legal_record.description")}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -65,7 +65,7 @@ export const CustomerEditForm = ({ defaultValues, onSubmit, isPending }: Custome
|
||||
return (
|
||||
<Form {...form}>
|
||||
<FormDebug form={form} />
|
||||
<form onSubmit={form.handleSubmit(handleSubmit, handleError)}>
|
||||
<form id='customer-edit-form' onSubmit={form.handleSubmit(handleSubmit, handleError)}>
|
||||
<div className='flex gap-6'>
|
||||
<div className='w-full xl:w-2/3 space-y-12'>
|
||||
<CustomerBasicInfoFields control={form.control} />
|
||||
|
||||
@ -46,9 +46,15 @@ export function TextAreaField<TFormValues extends FieldValues>({
|
||||
render={({ field }) => (
|
||||
<FormItem className={cn("space-y-0", className)}>
|
||||
{label && (
|
||||
<div className='flex justify-between items-center'>
|
||||
<FormLabel className='m-0'>{label}</FormLabel>
|
||||
{required && <span className='text-xs text-destructive'>{t("common.required")}</span>}
|
||||
<div className='mb-1 flex justify-between gap-2'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<FormLabel htmlFor={name} className='m-0'>
|
||||
{label}
|
||||
</FormLabel>
|
||||
{required && (
|
||||
<span className='text-xs text-destructive'>{t("common.required")}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<FormControl>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user