Clientes
This commit is contained in:
parent
01ca0bd5dc
commit
c8e3191d05
@ -9,11 +9,11 @@ import {
|
|||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
import { CURRENCY_OPTIONS, LANGUAGE_OPTIONS } from "../../constants";
|
import { CURRENCY_OPTIONS, LANGUAGE_OPTIONS } from "../../constants";
|
||||||
import { useTranslation } from "../../i18n";
|
import { useTranslation } from "../../i18n";
|
||||||
import { CustomerFormData } from "../../schemas";
|
import { CreateCustomerFormData } from "../../schemas";
|
||||||
|
|
||||||
export const CustomerAdditionalConfigFields = () => {
|
export const CustomerAdditionalConfigFields = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { control } = useFormContext<CustomerFormData>();
|
const { control } = useFormContext<CreateCustomerFormData>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className='border-0 shadow-none'>
|
<Card className='border-0 shadow-none'>
|
||||||
|
|||||||
@ -9,11 +9,11 @@ import {
|
|||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
import { COUNTRY_OPTIONS } from "../../constants";
|
import { COUNTRY_OPTIONS } from "../../constants";
|
||||||
import { useTranslation } from "../../i18n";
|
import { useTranslation } from "../../i18n";
|
||||||
import { CustomerFormData } from "../../schemas";
|
import { CreateCustomerFormData } from "../../schemas";
|
||||||
|
|
||||||
export const CustomerAddressFields = () => {
|
export const CustomerAddressFields = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { control } = useFormContext<CustomerFormData>();
|
const { control } = useFormContext<CreateCustomerFormData>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className='border-0 shadow-none'>
|
<Card className='border-0 shadow-none'>
|
||||||
|
|||||||
@ -15,11 +15,11 @@ import {
|
|||||||
} from "@repo/shadcn-ui/components";
|
} from "@repo/shadcn-ui/components";
|
||||||
import { useFormContext, useWatch } from "react-hook-form";
|
import { useFormContext, useWatch } from "react-hook-form";
|
||||||
import { useTranslation } from "../../i18n";
|
import { useTranslation } from "../../i18n";
|
||||||
import { CustomerFormData } from "../../schemas";
|
import { CreateCustomerFormData } from "../../schemas";
|
||||||
|
|
||||||
export const CustomerBasicInfoFields = () => {
|
export const CustomerBasicInfoFields = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { control } = useFormContext<CustomerFormData>();
|
const { control } = useFormContext<CreateCustomerFormData>();
|
||||||
|
|
||||||
const isCompany = useWatch({
|
const isCompany = useWatch({
|
||||||
control,
|
control,
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { FieldErrors, FormProvider, useForm } from "react-hook-form";
|
import { FieldErrors, FormProvider, useForm } from "react-hook-form";
|
||||||
|
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { CreateCustomerFormSchema, CustomerFormData } from "../../schemas";
|
import { CreateCustomerFormData, CreateCustomerFormSchema } from "../../schemas";
|
||||||
import { FormDebug } from "../form-debug";
|
import { FormDebug } from "../form-debug";
|
||||||
import { CustomerAdditionalConfigFields } from "./customer-additional-config-fields";
|
import { CustomerAdditionalConfigFields } from "./customer-additional-config-fields";
|
||||||
import { CustomerAddressFields } from "./customer-address-fields";
|
import { CustomerAddressFields } from "./customer-address-fields";
|
||||||
@ -11,9 +11,9 @@ import { CustomerContactFields } from "./customer-contact-fields";
|
|||||||
|
|
||||||
interface CustomerFormProps {
|
interface CustomerFormProps {
|
||||||
formId: string;
|
formId: string;
|
||||||
initialValues: CustomerFormData;
|
initialValues: CreateCustomerFormData;
|
||||||
onSubmit: (data: CustomerFormData) => void;
|
onSubmit: (data: CreateCustomerFormData) => void;
|
||||||
onError: (errors: FieldErrors<CustomerFormData>) => void;
|
onError: (errors: FieldErrors<CreateCustomerFormData>) => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
onDirtyChange: (isDirty: boolean) => void;
|
onDirtyChange: (isDirty: boolean) => void;
|
||||||
}
|
}
|
||||||
@ -26,7 +26,7 @@ export function CustomerEditForm({
|
|||||||
disabled,
|
disabled,
|
||||||
onDirtyChange,
|
onDirtyChange,
|
||||||
}: CustomerFormProps) {
|
}: CustomerFormProps) {
|
||||||
const form = useForm<CustomerFormData>({
|
const form = useForm<CreateCustomerFormData>({
|
||||||
resolver: zodResolver(CreateCustomerFormSchema),
|
resolver: zodResolver(CreateCustomerFormSchema),
|
||||||
defaultValues: initialValues,
|
defaultValues: initialValues,
|
||||||
disabled,
|
disabled,
|
||||||
|
|||||||
@ -2,11 +2,11 @@ import { useDataSource } from "@erp/core/hooks";
|
|||||||
import { UniqueID, ValidationErrorCollection } from "@repo/rdx-ddd";
|
import { UniqueID, ValidationErrorCollection } from "@repo/rdx-ddd";
|
||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { CreateCustomerRequestSchema, CustomerCreationResponseDTO } from "../../common";
|
import { CreateCustomerRequestSchema, CustomerCreationResponseDTO } from "../../common";
|
||||||
import { CustomerFormData } from "../schemas";
|
import { CreateCustomerFormData } from "../schemas";
|
||||||
import { CUSTOMERS_LIST_KEY } from "./use-update-customer-mutation";
|
import { CUSTOMERS_LIST_KEY } from "./use-update-customer-mutation";
|
||||||
|
|
||||||
type CreateCustomerPayload = {
|
type CreateCustomerPayload = {
|
||||||
data: CustomerFormData;
|
data: CreateCustomerFormData;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useCreateCustomerMutation() {
|
export function useCreateCustomerMutation() {
|
||||||
@ -34,8 +34,6 @@ export function useCreateCustomerMutation() {
|
|||||||
message: err.message,
|
message: err.message,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.debug(validationErrors);
|
|
||||||
|
|
||||||
throw new ValidationErrorCollection("Validation failed", validationErrors);
|
throw new ValidationErrorCollection("Validation failed", validationErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,23 @@
|
|||||||
import { useDataSource } from "@erp/core/hooks";
|
import { useDataSource } from "@erp/core/hooks";
|
||||||
|
import { ValidationErrorCollection } from "@repo/rdx-ddd";
|
||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { CustomerData, CustomerUpdateData } from "../schemas";
|
import { UpdateCustomerByIdRequestDTO, UpdateCustomerByIdRequestSchema } from "../../common";
|
||||||
|
import { CreateCustomerFormData } from "../schemas";
|
||||||
import { CUSTOMER_QUERY_KEY } from "./use-customer-query";
|
import { CUSTOMER_QUERY_KEY } from "./use-customer-query";
|
||||||
|
|
||||||
export const CUSTOMERS_LIST_KEY = ["customers"] as const;
|
export const CUSTOMERS_LIST_KEY = ["customers"] as const;
|
||||||
|
|
||||||
type UpdateCustomerPayload = {
|
type UpdateCustomerPayload = {
|
||||||
id: string;
|
id: string;
|
||||||
data: CustomerUpdateData;
|
data: CreateCustomerFormData;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useUpdateCustomerMutation() {
|
export function useUpdateCustomerMutation() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const dataSource = useDataSource();
|
const dataSource = useDataSource();
|
||||||
|
const schema = UpdateCustomerByIdRequestSchema;
|
||||||
|
|
||||||
return useMutation<CustomerData, Error, UpdateCustomerPayload>({
|
return useMutation<UpdateCustomerByIdRequestDTO, Error, UpdateCustomerPayload>({
|
||||||
mutationKey: ["customer:update"], //, customerId],
|
mutationKey: ["customer:update"], //, customerId],
|
||||||
|
|
||||||
mutationFn: async (payload) => {
|
mutationFn: async (payload) => {
|
||||||
@ -23,14 +26,28 @@ export function useUpdateCustomerMutation() {
|
|||||||
throw new Error("customerId is required");
|
throw new Error("customerId is required");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const result = schema.safeParse(data);
|
||||||
|
if (!result.success) {
|
||||||
|
// Construye errores detallados
|
||||||
|
const validationErrors = result.error.issues.map((err) => ({
|
||||||
|
field: err.path.join("."),
|
||||||
|
message: err.message,
|
||||||
|
}));
|
||||||
|
|
||||||
|
throw new ValidationErrorCollection("Validation failed", validationErrors);
|
||||||
|
}
|
||||||
|
|
||||||
const updated = await dataSource.updateOne("customers", customerId, data);
|
const updated = await dataSource.updateOne("customers", customerId, data);
|
||||||
return updated as CustomerData;
|
return updated as UpdateCustomerByIdRequestDTO;
|
||||||
},
|
},
|
||||||
onSuccess: (updated, variables) => {
|
onSuccess: (updated, variables) => {
|
||||||
const { id: customerId } = variables;
|
const { id: customerId } = variables;
|
||||||
|
|
||||||
// Refresca inmediatamente el detalle
|
// Refresca inmediatamente el detalle
|
||||||
queryClient.setQueryData<CustomerData>(CUSTOMER_QUERY_KEY(customerId), updated);
|
queryClient.setQueryData<UpdateCustomerByIdRequestDTO>(
|
||||||
|
CUSTOMER_QUERY_KEY(customerId),
|
||||||
|
updated
|
||||||
|
);
|
||||||
|
|
||||||
// Otra opción es invalidar el detalle para forzar refetch:
|
// Otra opción es invalidar el detalle para forzar refetch:
|
||||||
// queryClient.invalidateQueries({ queryKey: CUSTOMER_QUERY_KEY(customerId) });
|
// queryClient.invalidateQueries({ queryKey: CUSTOMER_QUERY_KEY(customerId) });
|
||||||
|
|||||||
@ -8,14 +8,14 @@ import { FieldErrors } from "react-hook-form";
|
|||||||
import { CustomerEditForm, ErrorAlert } from "../../components";
|
import { CustomerEditForm, ErrorAlert } from "../../components";
|
||||||
import { useCreateCustomerMutation } from "../../hooks";
|
import { useCreateCustomerMutation } from "../../hooks";
|
||||||
import { useTranslation } from "../../i18n";
|
import { useTranslation } from "../../i18n";
|
||||||
import { CustomerFormData, defaultCustomerFormData } from "../../schemas";
|
import { CreateCustomerFormData, defaultCustomerFormData } from "../../schemas";
|
||||||
|
|
||||||
export const CustomerCreate = () => {
|
export const CustomerCreate = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [isDirty, setIsDirty] = useState(false);
|
const [isDirty, setIsDirty] = useState(false);
|
||||||
|
|
||||||
// 2) Estado de creación (mutación)
|
// 1) Estado de creación (mutación)
|
||||||
const {
|
const {
|
||||||
mutate,
|
mutate,
|
||||||
isPending: isCreating,
|
isPending: isCreating,
|
||||||
@ -23,8 +23,8 @@ export const CustomerCreate = () => {
|
|||||||
error: createError,
|
error: createError,
|
||||||
} = useCreateCustomerMutation();
|
} = useCreateCustomerMutation();
|
||||||
|
|
||||||
// 3) Submit con navegación condicionada por éxito
|
// 2) Submit con navegación condicionada por éxito
|
||||||
const handleSubmit = (formData: CustomerFormData) => {
|
const handleSubmit = (formData: CreateCustomerFormData) => {
|
||||||
mutate(
|
mutate(
|
||||||
{ data: formData },
|
{ data: formData },
|
||||||
{
|
{
|
||||||
@ -48,7 +48,7 @@ export const CustomerCreate = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleError = (errors: FieldErrors<CustomerFormData>) => {
|
const handleError = (errors: FieldErrors<CreateCustomerFormData>) => {
|
||||||
console.error("Errores en el formulario:", errors);
|
console.error("Errores en el formulario:", errors);
|
||||||
// Aquí puedes manejar los errores, por ejemplo, mostrar un mensaje al usuario
|
// Aquí puedes manejar los errores, por ejemplo, mostrar un mensaje al usuario
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { AppBreadcrumb, AppContent } from "@repo/rdx-ui/components";
|
import { AppBreadcrumb, AppContent } from "@repo/rdx-ui/components";
|
||||||
import { Button } from "@repo/shadcn-ui/components";
|
import { Button } from "@repo/shadcn-ui/components";
|
||||||
import { PlusIcon } from "lucide-react";
|
import { PlusIcon } from "lucide-react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { Outlet, useNavigate } from "react-router-dom";
|
||||||
import { CustomersListGrid } from "../components";
|
import { CustomersListGrid } from "../components";
|
||||||
import { useTranslation } from "../i18n";
|
import { useTranslation } from "../i18n";
|
||||||
|
|
||||||
@ -28,6 +28,7 @@ export const CustomersList = () => {
|
|||||||
<div className='flex flex-col w-full h-full py-4'>
|
<div className='flex flex-col w-full h-full py-4'>
|
||||||
<CustomersListGrid />
|
<CustomersListGrid />
|
||||||
</div>
|
</div>
|
||||||
|
<Outlet />
|
||||||
</AppContent>
|
</AppContent>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { AppBreadcrumb, AppContent, BackHistoryButton, ButtonGroup } from "@repo/rdx-ui/components";
|
import { AppBreadcrumb, AppContent, BackHistoryButton } from "@repo/rdx-ui/components";
|
||||||
import { Button } from "@repo/shadcn-ui/components";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { useUrlParamId } from "@erp/core/hooks";
|
import { FormCommitButtonGroup, UnsavedChangesProvider, useUrlParamId } from "@erp/core/hooks";
|
||||||
import { showErrorToast, showSuccessToast } from "@repo/shadcn-ui/lib/utils";
|
import { showErrorToast, showSuccessToast } from "@repo/shadcn-ui/lib/utils";
|
||||||
|
import { useState } from "react";
|
||||||
import { FieldErrors } from "react-hook-form";
|
import { FieldErrors } from "react-hook-form";
|
||||||
import {
|
import {
|
||||||
CustomerEditForm,
|
CustomerEditForm,
|
||||||
@ -13,12 +13,13 @@ import {
|
|||||||
} from "../../components";
|
} from "../../components";
|
||||||
import { useCustomerQuery, useUpdateCustomerMutation } from "../../hooks";
|
import { useCustomerQuery, useUpdateCustomerMutation } from "../../hooks";
|
||||||
import { useTranslation } from "../../i18n";
|
import { useTranslation } from "../../i18n";
|
||||||
import { CustomerFormData } from "../../schemas";
|
import { CreateCustomerFormData } from "../../schemas";
|
||||||
|
|
||||||
export const CustomerUpdate = () => {
|
export const CustomerUpdate = () => {
|
||||||
const customerId = useUrlParamId();
|
const customerId = useUrlParamId();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [isDirty, setIsDirty] = useState(false);
|
||||||
|
|
||||||
// 1) Estado de carga del cliente (query)
|
// 1) Estado de carga del cliente (query)
|
||||||
const {
|
const {
|
||||||
@ -30,30 +31,38 @@ export const CustomerUpdate = () => {
|
|||||||
|
|
||||||
// 2) Estado de actualización (mutación)
|
// 2) Estado de actualización (mutación)
|
||||||
const {
|
const {
|
||||||
mutateAsync,
|
mutate,
|
||||||
isPending: isUpdating,
|
isPending: isUpdating,
|
||||||
isError: isUpdateError,
|
isError: isUpdateError,
|
||||||
error: updateError,
|
error: updateError,
|
||||||
} = useUpdateCustomerMutation();
|
} = useUpdateCustomerMutation();
|
||||||
|
|
||||||
// 3) Submit con navegación condicionada por éxito
|
// 3) Submit con navegación condicionada por éxito
|
||||||
const handleSubmit = async (formData: CustomerFormData) => {
|
const handleSubmit = (formData: CreateCustomerFormData) => {
|
||||||
console.log(formData);
|
mutate(
|
||||||
try {
|
{ id: customerId!, data: formData },
|
||||||
const result = await mutateAsync({ id: customerId!, data: formData });
|
{
|
||||||
console.log(result);
|
onSuccess(data) {
|
||||||
|
setIsDirty(false);
|
||||||
|
showSuccessToast(t("pages.update.successTitle"), t("pages.update.successMsg"));
|
||||||
|
|
||||||
if (result) {
|
// El timeout es para que a React le dé tiempo a procesar
|
||||||
showSuccessToast(t("pages.update.successTitle"), t("pages.update.successMsg"));
|
// el cambio de estado de isDirty / setIsDirty.
|
||||||
navigate("/customers/list", { relative: "path" });
|
setTimeout(() => {
|
||||||
|
navigate("/customers/list", {
|
||||||
|
state: { customerId: data.id, isNew: true },
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
showErrorToast(t("pages.update.errorTitle"), error.message);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
} catch (e) {
|
);
|
||||||
showErrorToast(t("pages.update.errorTitle"), (e as Error).message);
|
|
||||||
} finally {
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleError = (errors: FieldErrors<CustomerFormData>) => {
|
const handleError = (errors: FieldErrors<CreateCustomerFormData>) => {
|
||||||
console.error("Errores en el formulario:", errors);
|
console.error("Errores en el formulario:", errors);
|
||||||
// Aquí puedes manejar los errores, por ejemplo, mostrar un mensaje al usuario
|
// Aquí puedes manejar los errores, por ejemplo, mostrar un mensaje al usuario
|
||||||
};
|
};
|
||||||
@ -100,60 +109,48 @@ export const CustomerUpdate = () => {
|
|||||||
<>
|
<>
|
||||||
<AppBreadcrumb />
|
<AppBreadcrumb />
|
||||||
<AppContent>
|
<AppContent>
|
||||||
<div className='flex items-center justify-between space-y-4 px-6'>
|
<UnsavedChangesProvider isDirty={isDirty}>
|
||||||
<div className='space-y-2'>
|
<div className='flex items-center justify-between space-y-4 px-6'>
|
||||||
<h2 className='text-2xl font-bold tracking-tight text-balance scroll-m-2'>
|
<div className='space-y-2'>
|
||||||
{t("pages.update.title")}
|
<h2 className='text-2xl font-bold tracking-tight text-balance scroll-m-2'>
|
||||||
</h2>
|
{t("pages.update.title")}
|
||||||
<p className='text-muted-foreground scroll-m-20 tracking-tight text-balance'>
|
</h2>
|
||||||
{t("pages.update.description")}
|
<p className='text-muted-foreground scroll-m-20 tracking-tight text-balance'>
|
||||||
</p>
|
{t("pages.update.description")}
|
||||||
</div>
|
</p>
|
||||||
<ButtonGroup>
|
</div>
|
||||||
<Button
|
<FormCommitButtonGroup
|
||||||
variant={"outline"}
|
cancel={{
|
||||||
className='cursor-pointer'
|
to: "/customers/list",
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
navigate("/customers/list");
|
|
||||||
}}
|
}}
|
||||||
>
|
submit={{
|
||||||
{t("common.cancel")}
|
formId: "customer-create-form",
|
||||||
</Button>
|
disabled: isUpdating,
|
||||||
|
isLoading: isUpdating,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* Alerta de error de actualización (si ha fallado el último intento) */}
|
||||||
|
{isUpdateError && (
|
||||||
|
<ErrorAlert
|
||||||
|
title={t("pages.update.errorTitle", "No se pudo guardar los cambios")}
|
||||||
|
message={
|
||||||
|
(updateError as Error)?.message ??
|
||||||
|
t("pages.update.errorMsg", "Revisa los datos e inténtalo de nuevo.")
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<Button
|
<div className='flex flex-1 flex-col gap-4 p-4'>
|
||||||
type='submit'
|
<CustomerEditForm
|
||||||
form='customer-update-form'
|
formId={"customer-update-form"} // para que el botón del header pueda hacer submit
|
||||||
className='cursor-pointer'
|
initialValues={customerData}
|
||||||
disabled={isUpdating || isLoadingCustomer}
|
onSubmit={handleSubmit}
|
||||||
aria-busy={isUpdating}
|
onError={handleError}
|
||||||
aria-disabled={isUpdating || isLoadingCustomer}
|
onDirtyChange={setIsDirty}
|
||||||
data-state={isUpdating ? "loading" : "idle"}
|
/>
|
||||||
>
|
</div>
|
||||||
{t("common.save")}
|
</UnsavedChangesProvider>
|
||||||
</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
</div>
|
|
||||||
{/* Alerta de error de actualización (si ha fallado el último intento) */}
|
|
||||||
{isUpdateError && (
|
|
||||||
<ErrorAlert
|
|
||||||
title={t("pages.update.errorTitle", "No se pudo guardar los cambios")}
|
|
||||||
message={
|
|
||||||
(updateError as Error)?.message ??
|
|
||||||
t("pages.update.errorMsg", "Revisa los datos e inténtalo de nuevo.")
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className='flex flex-1 flex-col gap-4 p-4'>
|
|
||||||
<CustomerEditForm
|
|
||||||
formId={"customer-update-form"} // para que el botón del header pueda hacer submit
|
|
||||||
initialValues={customerData}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
onError={handleError}
|
|
||||||
disabled={isLoadingCustomer}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</AppContent>
|
</AppContent>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -55,9 +55,9 @@ export const CreateCustomerFormSchema = z.object({
|
|||||||
.default("EUR"),
|
.default("EUR"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type CustomerFormData = z.infer<typeof CreateCustomerFormSchema>;
|
export type CreateCustomerFormData = z.infer<typeof CreateCustomerFormSchema>;
|
||||||
|
|
||||||
export const defaultCustomerFormData: CustomerFormData = {
|
export const defaultCustomerFormData: CreateCustomerFormData = {
|
||||||
reference: "",
|
reference: "",
|
||||||
|
|
||||||
is_company: "true",
|
is_company: "true",
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import * as z from "zod/v4";
|
||||||
|
import { CreateCustomerFormSchema } from "./customer-create.form.schema";
|
||||||
|
|
||||||
|
export const UpdateCustomerFormSchema = CreateCustomerFormSchema.extend({
|
||||||
|
is_company: CreateCustomerFormSchema.shape.is_company.optional(),
|
||||||
|
name: CreateCustomerFormSchema.shape.name.optional(),
|
||||||
|
default_taxes: z.array(z.string()).optional(),
|
||||||
|
|
||||||
|
country: CreateCustomerFormSchema.shape.country.optional(),
|
||||||
|
|
||||||
|
language_code: CreateCustomerFormSchema.shape.language_code.optional(),
|
||||||
|
currency_code: CreateCustomerFormSchema.shape.currency_code.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type UpdateCustomerFormData = z.infer<typeof UpdateCustomerFormSchema>;
|
||||||
@ -1,2 +1,2 @@
|
|||||||
|
export * from "./customer-create.form.schema";
|
||||||
export * from "./customer.api.schema";
|
export * from "./customer.api.schema";
|
||||||
export * from "./customer.form.schema";
|
|
||||||
|
|||||||
47
packages/rdx-ui/src/components/full-screen-modal.tsx
Normal file
47
packages/rdx-ui/src/components/full-screen-modal.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import type React from "react";
|
||||||
|
|
||||||
|
import { Button, Dialog, DialogContent } from "@repo/shadcn-ui/components";
|
||||||
|
import { cn } from "@repo/shadcn-ui/lib/utils";
|
||||||
|
import { X } from "lucide-react";
|
||||||
|
|
||||||
|
interface FullscreenModalProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
title: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FullscreenModal = ({
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
title,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
}: FullscreenModalProps) => {
|
||||||
|
return (
|
||||||
|
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||||
|
<DialogContent
|
||||||
|
className={cn(
|
||||||
|
"max-w-none max-h-none w-screen h-screen",
|
||||||
|
"bg-background border-0 rounded-none p-0",
|
||||||
|
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
||||||
|
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{/* Header fijo */}
|
||||||
|
<div className='flex items-center justify-between p-6 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60'>
|
||||||
|
<h2 className='text-2xl font-semibold tracking-tight'>{title}</h2>
|
||||||
|
<Button variant='ghost' size='sm' onClick={onClose} className='h-8 w-8 p-0'>
|
||||||
|
<X className='h-4 w-4' />
|
||||||
|
<span className='sr-only'>Cerrar</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contenido scrolleable */}
|
||||||
|
<div className='flex-1 overflow-auto p-6'>{children}</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -4,6 +4,7 @@ export * from "./datatable/index.tsx";
|
|||||||
export * from "./dynamics-tabs.tsx";
|
export * from "./dynamics-tabs.tsx";
|
||||||
export * from "./error-overlay.tsx";
|
export * from "./error-overlay.tsx";
|
||||||
export * from "./form/index.tsx";
|
export * from "./form/index.tsx";
|
||||||
|
export * from "./full-screen-modal.tsx";
|
||||||
export * from "./grid/index.ts";
|
export * from "./grid/index.ts";
|
||||||
export * from "./layout/index.tsx";
|
export * from "./layout/index.tsx";
|
||||||
export * from "./loading-overlay/index.tsx";
|
export * from "./loading-overlay/index.tsx";
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { SidebarInset, SidebarProvider } from "@repo/shadcn-ui/components";
|
|||||||
import { Outlet } from "react-router";
|
import { Outlet } from "react-router";
|
||||||
import { AppSidebar } from "./app-sidebar.tsx";
|
import { AppSidebar } from "./app-sidebar.tsx";
|
||||||
|
|
||||||
export const AppLayout: React.FC = () => {
|
export const AppLayout = () => {
|
||||||
return (
|
return (
|
||||||
<SidebarProvider
|
<SidebarProvider
|
||||||
style={
|
style={
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user