editar, modificar, ver y crear productos listo
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -37,3 +37,5 @@ yarn-error.log*
|
|||||||
# Misc
|
# Misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.pem
|
*.pem
|
||||||
|
|
||||||
|
apps/web/public/uploads
|
||||||
@@ -47,7 +47,8 @@ export class UsersController {
|
|||||||
@ApiResponse({ status: 200, description: 'Return the product.' })
|
@ApiResponse({ status: 200, description: 'Return the product.' })
|
||||||
@ApiResponse({ status: 404, description: 'product not found.' })
|
@ApiResponse({ status: 404, description: 'product not found.' })
|
||||||
async findOne(@Param('id') id: string) {
|
async findOne(@Param('id') id: string) {
|
||||||
const data = await this.inventoryService.findOne(id);
|
const productId = Number(id)
|
||||||
|
const data = await this.inventoryService.findOne(productId);
|
||||||
return { message: 'product fetched successfully', data };
|
return { message: 'product fetched successfully', data };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ export class UsersController {
|
|||||||
@Req() req: Request,
|
@Req() req: Request,
|
||||||
@Body() createUserDto: CreateProductDto,
|
@Body() createUserDto: CreateProductDto,
|
||||||
@UploadedFiles() files: Express.Multer.File[],
|
@UploadedFiles() files: Express.Multer.File[],
|
||||||
@Query('roleId') roleId?: string,
|
// @Query('roleId') roleId?: string,
|
||||||
) {
|
) {
|
||||||
const id = Number(req['user'].id);
|
const id = Number(req['user'].id);
|
||||||
const data = await this.inventoryService.create(files,createUserDto,id)
|
const data = await this.inventoryService.create(files,createUserDto,id)
|
||||||
@@ -79,18 +80,20 @@ export class UsersController {
|
|||||||
@Body() body: any
|
@Body() body: any
|
||||||
) {
|
) {
|
||||||
const id = Number(req['user'].id);
|
const id = Number(req['user'].id);
|
||||||
const result = await this.inventoryService.saveImages(files,body,id);
|
const result = await this.inventoryService.update(files,body,id);
|
||||||
return { data: result };
|
return { data: result };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// @Delete(':id')
|
@Delete(':id')
|
||||||
// @Roles('admin')
|
// @Roles('admin')
|
||||||
// @ApiOperation({ summary: 'Delete a user' })
|
@ApiOperation({ summary: 'Delete a Product' })
|
||||||
// @ApiResponse({ status: 200, description: 'User deleted successfully.' })
|
@ApiResponse({ status: 200, description: 'Product deleted successfully.' })
|
||||||
// @ApiResponse({ status: 404, description: 'User not found.' })
|
@ApiResponse({ status: 404, description: 'Product not found.' })
|
||||||
// async remove(@Param('id') id: string) {
|
async remove(@Req() req: Request, @Param('id') id: string) {
|
||||||
// return await this.inventoryService.remove(id);
|
const productId = Number(id);
|
||||||
// }
|
const userId = Number(req['user'].id);
|
||||||
|
return await this.inventoryService.remove(productId,userId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ export class InventoryService {
|
|||||||
return { data, meta };
|
return { data, meta };
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOne(id: string): Promise<Product> {
|
async findOne(id: number): Promise<Product> {
|
||||||
const find = await this.drizzle
|
const find = await this.drizzle
|
||||||
.select({
|
.select({
|
||||||
id: viewProductsStore.id,
|
id: viewProductsStore.id,
|
||||||
@@ -168,6 +168,7 @@ export class InventoryService {
|
|||||||
price: viewProductsStore.price,
|
price: viewProductsStore.price,
|
||||||
address: viewProductsStore.address,
|
address: viewProductsStore.address,
|
||||||
urlImg: viewProductsStore.urlImg,
|
urlImg: viewProductsStore.urlImg,
|
||||||
|
gallery: viewProductsStore.gallery,
|
||||||
stock: viewProductsStore.stock,
|
stock: viewProductsStore.stock,
|
||||||
status: viewProductsStore.status,
|
status: viewProductsStore.status,
|
||||||
userId: viewProductsStore.userId,
|
userId: viewProductsStore.userId,
|
||||||
@@ -176,10 +177,10 @@ export class InventoryService {
|
|||||||
phone: viewProductsStore.phone
|
phone: viewProductsStore.phone
|
||||||
})
|
})
|
||||||
.from(viewProductsStore)
|
.from(viewProductsStore)
|
||||||
.where(eq(viewProductsStore.id, parseInt(id)));
|
.where(eq(viewProductsStore.id, id));
|
||||||
|
|
||||||
if (find.length === 0) {
|
if (find.length === 0) {
|
||||||
throw new HttpException('Product does not exist', HttpStatus.BAD_REQUEST);
|
throw new HttpException('Product does not exist', HttpStatus.NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
return find[0];
|
return find[0];
|
||||||
@@ -200,7 +201,7 @@ export class InventoryService {
|
|||||||
gallery.push(fileName);
|
gallery.push(fileName);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.log(gallery);
|
// console.log(gallery);
|
||||||
|
|
||||||
|
|
||||||
// Start a transaction
|
// Start a transaction
|
||||||
@@ -217,7 +218,7 @@ export class InventoryService {
|
|||||||
userId: userId,
|
userId: userId,
|
||||||
gallery: gallery
|
gallery: gallery
|
||||||
}
|
}
|
||||||
console.log(productValue);
|
// console.log(productValue);
|
||||||
|
|
||||||
|
|
||||||
const [newProduct] = await tx
|
const [newProduct] = await tx
|
||||||
@@ -240,63 +241,38 @@ export class InventoryService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// async update(id: string, updateProductDto: UpdateProductDto): Promise<Product> {
|
async update(file: Express.Multer.File[], updateProductDto: UpdateProductDto, userId: number): Promise<Product> {
|
||||||
// const productId = parseInt(id);
|
|
||||||
// // console.log(updateProductDto);
|
|
||||||
|
|
||||||
// // Check if exists
|
|
||||||
// await this.findOne(id);
|
|
||||||
|
|
||||||
// // Prepare update data
|
|
||||||
// const updateData: any = {};
|
|
||||||
// if (updateProductDto.title) updateData.title = updateProductDto.title;
|
|
||||||
// if (updateProductDto.description) updateData.description = updateProductDto.description;
|
|
||||||
// if (updateProductDto.price) updateData.price = updateProductDto.price;
|
|
||||||
// if (updateProductDto.address) updateData.address = updateProductDto.address;
|
|
||||||
// if (updateProductDto.status) updateData.status = updateProductDto.status;
|
|
||||||
// if (updateProductDto.stock) updateData.stock = updateProductDto.stock;
|
|
||||||
// if (updateProductDto.urlImg) updateData.urlImg = updateProductDto.urlImg;
|
|
||||||
|
|
||||||
// const [updatedProduct] = await this.drizzle.update(products).set(updateData).where(eq(products.id, productId)).returning();
|
|
||||||
// return updatedProduct
|
|
||||||
// // Return updated user
|
|
||||||
// // return this.findOne(id);
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Guarda una imagen en el directorio de imágenes.
|
|
||||||
* @param file - El archivo de imagen a guardar.
|
|
||||||
* @returns La ruta de la imagen guardada.
|
|
||||||
*/
|
|
||||||
async saveImages(file: Express.Multer.File[], updateProductDto: UpdateProductDto, userId: number): Promise<Product> {
|
|
||||||
const productId = parseInt(updateProductDto.id);
|
const productId = parseInt(updateProductDto.id);
|
||||||
|
|
||||||
// Construye la ruta al directorio de imágenes.
|
// Check if exists
|
||||||
const picturesPath = join(__dirname, '..', '..', '..', '..', 'web', 'public', 'uploads', 'inventory', userId.toString() , productId.toString());
|
await this.findOne(productId);
|
||||||
|
|
||||||
// --- NUEVA LÓGICA: Borrar el directorio anterior ---
|
|
||||||
try {
|
|
||||||
// Borra el directorio y todos sus contenidos de forma recursiva y forzada.
|
|
||||||
await rm(picturesPath, { recursive: true, force: true });
|
|
||||||
} catch (error) {
|
|
||||||
// Es buena práctica manejar el error, aunque `force: true` lo hace menos probable.
|
|
||||||
// Podrías registrar el error, pero no detener la ejecución.
|
|
||||||
console.error(`No se pudo eliminar el directorio ${picturesPath}:`, error);
|
|
||||||
}
|
|
||||||
// --- FIN DE LA NUEVA LÓGICA ---
|
|
||||||
|
|
||||||
// Crea el directorio si no existe (ya que lo acabamos de borrar o no existía).
|
|
||||||
await mkdir(picturesPath, { recursive: true });
|
|
||||||
|
|
||||||
let gallery: string[] = [];
|
let gallery: string[] = [];
|
||||||
|
|
||||||
// Usamos `Promise.all` para manejar las operaciones asíncronas de forma correcta.
|
// check if product exist
|
||||||
await Promise.all(file.map(async (f, index) => {
|
if (file && file.length > 0) {
|
||||||
const fileName = `${index + 1}-${f.originalname}`;
|
// Construye la ruta al directorio de imágenes.
|
||||||
gallery.push(fileName);
|
const picturesPath = join(__dirname, '..', '..', '..', '..', 'web', 'public', 'uploads', 'inventory', userId.toString() , productId.toString());
|
||||||
const filePath = join(picturesPath, fileName);
|
|
||||||
await writeFile(filePath, f.buffer);
|
try {
|
||||||
}));
|
// Borra el directorio y todos sus contenidos de forma recursiva y forzada.
|
||||||
|
await rm(picturesPath, { recursive: true, force: true });
|
||||||
|
} catch (error) {
|
||||||
|
// Es buena práctica manejar el error, aunque `force: true` lo hace menos probable.
|
||||||
|
// Podrías registrar el error, pero no detener la ejecución.
|
||||||
|
console.error(`No se pudo eliminar el directorio ${picturesPath}:`, error);
|
||||||
|
}
|
||||||
|
// Crea el directorio si no existe (ya que lo acabamos de borrar o no existía).
|
||||||
|
await mkdir(picturesPath, { recursive: true });
|
||||||
|
|
||||||
|
// Usamos `Promise.all` para manejar las operaciones asíncronas de forma correcta.
|
||||||
|
await Promise.all(file.map(async (f, index) => {
|
||||||
|
const fileName = `${index + 1}-${f.originalname}`;
|
||||||
|
gallery.push(fileName);
|
||||||
|
const filePath = join(picturesPath, fileName);
|
||||||
|
await writeFile(filePath, f.buffer);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare update data
|
// Prepare update data
|
||||||
const updateData: any = {};
|
const updateData: any = {};
|
||||||
@@ -313,17 +289,26 @@ export class InventoryService {
|
|||||||
return updatedProduct;
|
return updatedProduct;
|
||||||
}
|
}
|
||||||
|
|
||||||
// async remove(id: string): Promise<{ message: string, data: User }> {
|
async remove(productId: number, userId: number): Promise<{ message: string }> {
|
||||||
// const userId = parseInt(id);
|
const picturesPath = join(__dirname, '..', '..', '..', '..', 'web', 'public', 'uploads', 'inventory', userId.toString() , productId.toString());
|
||||||
|
|
||||||
// // Check if user exists
|
try {
|
||||||
// const user = await this.findOne(id);
|
// Borra el directorio y todos sus contenidos de forma recursiva y forzada.
|
||||||
|
await rm(picturesPath, { recursive: true, force: true });
|
||||||
|
} catch (error) {
|
||||||
|
// Es buena práctica manejar el error, aunque `force: true` lo hace menos probable.
|
||||||
|
// Podrías registrar el error, pero no detener la ejecución.
|
||||||
|
console.error(`No se pudo eliminar el directorio ${picturesPath}:`, error);
|
||||||
|
}
|
||||||
|
|
||||||
// // Delete user (this will cascade delete related records due to foreign key constraints)
|
// Check if exists
|
||||||
// // await this.drizzle.delete(users).where(eq(users.id, userId));
|
await this.findOne(productId);
|
||||||
// await this.drizzle.update(users).set({ isActive: false }).where(eq(users.id, userId));
|
|
||||||
|
|
||||||
// return { message: 'User deleted successfully', data: user };
|
// Delete user (this will cascade delete related records due to foreign key constraints)
|
||||||
// }
|
await this.drizzle.delete(products).where(eq(products.id, productId));
|
||||||
|
// await this.drizzle.update(products).set({ status: 'ELIMINADO' }).where(eq(products.id, productId));
|
||||||
|
|
||||||
|
return { message: 'Product deleted successfully' };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,4 @@
|
|||||||
// 'use client';
|
|
||||||
|
|
||||||
// import PageContainer from '@/components/layout/page-container';
|
|
||||||
import { getProductById } from '@/feactures/inventory/actions/actions';
|
import { getProductById } from '@/feactures/inventory/actions/actions';
|
||||||
// import { SurveyResponse } from '@/feactures/surveys/components/survey-response';
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardFooter,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from '@repo/shadcn/card';
|
|
||||||
import {ProductList} from '@/feactures/inventory/components/products/see-product'
|
import {ProductList} from '@/feactures/inventory/components/products/see-product'
|
||||||
|
|
||||||
export default async function SurveyResponsePage({
|
export default async function SurveyResponsePage({
|
||||||
@@ -19,16 +8,18 @@ export default async function SurveyResponsePage({
|
|||||||
}) {
|
}) {
|
||||||
const { id } = await params; // You can still destructure id from params
|
const { id } = await params; // You can still destructure id from params
|
||||||
|
|
||||||
if (!id || id === '') {
|
if (!id || id === '') return null;
|
||||||
// Handle the case where no id is provided
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the function passing the dynamic id
|
// Call the function passing the dynamic id
|
||||||
const data = await getProductById(Number(id));
|
const data = await getProductById(Number(id));
|
||||||
|
|
||||||
if (!data?.data) {
|
if (!data?.data) {
|
||||||
return <div>Encuesta no encontrada</div>;
|
return (
|
||||||
|
<main className='flex h-full flex-col items-center justify-center'>
|
||||||
|
<p className='text-2xl'>Lo siento...</p>
|
||||||
|
<p className='text-4xl text-primary'>Producto no encontrado</p>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import PageContainer from '@/components/layout/page-container';
|
// import PageContainer from '@/components/layout/page-container';
|
||||||
import { ProductList } from '@/feactures/inventory/components/products/product-list';
|
import { ProductList } from '@/feactures/inventory/components/products/product-list';
|
||||||
import { Button } from '@repo/shadcn/components/ui/button';
|
import { Button } from '@repo/shadcn/components/ui/button';
|
||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
@@ -13,7 +13,7 @@ export default function SurveysPage() {
|
|||||||
// <PageContainer>
|
// <PageContainer>
|
||||||
<main className='p-4 md:px-6'>
|
<main className='p-4 md:px-6'>
|
||||||
<header className="w-full flex flex-col sm:flex-row sm:justify-between">
|
<header className="w-full flex flex-col sm:flex-row sm:justify-between">
|
||||||
<h1 className="text-2xl font-bold mb-1">Productos Disponibles</h1>
|
<h1 className="text-2xl font-bold mb-1">Productos disponibles</h1>
|
||||||
<a className='mb-1' href="/dashboard/inventario">
|
<a className='mb-1' href="/dashboard/inventario">
|
||||||
<Button>Mi inventario</Button>
|
<Button>Mi inventario</Button>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -4,14 +4,11 @@ import {
|
|||||||
ApiResponseSchema,
|
ApiResponseSchema,
|
||||||
InventoryTable,
|
InventoryTable,
|
||||||
productMutate,
|
productMutate,
|
||||||
test,
|
|
||||||
// editInventory,
|
|
||||||
productApiResponseSchema,
|
productApiResponseSchema,
|
||||||
getProduct
|
getProduct,
|
||||||
|
deleteProduct
|
||||||
} from '../schemas/inventory';
|
} from '../schemas/inventory';
|
||||||
|
|
||||||
import { auth } from '@/lib/auth';
|
|
||||||
|
|
||||||
export const getInventoryAction = async (params: {
|
export const getInventoryAction = async (params: {
|
||||||
page?: number;
|
page?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
@@ -53,7 +50,7 @@ export const getInventoryAction = async (params: {
|
|||||||
nextPage: null,
|
nextPage: null,
|
||||||
previousPage: null,
|
previousPage: null,
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAllProducts = async (params: {
|
export const getAllProducts = async (params: {
|
||||||
@@ -100,9 +97,12 @@ export const getAllProducts = async (params: {
|
|||||||
previousPage: null,
|
previousPage: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export const getProductById = async (id: number) => {
|
export const getProductById = async (id: number) => {
|
||||||
|
if (!id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const [error, data] = await safeFetchApi(
|
const [error, data] = await safeFetchApi(
|
||||||
getProduct,
|
getProduct,
|
||||||
`/products/id/${id}`,
|
`/products/id/${id}`,
|
||||||
@@ -110,12 +110,15 @@ export const getProductById = async (id: number) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
if (error.details.status === 404){
|
||||||
|
return null
|
||||||
|
}
|
||||||
console.error('❌ Error en la API:', error);
|
console.error('❌ Error en la API:', error);
|
||||||
throw new Error(error.message);
|
throw new Error(error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const createProductAction = async (payload: FormData) => {
|
export const createProductAction = async (payload: FormData) => {
|
||||||
const [error, data] = await safeFetchApi(
|
const [error, data] = await safeFetchApi(
|
||||||
@@ -131,7 +134,7 @@ export const createProductAction = async (payload: FormData) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const updateProductAction = async (payload: InventoryTable) => {
|
export const updateProductAction = async (payload: InventoryTable) => {
|
||||||
try {
|
try {
|
||||||
@@ -155,13 +158,16 @@ export const updateProductAction = async (payload: InventoryTable) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const deleteProductAction = async (id: Number) => {
|
export const deleteProductAction = async (id: Number) => {
|
||||||
|
if (!id) {
|
||||||
|
throw new Error('Error al eliminar el producto')
|
||||||
|
}
|
||||||
const [error] = await safeFetchApi(
|
const [error] = await safeFetchApi(
|
||||||
productMutate,
|
deleteProduct,
|
||||||
`/products/${id}`,
|
`/products/${id}`,
|
||||||
'DELETE'
|
'DELETE'
|
||||||
)
|
)
|
||||||
console.log(error);
|
console.log(error);
|
||||||
if (error) throw new Error(error.message || 'Error al eliminar el usuario')
|
if (error) throw new Error(error.message || 'Error al eliminar el producto')
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
import { Input } from '@repo/shadcn/input';
|
import { Input } from '@repo/shadcn/input';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { useCreateProduct } from "@/feactures/inventory/hooks/use-mutation";
|
import { useCreateProduct } from "@/feactures/inventory/hooks/use-mutation";
|
||||||
import { editInventory, EditInventory } from '@/feactures/inventory/schemas/inventory';
|
import { createProduct, EditInventory } from '@/feactures/inventory/schemas/inventory';
|
||||||
import { Textarea } from '@repo/shadcn/textarea';
|
import { Textarea } from '@repo/shadcn/textarea';
|
||||||
import { STATUS } from '@/constants/status'
|
import { STATUS } from '@/constants/status'
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
@@ -59,7 +59,7 @@ export function CreateForm({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const form = useForm<EditInventory>({
|
const form = useForm<EditInventory>({
|
||||||
resolver: zodResolver(editInventory),
|
resolver: zodResolver(createProduct),
|
||||||
defaultValues: defaultformValues,
|
defaultValues: defaultformValues,
|
||||||
mode: 'onChange',
|
mode: 'onChange',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@repo/shadcn/dialog';
|
} from '@repo/shadcn/dialog';
|
||||||
// import { AccountPlan } from '@/feactures/users/schemas/account-plan.schema';
|
// import { AccountPlan } from '@/feactures/users/schemas/account-plan.schema';
|
||||||
import { EditInventory, editInventory } from '../../schemas/inventory';
|
import { EditInventory, InventoryTable } from '../../schemas/inventory';
|
||||||
import { CreateForm } from './create-product-form';
|
import { CreateForm } from './create-product-form';
|
||||||
import { UpdateForm } from './update-product-form';
|
import { UpdateForm } from './update-product-form';
|
||||||
|
|
||||||
interface ModalProps {
|
interface ModalProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
defaultValues?: Partial<EditInventory>;
|
defaultValues?: Partial<InventoryTable>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AccountPlanModal({
|
export function AccountPlanModal({
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import {
|
|||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@repo/shadcn/tooltip';
|
} from '@repo/shadcn/tooltip';
|
||||||
import { Edit, Trash, User } from 'lucide-react';
|
import { Edit, Trash, Eye } from 'lucide-react';
|
||||||
import { InventoryTable } from '@/feactures/inventory/schemas/inventory';
|
import { InventoryTable } from '@/feactures/inventory/schemas/inventory';
|
||||||
import { useDeleteUser } from '@/feactures/users/hooks/use-mutation-users';
|
// import { useDeleteUser } from '@/feactures/users/hooks/use-mutation-users';
|
||||||
|
import { useDeleteProduct } from "@/feactures/inventory/hooks/use-mutation";
|
||||||
import { AccountPlanModal } from '../inventory-modal';
|
import { AccountPlanModal } from '../inventory-modal';
|
||||||
|
|
||||||
interface CellActionProps {
|
interface CellActionProps {
|
||||||
@@ -22,7 +23,7 @@ export const CellAction: React.FC<CellActionProps> = ({ data }) => {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [edit, setEdit] = useState(false);
|
const [edit, setEdit] = useState(false);
|
||||||
const { mutate: deleteUser } = useDeleteUser();
|
const { mutate: deleteUser } = useDeleteProduct();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const onConfirm = async () => {
|
const onConfirm = async () => {
|
||||||
@@ -51,6 +52,23 @@ export const CellAction: React.FC<CellActionProps> = ({ data }) => {
|
|||||||
<AccountPlanModal open={edit} onOpenChange={setEdit} defaultValues={data}/>
|
<AccountPlanModal open={edit} onOpenChange={setEdit} defaultValues={data}/>
|
||||||
|
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => router.push(`/dashboard/productos/${data.id}`)}
|
||||||
|
>
|
||||||
|
<Eye className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Ver</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
@@ -80,7 +98,7 @@ export const CellAction: React.FC<CellActionProps> = ({ data }) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>Deshabilitar</p>
|
<p>Eliminar</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
|
|||||||
@@ -5,16 +5,12 @@ import { CellAction } from './cell-action';
|
|||||||
import { InventoryTable } from '../../../schemas/inventory';
|
import { InventoryTable } from '../../../schemas/inventory';
|
||||||
|
|
||||||
export const columns: ColumnDef<InventoryTable>[] = [
|
export const columns: ColumnDef<InventoryTable>[] = [
|
||||||
{
|
|
||||||
accessorKey: 'userId',
|
|
||||||
header: 'ID',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
accessorKey: 'urlImg',
|
accessorKey: 'urlImg',
|
||||||
header: 'img',
|
header: 'img',
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
return (
|
return (
|
||||||
<img src={`http://localhost:3000/uploads/inventory/${row.original.userId}/${row.original.id}/${row.original.urlImg}`} alt="" width={64} height={64} className="rounded"/>
|
<img src={`/uploads/inventory/${row.original.userId}/${row.original.id}/${row.original.urlImg}`} alt="" width={64} height={64} className="rounded"/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
import { Input } from '@repo/shadcn/input';
|
import { Input } from '@repo/shadcn/input';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { useUpdateProduct } from "@/feactures/inventory/hooks/use-mutation";
|
import { useUpdateProduct } from "@/feactures/inventory/hooks/use-mutation";
|
||||||
import { editInventory, EditInventory } from '@/feactures/inventory/schemas/inventory'; // Renombrado EditInventory para claridad
|
import { updateInventory, EditInventory, InventoryTable } from '@/feactures/inventory/schemas/inventory'; // Renombrado EditInventory para claridad
|
||||||
import { Textarea } from '@repo/shadcn/components/ui/textarea';
|
import { Textarea } from '@repo/shadcn/components/ui/textarea';
|
||||||
import {STATUS} from '@/constants/status'
|
import {STATUS} from '@/constants/status'
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
@@ -43,7 +43,7 @@ import { z } from 'zod'; // Asegúrate de importar Zod
|
|||||||
interface UpdateFormProps {
|
interface UpdateFormProps {
|
||||||
onSuccess?: () => void;
|
onSuccess?: () => void;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
defaultValues?: Partial<EditInventory>;
|
defaultValues?: Partial<InventoryTable>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UpdateForm({
|
export function UpdateForm({
|
||||||
@@ -78,7 +78,7 @@ export function UpdateForm({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const form = useForm<EditInventory>({ // Usamos el nuevo tipo aquí
|
const form = useForm<EditInventory>({ // Usamos el nuevo tipo aquí
|
||||||
resolver: zodResolver(editInventory), // Usamos el esquema extendido
|
resolver: zodResolver(updateInventory), // Usamos el esquema extendido
|
||||||
defaultValues: defaultformValues,
|
defaultValues: defaultformValues,
|
||||||
mode: 'onChange',
|
mode: 'onChange',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ export function UsersHeader() {
|
|||||||
<>
|
<>
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<Heading
|
<Heading
|
||||||
title="Administración del inventario"
|
title="Mi inventario"
|
||||||
description="Gestione aquí los productos que usted registre en la plataforma"
|
description="Gestione aquí los productos que registre en la plataforma"
|
||||||
/>
|
/>
|
||||||
<Button onClick={() => setOpen(true)} size="sm">
|
<Button onClick={() => setOpen(true)} size="sm">
|
||||||
<Plus className="h-4 w-4" /><span className='hidden md:inline'>Agregar Producto</span>
|
<Plus className="h-4 w-4" /><span className='hidden md:inline'>Agregar Producto</span>
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ import {
|
|||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useAllProductQuery } from '@/feactures/inventory/hooks/use-query-products';
|
import { useAllProductQuery } from '@/feactures/inventory/hooks/use-query-products';
|
||||||
import { allProducts } from '../../schemas/inventory';
|
import { allProducts } from '../../schemas/inventory';
|
||||||
import { ImageIcon } from 'lucide-react';
|
// import { ImageIcon } from 'lucide-react';
|
||||||
|
|
||||||
export function ProductList() {
|
export function ProductList() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { data: produts } = useAllProductQuery();
|
const { data: produts } = useAllProductQuery();
|
||||||
console.log(produts);
|
// console.log(produts);
|
||||||
|
|
||||||
const handle = (id: number) => {
|
const handle = (id: number) => {
|
||||||
router.push(`/dashboard/productos/${id}`);
|
router.push(`/dashboard/productos/${id}`);
|
||||||
@@ -36,15 +36,15 @@ export function ProductList() {
|
|||||||
className="cursor-pointer flex flex-col"
|
className="cursor-pointer flex flex-col"
|
||||||
onClick={() => handle(Number(data.id))}
|
onClick={() => handle(Number(data.id))}
|
||||||
>
|
>
|
||||||
<CardHeader>
|
{/* <CardHeader> */}
|
||||||
<CardTitle className="text-base font-bold truncate">
|
<CardTitle className="text-base font-bold truncate p-3 text-primary">
|
||||||
{data.title.charAt(0).toUpperCase() + data.title.slice(1)}
|
{data.title.charAt(0).toUpperCase() + data.title.slice(1)}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
{/* </CardHeader> */}
|
||||||
<CardContent className="p-0 flex-grow">
|
<CardContent className="p-0 flex-grow">
|
||||||
<img
|
<img
|
||||||
className="object-cover w-full h-full aspect-square border"
|
className="object-cover w-full h-full aspect-square border"
|
||||||
src={`http://localhost:3000/uploads/inventory/${data.userId}/${data.id}/${data.urlImg}`}
|
src={`/uploads/inventory/${data.userId}/${data.id}/${data.urlImg}`}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { allProducts, } from "../../schemas/inventory";
|
'use client';
|
||||||
|
import { useState } from "react";
|
||||||
|
import { allProducts } from "../../schemas/inventory";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
@@ -8,6 +10,9 @@ import {
|
|||||||
} from '@repo/shadcn/card';
|
} from '@repo/shadcn/card';
|
||||||
|
|
||||||
export function ProductList({product}: {product: allProducts}) {
|
export function ProductList({product}: {product: allProducts}) {
|
||||||
|
const [selectedImg, setSelectedImg] = useState(`/uploads/inventory/${product.userId}/${product.id}/${product.urlImg}`)
|
||||||
|
console.log(product);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// <PageContainer>
|
// <PageContainer>
|
||||||
<main className='px-4 lg:px-6 flex flex-col md:flex-row gap-3 lg:gap-4 md:relative'>
|
<main className='px-4 lg:px-6 flex flex-col md:flex-row gap-3 lg:gap-4 md:relative'>
|
||||||
@@ -15,44 +20,60 @@ return (
|
|||||||
|
|
||||||
<img
|
<img
|
||||||
className="border-2 object-contain w-full f-full min-h-[400px] md:h-[70vh] aspect-square rounded-2xl"
|
className="border-2 object-contain w-full f-full min-h-[400px] md:h-[70vh] aspect-square rounded-2xl"
|
||||||
src={`http://localhost:3000/uploads/inventory/${product.userId}/${product.id}/${product.urlImg}`}
|
src={selectedImg}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<section className=''>
|
<section className="relative flex flex-row flex-nowrap overflow-auto gap-1 md:gap-2 p-2">
|
||||||
<img
|
{/* <span className="sticky left-0 flex items-center">
|
||||||
className="border-2 object-cover w-[64px] h-[64px] md:w-[96px] md:h-[96px] aspect-square rounded-2xl"
|
<span className="text-xl p-3 cursor-pointer bg-neutral-800/50 rounded-full text-white">
|
||||||
src={`http://localhost:3000/uploads/inventory/${product.userId}/${product.id}/${product.urlImg}`}
|
{"<"}
|
||||||
|
</span>
|
||||||
|
</span> */}
|
||||||
|
{product.gallery?.map((img, index) => (
|
||||||
|
<img
|
||||||
|
key={index}
|
||||||
|
className="cursor-pointer border-2 object-cover w-[64px] h-[64px] md:w-[96px] md:h-[96px] aspect-square rounded-2xl"
|
||||||
|
src={`/uploads/inventory/${product.userId}/${product.id}/${img}`}
|
||||||
alt=""
|
alt=""
|
||||||
|
onClick={() => setSelectedImg(`/uploads/inventory/${product.userId}/${product.id}/${img}`)}
|
||||||
/>
|
/>
|
||||||
|
))}
|
||||||
|
{/* <div className="sticky right-0 flex items-center">
|
||||||
|
<span className="text-xl p-3 cursor-pointer bg-neutral-800/50 rounded-full text-white">
|
||||||
|
{">"}
|
||||||
|
</span>
|
||||||
|
</div> */}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<Card className="flex flex-col md:w-[400px] lg:w-[500px] min-h-[400px] md:h-[85vh] md:overflow-auto md:sticky top-0 right-0">
|
<Card className="flex flex-col md:w-[400px] lg:w-[500px] min-h-[400px] md:h-[85vh] md:overflow-auto md:sticky top-0 right-0">
|
||||||
<CardHeader className='py-2 px-2 md:px-4 lg:px-6'>
|
<CardHeader className='py-2 px-2 md:px-4 lg:px-6'>
|
||||||
<CardTitle className="font-bold text-2xl">
|
<CardTitle className="font-bold text-2xl text-primary">
|
||||||
{product.title.charAt(0).toUpperCase() + product.title.slice(1)}
|
{product.title.charAt(0).toUpperCase() + product.title.slice(1)}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<p className='font-semibold'>$ {product.price} </p>
|
<p className='font-semibold'>{product.price}$
|
||||||
{product.status === 'AGOTADO' ? (
|
{product.status === 'AGOTADO' ? (
|
||||||
<p className="font-semibold text-lg text-red-900">AGOTADO</p>
|
<span className="font-semibold text-lg text-red-900"> AGOTADO</span>
|
||||||
) : ('')}
|
) : ('')}
|
||||||
|
</p>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="py-0 px-2 md:px-4 lg:px-6 flex-col justify-between flex-grow md:overflow-auto">
|
<CardContent className="py-0 px-2 h-full flex flex-col justify-around flex-grow md:px-4 md:overflow-auto lg:px-6">
|
||||||
<div>
|
<section>
|
||||||
<p className='font-semibold text-lg border-t border-b'> Descripción</p>
|
<p className='font-semibold text-lg border-t border-b'>• Descripción</p>
|
||||||
<p className='p-1'>{product.description}</p>
|
<p className='p-1'>{product.description}</p>
|
||||||
{/* <p className='p-1'>{lorem+lorem+lorem+lorem}</p> */}
|
</section>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-2'>
|
<section>
|
||||||
<p className='font-semibold text-lg border-t border-b'> Dirección</p>
|
<p className='font-semibold text-lg border-t border-b'>• Dirección</p>
|
||||||
<p className='p-1'>{product.address}</p>
|
<p className='p-1'>{product.address}</p>
|
||||||
</div>
|
</section>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
||||||
<CardFooter className="px-2 md:px-4 lg:px-6">
|
<CardFooter className="px-2 md:px-4 lg:px-6">
|
||||||
<div>
|
<div>
|
||||||
<p className='font-semibold text-lg border-t border-b mt-4'>Información del vendedor</p>
|
<p className='font-semibold text-lg border-t border-b mt-4'>• Información del vendedor</p>
|
||||||
<p>{product.fullname}</p>
|
<p>{product.fullname}</p>
|
||||||
<p>{product.phone}</p>
|
<p>{product.phone}</p>
|
||||||
<p>{product.email}</p>
|
<p>{product.email}</p>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
// import { EditInventory } from "../schemas/inventory";
|
// import { EditInventory } from "../schemas/inventory";
|
||||||
import { updateProductAction, createProductAction, } from "../actions/actions";
|
import { updateProductAction, createProductAction,deleteProductAction } from "../actions/actions";
|
||||||
|
|
||||||
// Create mutation
|
// Create mutation
|
||||||
export function useCreateProduct() {
|
export function useCreateProduct() {
|
||||||
@@ -25,11 +25,11 @@ export function useUpdateProduct() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete mutation
|
// Delete mutation
|
||||||
// export function useDeleteUser() {
|
export function useDeleteProduct() {
|
||||||
// const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
// return useMutation({
|
return useMutation({
|
||||||
// mutationFn: (id: number) => deleteUserAction(id),
|
mutationFn: (id: number) => deleteProductAction(id),
|
||||||
// onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['product'] }),
|
||||||
// onError: (e) => console.error('Error:', e)
|
onError: (e) => console.error('Error:', e)
|
||||||
// })
|
})
|
||||||
// }
|
}
|
||||||
@@ -4,7 +4,8 @@ import { url } from 'inspector';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
export type InventoryTable = z.infer<typeof seeProduct>;
|
export type InventoryTable = z.infer<typeof seeProduct>;
|
||||||
export type EditInventory = z.infer<typeof editInventory>;
|
export type EditInventory = z.infer<typeof updateInventory>;
|
||||||
|
export type CreateInventory = z.infer<typeof createProduct>;
|
||||||
export type ProductApiResponseSchema = z.infer<typeof productApiResponseSchema>;
|
export type ProductApiResponseSchema = z.infer<typeof productApiResponseSchema>;
|
||||||
export type allProducts = z.infer<typeof productDetails>;
|
export type allProducts = z.infer<typeof productDetails>;
|
||||||
|
|
||||||
@@ -35,19 +36,58 @@ export const productDetails = seeProduct.extend({
|
|||||||
email: z.string().email().nullable()
|
email: z.string().email().nullable()
|
||||||
})
|
})
|
||||||
|
|
||||||
export const editInventory = z.object({
|
const validateProduct = z.object({
|
||||||
id: z.number().optional(),
|
id: z.number().optional(),
|
||||||
title: z.string().min(5, { message: "Debe de tener 5 o más caracteres" }),
|
title: z.string().min(5, { message: "Debe de tener 5 o más caracteres" }),
|
||||||
description: z.string().min(10, { message: "Debe de tener 10 o más caracteres" }),
|
description: z.string().min(10, { message: "Debe de tener 10 o más caracteres" }),
|
||||||
stock: z.number(),
|
stock: z.number(),
|
||||||
address: z.string().min(5, { message: "Debe de tener 5 o más caracteres" }),
|
address: z.string().min(5, { message: "Debe de tener 5 o más caracteres" }),
|
||||||
price: z.string(),
|
price: z.string().min(1, { message: "Debe de tener 1 o más caracteres" }),
|
||||||
|
urlImg: z.custom<FileList | undefined>(),
|
||||||
|
status: z.string().min(1, { message: "Debe de seleccionar un valor" }),
|
||||||
|
userId: z.number().optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const updateInventory = validateProduct.extend({
|
||||||
|
urlImg: z.custom<FileList | undefined>()
|
||||||
|
.refine((files) => (files && files.length <= 10) || files === undefined, "Máximo 10 imágenes")
|
||||||
|
.refine((files) =>
|
||||||
|
// (files && Array.from(files).every(file => file.size <= MAX_FILE_SIZE)) || files === undefined
|
||||||
|
{
|
||||||
|
if (files) {
|
||||||
|
let size = 0;
|
||||||
|
Array.from(files).map(file => {
|
||||||
|
size += file.size;
|
||||||
|
})
|
||||||
|
if (size <= MAX_FILE_SIZE) return true;
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
,
|
||||||
|
`El tamaño máximo entre toda las imagenes es de 5MB`
|
||||||
|
).refine((files) =>
|
||||||
|
(files && Array.from(files).every(file => ACCEPTED_IMAGE_TYPES.includes(file.type))) || files === undefined,
|
||||||
|
"Solo se aceptan archivos .jpg, .jpeg, .png y .webp"
|
||||||
|
).refine((files) =>
|
||||||
|
(files && Array.from(files).every(file => file.name.length <= MAX_FILENAME_LENGTH)) || files === undefined,
|
||||||
|
`El nombre de cada archivo no puede superar los ${MAX_FILENAME_LENGTH} caracteres`
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const createProduct = validateProduct.extend({
|
||||||
urlImg: z.custom<FileList | undefined>()
|
urlImg: z.custom<FileList | undefined>()
|
||||||
.refine((files) => files && files.length > 0, "Se requiere al menos una imagen")
|
.refine((files) => files && files.length > 0, "Se requiere al menos una imagen")
|
||||||
.refine((files) => files && files.length <= 10, "Máximo 10 imágenes")
|
.refine((files) => files && files.length <= 10, "Máximo 10 imágenes")
|
||||||
.refine((files) =>
|
.refine((files) => {
|
||||||
files && Array.from(files).every(file => file.size <= MAX_FILE_SIZE),
|
let size = 0;
|
||||||
`El tamaño máximo de cada imagen es de 5MB`
|
if (files) Array.from(files).map(file => {
|
||||||
|
size += file.size;
|
||||||
|
})
|
||||||
|
if (size <= MAX_FILE_SIZE) return true;
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
`El tamaño máximo entre toda las imagenes es de 5MB`
|
||||||
).refine((files) =>
|
).refine((files) =>
|
||||||
files && Array.from(files).every(file => ACCEPTED_IMAGE_TYPES.includes(file.type)),
|
files && Array.from(files).every(file => ACCEPTED_IMAGE_TYPES.includes(file.type)),
|
||||||
"Solo se aceptan archivos .jpg, .jpeg, .png y .webp"
|
"Solo se aceptan archivos .jpg, .jpeg, .png y .webp"
|
||||||
@@ -55,13 +95,11 @@ export const editInventory = z.object({
|
|||||||
files && Array.from(files).every(file => file.name.length <= MAX_FILENAME_LENGTH),
|
files && Array.from(files).every(file => file.name.length <= MAX_FILENAME_LENGTH),
|
||||||
`El nombre de cada archivo no puede superar los ${MAX_FILENAME_LENGTH} caracteres`
|
`El nombre de cada archivo no puede superar los ${MAX_FILENAME_LENGTH} caracteres`
|
||||||
),
|
),
|
||||||
status: z.string().min(1, { message: "Debe de seleccionar un valor" }),
|
|
||||||
userId: z.number().optional(),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const ApiResponseSchema = z.object({
|
export const ApiResponseSchema = z.object({
|
||||||
message: z.string(),
|
message: z.string(),
|
||||||
data: z.array(product),
|
data: z.array(seeProduct),
|
||||||
meta: z.object({
|
meta: z.object({
|
||||||
page: z.number(),
|
page: z.number(),
|
||||||
limit: z.number(),
|
limit: z.number(),
|
||||||
@@ -89,11 +127,6 @@ export const productApiResponseSchema = z.object({
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const test = z.object({
|
|
||||||
// message: z.string(),
|
|
||||||
data: z.array(z.string()),
|
|
||||||
})
|
|
||||||
|
|
||||||
export const productMutate = z.object({
|
export const productMutate = z.object({
|
||||||
message: z.string(),
|
message: z.string(),
|
||||||
data: seeProduct,
|
data: seeProduct,
|
||||||
@@ -103,3 +136,7 @@ export const getProduct = z.object({
|
|||||||
message: z.string(),
|
message: z.string(),
|
||||||
data: productDetails,
|
data: productDetails,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const deleteProduct = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
})
|
||||||
@@ -130,21 +130,21 @@ const authConfig: NextAuthConfig = {
|
|||||||
return token; // Si no ha expirado, no hacer nada y devolver el token actual
|
return token; // Si no ha expirado, no hacer nada y devolver el token actual
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Now Access Expire:",token.access_expire_in);
|
// console.log("Now Access Expire:",token.access_expire_in);
|
||||||
|
|
||||||
|
|
||||||
// 3. Si el token de acceso ha expirado, verificar el refresh token
|
// 3. Si el token de acceso ha expirado, verificar el refresh token
|
||||||
console.log("Access token ha expirado. Verificando refresh token...");
|
// console.log("Access token ha expirado. Verificando refresh token...");
|
||||||
if (now > (token.refresh_expire_in as number)) {
|
if (now > (token.refresh_expire_in as number)) {
|
||||||
console.log("Refresh token ha expirado. Forzando logout.");
|
// console.log("Refresh token ha expirado. Forzando logout.");
|
||||||
return null; // Forzar el logout al devolver null
|
return null; // Forzar el logout al devolver null
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("token:", token.refresh_token);
|
// console.log("token:", token.refresh_token);
|
||||||
|
|
||||||
|
|
||||||
// 4. Si el token de acceso ha expirado pero el refresh token es válido, renovar
|
// 4. Si el token de acceso ha expirado pero el refresh token es válido, renovar
|
||||||
console.log("Renovando token de acceso...");
|
// console.log("Renovando token de acceso...");
|
||||||
try {
|
try {
|
||||||
const res = await resfreshTokenAction({ token: token.refresh_token as string });
|
const res = await resfreshTokenAction({ token: token.refresh_token as string });
|
||||||
|
|
||||||
@@ -152,10 +152,10 @@ const authConfig: NextAuthConfig = {
|
|||||||
throw new Error('Fallo en la respuesta de la API de refresco.');
|
throw new Error('Fallo en la respuesta de la API de refresco.');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Old Access Expire:", token.access_expire_in);
|
// console.log("Old Access Expire:", token.access_expire_in);
|
||||||
console.log("New Access Expire:", res.tokens.access_expire_in);
|
// console.log("New Access Expire:", res.tokens.access_expire_in);
|
||||||
|
|
||||||
console.log("token:", token.refresh_token);
|
// console.log("token:", token.refresh_token);
|
||||||
|
|
||||||
|
|
||||||
// Actualizar el token directamente con los nuevos valores
|
// Actualizar el token directamente con los nuevos valores
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const fetchApi = axios.create({
|
|||||||
// ESTE INTERCEPTOR ESTÁ BIEN PARA EL RESTO DE LAS PETICIONES AUTENTICADAS
|
// ESTE INTERCEPTOR ESTÁ BIEN PARA EL RESTO DE LAS PETICIONES AUTENTICADAS
|
||||||
fetchApi.interceptors.request.use(async (config: any) => {
|
fetchApi.interceptors.request.use(async (config: any) => {
|
||||||
try {
|
try {
|
||||||
console.log("Solicitando autenticación...");
|
// console.log("Solicitando autenticación...");
|
||||||
|
|
||||||
const { auth } = await import('@/lib/auth'); // Importación dinámica
|
const { auth } = await import('@/lib/auth'); // Importación dinámica
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 56 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
Reference in New Issue
Block a user