cambios en el crear producto y en el refresh token

This commit is contained in:
2025-08-14 15:38:10 -04:00
parent 854b31f594
commit 6a28e141a9
19 changed files with 278 additions and 400 deletions

View File

@@ -16,56 +16,83 @@ import {
SelectTrigger,
SelectValue,
} from '@repo/shadcn/select';
import { Textarea } from '@repo/shadcn/textarea';
import { Input } from '@repo/shadcn/input';
import { useForm } from 'react-hook-form';
import { useCreateUser } from "@/feactures/inventory/hooks/use-mutation";
import { EditInventory, editInventory } from '@/feactures/inventory/schemas/inventory';
import {STATUS} from '@/constants/status'
import { editInventory, EditInventory } from '@/feactures/inventory/schemas/inventory';
import { Textarea } from '@repo/shadcn/textarea';
import { STATUS } from '@/constants/status'
import { useState, useEffect } from 'react';
import { sizeFormate } from "@/feactures/inventory/utils/sizeFormate"
interface CreateFormProps {
onSuccess?: () => void;
onCancel?: () => void;
defaultValues?: Partial<EditInventory>;
}
export function CreateForm({
onSuccess,
onCancel
onCancel,
}: CreateFormProps) {
const {
mutate: saveAccountingAccounts,
mutate: saveProduct,
isPending: isSaving,
isError,
} = useCreateUser();
const defaultformValues = {
const [sizeFile, setSizeFile] = useState('0 bytes');
const [previewUrls, setPreviewUrls] = useState<string[]>([]);
useEffect(() => {
return () => {
previewUrls.forEach(url => URL.revokeObjectURL(url));
};
}, [previewUrls]);
const defaultformValues: EditInventory = {
title: '',
description: '',
address: '',
price: '',
address: '',
status: 'BORRADOR',
stock: 0,
urlImg: '',
status: ''
}
urlImg: undefined,
};
const form = useForm<EditInventory>({
resolver: zodResolver(editInventory),
defaultValues: defaultformValues,
mode: 'onChange', // Enable real-time validation
mode: 'onChange',
});
const onSubmit = async (data: EditInventory) => {
const formData = new FormData();
saveAccountingAccounts(data, {
formData.append('title', data.title);
formData.append('description', data.description);
formData.append('price', String(data.price));
formData.append('address', data.address);
formData.append('status', data.status);
formData.append('stock', String(data.stock));
if (data.urlImg) {
for (let i = 0; i < data.urlImg.length; i++) {
const file = data.urlImg[i];
if (file) {
formData.append('urlImg', file);
}
}
}
saveProduct(formData as any, {
onSuccess: () => {
form.reset();
onSuccess?.();
},
onError: (e) => {
onError: (error) => {
console.error("Error al guardar el producto:", error);
form.setError('root', {
type: 'manual',
message: e.message,
message: error.message || 'Error al guardar el producto',
});
},
});
@@ -101,9 +128,7 @@ export function CreateForm({
<FormItem>
<FormLabel>Precio</FormLabel>
<FormControl>
<Input {...field}
// value={field.value?.toString() ?? ''}
/>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
@@ -131,7 +156,7 @@ export function CreateForm({
<FormItem className='col-span-2'>
<FormLabel>Descripción</FormLabel>
<FormControl>
<Textarea {...field} className="resize-none"/>
<Textarea {...field} className="resize-none" />
</FormControl>
<FormMessage />
</FormItem>
@@ -145,7 +170,7 @@ export function CreateForm({
<FormItem>
<FormLabel>Cantidad/Stock</FormLabel>
<FormControl>
<Input {...field} type='number' onChange={(e) => field.onChange(Number(e.target.value))}/>
<Input {...field} type='number' onChange={(e) => field.onChange(Number(e.target.value))} />
</FormControl>
<FormMessage />
</FormItem>
@@ -158,7 +183,7 @@ export function CreateForm({
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Estatus</FormLabel>
<Select onValueChange={(value) => field.onChange(value)}>
<Select value={field.value} onValueChange={(value) => field.onChange(value)}>
<SelectTrigger className="w-full">
<SelectValue placeholder="Seleccione un estatus" />
</SelectTrigger>
@@ -175,19 +200,54 @@ export function CreateForm({
)}
/>
<FormField
control={form.control}
name="urlImg"
render={({ field }) => (
<FormItem>
<FormLabel>Imagen</FormLabel>
<FormControl>
<Input {...field}/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="col-span-2">
<FormField
control={form.control}
name="urlImg"
render={({ field: { onChange, onBlur, name, ref } }) => (
<FormItem>
<FormLabel>Imagen</FormLabel>
<p>Peso máximo: 5MB / {sizeFile} <span className='text-xs text-destructive'>(Máximo 10 archivos)</span></p>
<FormControl>
<Input
type="file"
multiple
onBlur={onBlur}
name={name}
ref={ref}
onChange={(e) => {
if (e.target.files) {
const files = Array.from(e.target.files);
let size = 0;
const newPreviewUrls: string[] = [];
files.forEach(element => {
size += element.size;
newPreviewUrls.push(URL.createObjectURL(element));
});
const tamañoFormateado = sizeFormate(size);
setSizeFile(tamañoFormateado);
setPreviewUrls(newPreviewUrls);
onChange(e.target.files);
} else {
setPreviewUrls([]);
}
}}
/>
</FormControl>
<FormMessage />
{previewUrls.length > 0 && (
<div className="mt-2 flex flex-wrap gap-2">
{previewUrls.map((url, index) => (
<img key={index} src={url} alt={`Preview ${index}`} className="w-24 h-24 object-cover rounded-md" />
))}
</div>
)}
</FormItem>
)}
/>
</div>
</div>
<div className="flex justify-end gap-4">

View File

@@ -1,5 +1,4 @@
'use client';
import { DataTable } from '@repo/shadcn/table/data-table';
import { DataTableSkeleton } from '@repo/shadcn/table/data-table-skeleton';
import { columns } from './product-tables/columns';
@@ -27,7 +26,7 @@ export default function UsersAdminList({
const {data, isLoading} = useProductQuery(filters)
console.log(data?.data);
// console.log(data?.data);
if (isLoading) {
return <DataTableSkeleton columnCount={6} rowCount={initialLimit} />;

View File

@@ -1,231 +0,0 @@
'use client';
import { zodResolver } from '@hookform/resolvers/zod';
import { Button } from '@repo/shadcn/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@repo/shadcn/form';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@repo/shadcn/select';
import { Input } from '@repo/shadcn/input';
import { useForm } from 'react-hook-form';
import { useUpdateUser } from "@/feactures/inventory/hooks/use-mutation";
import { editInventory, EditInventory } from '@/feactures/inventory/schemas/inventory'; // Renombrado EditInventory para claridad
import { Textarea } from '@repo/shadcn/components/ui/textarea';
import {STATUS} from '@/constants/status'
import { useState } from 'react';
import {sizeFormate} from "@/feactures/inventory/utils/sizeFormate"
interface UpdateFormProps {
onSuccess?: () => void;
onCancel?: () => void;
defaultValues?: Partial<EditInventory>;
}
export function UpdateForm({
onSuccess,
onCancel,
defaultValues,
}: UpdateFormProps) {
const {
mutate: saveAccountingAccounts,
isPending: isSaving,
isError, // isError no se usa en el template, podrías usarlo para mostrar un mensaje global
} = useUpdateUser();
const [sizeFile, setSizeFile] = useState('0 bytes');
const defaultformValues: EditInventory = {
id: defaultValues?.id,
title: defaultValues?.title || '',
description: defaultValues?.description || '',
price: defaultValues?.price || '',
address: defaultValues?.address || '',
status: defaultValues?.status || 'BORRADOR',
stock: defaultValues?.stock ?? 0,
urlImg: [],
userId: defaultValues?.userId
};
const form = useForm<EditInventory>({
resolver: zodResolver(editInventory),
defaultValues: defaultformValues,
mode: 'onChange', // Enable real-time validation
});
const onSubmit = async (data: EditInventory) => {
// console.log(data);
saveAccountingAccounts(data, {
onSuccess: () => {
form.reset();
onSuccess?.();
},
onError: (error) => { // Captura el error para mostrar un mensaje más específico si es posible
console.error("Error al guardar el producto:", error);
form.setError('root', {
type: 'manual',
message: error.message || 'Error al guardar el producto', // Mejor mensaje de error
});
},
});
};
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
{form.formState.errors.root && (
<div className="text-destructive text-sm">
{form.formState.errors.root.message}
</div>
)}
<div className="grid grid-cols-2 gap-4">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Nombre/Título</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="price"
render={({ field }) => (
<FormItem >
<FormLabel>Precio</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="address"
render={({ field }) => (
<FormItem className='col-span-2'>
<FormLabel>Dirección</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
render={({ field }) => (
<FormItem className='col-span-2'>
<FormLabel>Descripción</FormLabel>
<FormControl>
<Textarea {...field} className="resize-none"/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="stock"
render={({ field }) => (
<FormItem>
<FormLabel>Cantidad/Stock</FormLabel>
<FormControl>
<Input {...field} type="number" onChange={(e) => field.onChange(Number(e.target.value))}/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="status"
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Estatus</FormLabel>
<Select value={field.value} onValueChange={(value) => field.onChange(value)}>
<SelectTrigger className="w-full">
<SelectValue placeholder="Seleccione un estatus" />
</SelectTrigger>
<SelectContent>
{Object.entries(STATUS).map(([value, label]) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<div className="col-span-2">
<FormField
control={form.control}
name="urlImg"
render={({ field: { onChange, onBlur, name, ref } }) => (
<FormItem>
<FormLabel>Imagen</FormLabel>
<p>Peso máximo: 2MB / {sizeFile}</p>
<FormControl>
<Input
type="file"
multiple
onBlur={onBlur}
name={name}
ref={ref}
onChange={(e) => {
if (e.target.files) {
const files = Array.from(e.target.files);
// Calcular el tamaño total de los archivos
let size = 0;
files.forEach(element => size += element.size)
const tamañoFormateado = sizeFormate(size)
setSizeFile(tamañoFormateado);
}
onChange(e.target.files);
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
<div className="flex justify-end gap-4">
<Button variant="outline" type="button" onClick={onCancel}>
Cancelar
</Button>
<Button type="submit" disabled={isSaving}>
{isSaving ? 'Guardando...' : 'Guardar'}
</Button>
</div>
</form>
</Form>
);
}

View File

@@ -15,6 +15,7 @@ import { ImageIcon } from 'lucide-react';
export function ProductList() {
const router = useRouter();
const { data: produts } = useAllProductQuery();
console.log(produts);
const handle = (id: number) => {
router.push(`/dashboard/productos/${id}`);
@@ -43,7 +44,7 @@ export function ProductList() {
<CardContent className="p-0 flex-grow">
<img
className="object-cover w-full h-full aspect-square border"
src={`http://localhost:3000/${data.urlImg}`}
src={`http://localhost:3000/uploads/inventory/${data.userId}/${data.urlImg}`}
alt=""
/>
</CardContent>