tokenRefresh, crear y editar productos con img funcionando
This commit is contained in:
@@ -24,7 +24,7 @@ export class JwtRefreshGuard implements CanActivate {
|
|||||||
const request = context.switchToHttp().getRequest();
|
const request = context.switchToHttp().getRequest();
|
||||||
const token = this.extractTokenFromHeader(request);
|
const token = this.extractTokenFromHeader(request);
|
||||||
if (!token) {
|
if (!token) {
|
||||||
throw new UnauthorizedException();
|
throw new UnauthorizedException('No Refresh Token?');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
request.user = await this.jwtService.verifyAsync(token, {
|
request.user = await this.jwtService.verifyAsync(token, {
|
||||||
@@ -43,8 +43,8 @@ export class JwtRefreshGuard implements CanActivate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private extractTokenFromHeader(request: Request): string | undefined {
|
private extractTokenFromHeader(request: Request): string | undefined {
|
||||||
const token = request.body.token
|
const token = request.body.refresh_token
|
||||||
console.log(token);
|
// console.log(token);
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
return token;
|
return token;
|
||||||
|
|||||||
@@ -57,13 +57,20 @@ export class AuthController {
|
|||||||
@Patch('refresh')
|
@Patch('refresh')
|
||||||
//@RequirePermissions('auth:refresh-token')
|
//@RequirePermissions('auth:refresh-token')
|
||||||
async refreshToken(@Req() req: Request,@Body() refreshTokenDto: RefreshTokenDto) {
|
async refreshToken(@Req() req: Request,@Body() refreshTokenDto: RefreshTokenDto) {
|
||||||
console.log("Pepe");
|
|
||||||
console.log(req['user']);
|
|
||||||
//console.log(refreshTokenDto);
|
|
||||||
|
|
||||||
return null
|
// console.log("Pepeeeee");
|
||||||
|
// console.log(req['user']);
|
||||||
|
// console.log("refreshTokenDto",refreshTokenDto);
|
||||||
|
// console.log(typeof refreshTokenDto);
|
||||||
|
|
||||||
// return await this.authService.refreshToken(refreshTokenDto);
|
const data = await this.authService.refreshToken(refreshTokenDto,req['user'].sub);
|
||||||
|
// console.log("data",data);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {tokens: data}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Public()
|
// @Public()
|
||||||
|
|||||||
@@ -261,8 +261,9 @@ export class AuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Refresh User Access Token
|
//Refresh User Access Token
|
||||||
async refreshToken(dto: RefreshTokenDto): Promise<RefreshTokenInterface> {
|
async refreshToken(dto: RefreshTokenDto,user_id:number): Promise<RefreshTokenInterface> {
|
||||||
const { user_id } = dto;
|
// const { user_id } = dto;
|
||||||
|
// const user_id = 1;
|
||||||
|
|
||||||
const session = await this.drizzle
|
const session = await this.drizzle
|
||||||
.select()
|
.select()
|
||||||
@@ -274,16 +275,22 @@ export class AuthService {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// console.log(session.length);
|
||||||
|
|
||||||
if (session.length === 0) throw new NotFoundException('session not found');
|
if (session.length === 0) throw new NotFoundException('session not found');
|
||||||
const user = await this.findUserById(dto.user_id);
|
const user = await this.findUserById(user_id);
|
||||||
if (!user) throw new NotFoundException('User not found');
|
if (!user) throw new NotFoundException('User not found');
|
||||||
|
|
||||||
|
// Genera token
|
||||||
const tokens = await this.generateTokens(user);
|
const tokens = await this.generateTokens(user);
|
||||||
const decodeAccess = this.decodeToken(tokens.access_token);
|
const decodeAccess = this.decodeToken(tokens.access_token);
|
||||||
const decodeRefresh = this.decodeToken(tokens.refresh_token);
|
const decodeRefresh = this.decodeToken(tokens.refresh_token);
|
||||||
|
|
||||||
|
// Actualiza session
|
||||||
await this.drizzle
|
await this.drizzle
|
||||||
.update(sessions)
|
.update(sessions)
|
||||||
.set({ sessionToken: tokens.refresh_token, expiresAt: decodeRefresh.exp })
|
.set({ sessionToken: tokens.refresh_token, expiresAt: decodeRefresh.exp })
|
||||||
.where(eq(sessions.userId, dto.user_id));
|
.where(eq(sessions.userId, user_id));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
access_token: tokens.access_token,
|
access_token: tokens.access_token,
|
||||||
|
|||||||
@@ -39,15 +39,10 @@ export class CreateProductDto {
|
|||||||
status: string;
|
status: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsInt({
|
@IsOptional()
|
||||||
message: 'userID must be a number',
|
|
||||||
})
|
|
||||||
// @IsOptional()
|
|
||||||
userId: number;
|
userId: number;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString({
|
@IsOptional()
|
||||||
message: 'urlImg must be a string',
|
|
||||||
})
|
|
||||||
urlImg: string;
|
urlImg: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,43 +52,34 @@ export class UsersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
// @Roles('admin')
|
|
||||||
@ApiOperation({ summary: 'Create a new product' })
|
@ApiOperation({ summary: 'Create a new product' })
|
||||||
@ApiResponse({ status: 201, description: 'Product created successfully.' })
|
@ApiResponse({ status: 201, description: 'Product created successfully.' })
|
||||||
|
@ApiResponse({ status: 500, description: 'Internal server error.' })
|
||||||
|
@UseInterceptors(FilesInterceptor('urlImg'))
|
||||||
async create(
|
async create(
|
||||||
|
@Req() req: Request,
|
||||||
@Body() createUserDto: CreateProductDto,
|
@Body() createUserDto: CreateProductDto,
|
||||||
|
@UploadedFiles() files: Express.Multer.File[],
|
||||||
@Query('roleId') roleId?: string,
|
@Query('roleId') roleId?: string,
|
||||||
) {
|
) {
|
||||||
const data = await this.inventoryService.create(createUserDto)
|
const id = Number(req['user'].id);
|
||||||
|
const data = await this.inventoryService.create(files,createUserDto,id)
|
||||||
return { message: 'User created successfully', data };
|
return { message: 'User created successfully', data };
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch('/id/:id')
|
|
||||||
// @Roles('admin')
|
|
||||||
@ApiOperation({ summary: 'Update a product' })
|
|
||||||
@ApiResponse({ status: 200, description: 'Product updated successfully.' })
|
|
||||||
@ApiResponse({ status: 404, description: 'Product not found.' })
|
|
||||||
async update(@Param('id') id: string, @Body() UpdateProductDto: UpdateProductDto) {
|
|
||||||
const data = await this.inventoryService.update(id, UpdateProductDto);
|
|
||||||
return { message: 'User updated successfully', data };
|
|
||||||
}
|
|
||||||
|
|
||||||
@Patch('/upload')
|
@Patch('/upload')
|
||||||
@ApiOperation({ summary: 'Update a product' })
|
@ApiOperation({ summary: 'Update a product' })
|
||||||
@ApiResponse({ status: 200, description: 'Product uploaded successfully.'})
|
@ApiResponse({ status: 200, description: 'Product uploaded successfully.'})
|
||||||
@ApiResponse({ status: 404, description: 'Product not found.' })
|
@ApiResponse({ status: 404, description: 'Product not found.' })
|
||||||
@ApiResponse({ status: 500, description: 'Internal server error.' })
|
@ApiResponse({ status: 500, description: 'Internal server error.' })
|
||||||
@UseInterceptors(FilesInterceptor('urlImg'))
|
@UseInterceptors(FilesInterceptor('urlImg'))
|
||||||
async uploadFile(@Req() req: Request, @UploadedFiles() files: Express.Multer.File[], @Body() body: any) {
|
async uploadFile(
|
||||||
// Aquí puedes acceder a los campos del formulario
|
@Req() req: Request,
|
||||||
// console.log('Archivos:', files);
|
@UploadedFiles() files: Express.Multer.File[],
|
||||||
|
@Body() body: any
|
||||||
|
) {
|
||||||
const id = Number(req['user'].id);
|
const id = Number(req['user'].id);
|
||||||
// console.log(req['user'].id)
|
|
||||||
// console.log('Otros campos del formulario:', body);
|
|
||||||
const result = await this.inventoryService.saveImages(files,body,id);
|
const result = await this.inventoryService.saveImages(files,body,id);
|
||||||
|
|
||||||
// const result = ['result']
|
|
||||||
|
|
||||||
return { data: result };
|
return { data: result };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -187,63 +187,92 @@ export class InventoryService {
|
|||||||
|
|
||||||
// Rest of the service remains the same
|
// Rest of the service remains the same
|
||||||
async create(
|
async create(
|
||||||
createProductDto: CreateProductDto
|
file: Express.Multer.File[],
|
||||||
|
createProductDto: CreateProductDto,
|
||||||
|
userId: number,
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
|
|
||||||
|
let gallery: string[] = [];
|
||||||
|
|
||||||
|
|
||||||
|
await Promise.all(file.map(async (f, index) => {
|
||||||
|
const fileName = `${index + 1}-${f.originalname}`;
|
||||||
|
gallery.push(fileName);
|
||||||
|
}));
|
||||||
|
|
||||||
|
console.log(gallery);
|
||||||
|
|
||||||
|
|
||||||
// Start a transaction
|
// Start a transaction
|
||||||
return await this.drizzle.transaction(async (tx) => {
|
return await this.drizzle.transaction(async (tx) => {
|
||||||
|
|
||||||
const [newProduct] = await tx
|
const productValue = {
|
||||||
.insert(products)
|
|
||||||
.values({
|
|
||||||
title: createProductDto.title,
|
title: createProductDto.title,
|
||||||
description: createProductDto.description,
|
description: createProductDto.description,
|
||||||
price: createProductDto.price,
|
price: createProductDto.price,
|
||||||
address: createProductDto.address,
|
address: createProductDto.address,
|
||||||
urlImg: createProductDto.urlImg,
|
|
||||||
stock: createProductDto.stock,
|
|
||||||
status: createProductDto.status,
|
status: createProductDto.status,
|
||||||
userId: createProductDto.userId
|
urlImg: gallery[0],
|
||||||
})
|
stock: createProductDto.stock,
|
||||||
|
userId: userId,
|
||||||
|
gallery: gallery
|
||||||
|
}
|
||||||
|
console.log(productValue);
|
||||||
|
|
||||||
|
|
||||||
|
const [newProduct] = await tx
|
||||||
|
.insert(products)
|
||||||
|
.values(productValue)
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
|
const productId = newProduct.id;
|
||||||
|
const picturesPath = join(__dirname, '..', '..', '..', '..', 'web', 'public', 'uploads', 'inventory',userId.toString() , productId.toString());
|
||||||
|
// Crea el directorio si no existe
|
||||||
|
await mkdir(picturesPath, { recursive: true });
|
||||||
|
|
||||||
|
await Promise.all(file.map(async (f, index) => {
|
||||||
|
const fileName = `${index + 1}-${f.originalname}`;
|
||||||
|
const filePath = join(picturesPath, fileName);
|
||||||
|
await writeFile(filePath, f.buffer);
|
||||||
|
}));
|
||||||
|
|
||||||
return newProduct
|
return newProduct
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, updateProductDto: UpdateProductDto): Promise<Product> {
|
// async update(id: string, updateProductDto: UpdateProductDto): Promise<Product> {
|
||||||
const productId = parseInt(id);
|
// const productId = parseInt(id);
|
||||||
// console.log(updateProductDto);
|
// // console.log(updateProductDto);
|
||||||
|
|
||||||
// Check if exists
|
// // Check if exists
|
||||||
await this.findOne(id);
|
// await this.findOne(id);
|
||||||
|
|
||||||
// Prepare update data
|
// // Prepare update data
|
||||||
const updateData: any = {};
|
// const updateData: any = {};
|
||||||
if (updateProductDto.title) updateData.title = updateProductDto.title;
|
// if (updateProductDto.title) updateData.title = updateProductDto.title;
|
||||||
if (updateProductDto.description) updateData.description = updateProductDto.description;
|
// if (updateProductDto.description) updateData.description = updateProductDto.description;
|
||||||
if (updateProductDto.price) updateData.price = updateProductDto.price;
|
// if (updateProductDto.price) updateData.price = updateProductDto.price;
|
||||||
if (updateProductDto.address) updateData.address = updateProductDto.address;
|
// if (updateProductDto.address) updateData.address = updateProductDto.address;
|
||||||
if (updateProductDto.status) updateData.status = updateProductDto.status;
|
// if (updateProductDto.status) updateData.status = updateProductDto.status;
|
||||||
if (updateProductDto.stock) updateData.stock = updateProductDto.stock;
|
// if (updateProductDto.stock) updateData.stock = updateProductDto.stock;
|
||||||
if (updateProductDto.urlImg) updateData.urlImg = updateProductDto.urlImg;
|
// if (updateProductDto.urlImg) updateData.urlImg = updateProductDto.urlImg;
|
||||||
|
|
||||||
const [updatedProduct] = await this.drizzle.update(products).set(updateData).where(eq(products.id, productId)).returning();
|
// const [updatedProduct] = await this.drizzle.update(products).set(updateData).where(eq(products.id, productId)).returning();
|
||||||
return updatedProduct
|
// return updatedProduct
|
||||||
// Return updated user
|
// // Return updated user
|
||||||
// return this.findOne(id);
|
// // return this.findOne(id);
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Guarda una imagen en el directorio de imágenes.
|
* Guarda una imagen en el directorio de imágenes.
|
||||||
* @param file - El archivo de imagen a guardar.
|
* @param file - El archivo de imagen a guardar.
|
||||||
* @returns La ruta de la imagen guardada.
|
* @returns La ruta de la imagen guardada.
|
||||||
*/
|
*/
|
||||||
async saveImages(file: Express.Multer.File[], updateProductDto: UpdateProductDto, id: number): Promise<Product> {
|
async saveImages(file: Express.Multer.File[], updateProductDto: UpdateProductDto, userId: number): Promise<Product> {
|
||||||
const productId = parseInt(id.toString());
|
const productId = parseInt(updateProductDto.id);
|
||||||
|
|
||||||
// Construye la ruta al directorio de imágenes.
|
// Construye la ruta al directorio de imágenes.
|
||||||
const picturesPath = join(__dirname, '..', '..', '..', '..', 'web', 'public', 'uploads', 'inventory', id.toString());
|
const picturesPath = join(__dirname, '..', '..', '..', '..', 'web', 'public', 'uploads', 'inventory', userId.toString() , productId.toString());
|
||||||
|
|
||||||
// --- NUEVA LÓGICA: Borrar el directorio anterior ---
|
// --- NUEVA LÓGICA: Borrar el directorio anterior ---
|
||||||
try {
|
try {
|
||||||
@@ -278,6 +307,7 @@ export class InventoryService {
|
|||||||
if (updateProductDto.status) updateData.status = updateProductDto.status;
|
if (updateProductDto.status) updateData.status = updateProductDto.status;
|
||||||
if (updateProductDto.stock) updateData.stock = updateProductDto.stock;
|
if (updateProductDto.stock) updateData.stock = updateProductDto.stock;
|
||||||
if (file && file.length > 0) updateData.gallery = gallery;
|
if (file && file.length > 0) updateData.gallery = gallery;
|
||||||
|
if (file && file.length > 0) updateData.urlImg = gallery[0];
|
||||||
|
|
||||||
const [updatedProduct] = await this.drizzle.update(products).set(updateData).where(eq(products.id, productId)).returning();
|
const [updatedProduct] = await this.drizzle.update(products).set(updateData).where(eq(products.id, productId)).returning();
|
||||||
return updatedProduct;
|
return updatedProduct;
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from '@repo/shadcn/card';
|
} from '@repo/shadcn/card';
|
||||||
import { Edit } from 'lucide-react';
|
import {ProductList} from '@/feactures/inventory/components/products/see-product'
|
||||||
|
|
||||||
|
|
||||||
export default async function SurveyResponsePage({
|
export default async function SurveyResponsePage({
|
||||||
params,
|
params,
|
||||||
@@ -39,56 +38,57 @@ export default async function SurveyResponsePage({
|
|||||||
// console.log(data.data);
|
// console.log(data.data);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<ProductList product={product} />
|
||||||
// <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'>
|
||||||
<div className='w-full flex justify-between flex-col'>
|
// <div className='w-full flex justify-between flex-col'>
|
||||||
|
|
||||||
<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.urlImg}`}
|
// src={`http://localhost:3000/uploads/inventory/${product.userId}/${product.urlImg}`}
|
||||||
alt=""
|
// alt=""
|
||||||
/>
|
// />
|
||||||
|
|
||||||
<section className=''>
|
// <section className=''>
|
||||||
<img
|
// <img
|
||||||
className="border-2 object-cover w-[64px] h-[64px] md:w-[96px] md:h-[96px] aspect-square rounded-2xl"
|
// className="border-2 object-cover w-[64px] h-[64px] md:w-[96px] md:h-[96px] aspect-square rounded-2xl"
|
||||||
src={`http://localhost:3000/uploads/inventory/${product.userId}/${product.urlImg}`}
|
// src={`http://localhost:3000/uploads/inventory/${product.userId}/${product.urlImg}`}
|
||||||
alt=""
|
// alt=""
|
||||||
/>
|
// />
|
||||||
</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">
|
||||||
{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} </p>
|
||||||
{product.status === 'AGOTADO' ? (
|
// {product.status === 'AGOTADO' ? (
|
||||||
<p className="font-semibold text-lg text-red-900">AGOTADO</p>
|
// <p className="font-semibold text-lg text-red-900">AGOTADO</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 md:px-4 lg:px-6 flex-col justify-between flex-grow md:overflow-auto">
|
||||||
<div>
|
// <div>
|
||||||
<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> */}
|
// {/* <p className='p-1'>{lorem+lorem+lorem+lorem}</p> */}
|
||||||
</div>
|
// </div>
|
||||||
|
|
||||||
<div className='mt-2'>
|
// <div className='mt-2'>
|
||||||
<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>
|
// </div>
|
||||||
</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>
|
||||||
</div>
|
// </div>
|
||||||
</CardFooter>
|
// </CardFooter>
|
||||||
</Card>
|
// </Card>
|
||||||
</main>
|
// </main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -7,12 +7,7 @@ import {
|
|||||||
|
|
||||||
export const resfreshTokenAction = async (refreshToken: RefreshTokenValue) => {
|
export const resfreshTokenAction = async (refreshToken: RefreshTokenValue) => {
|
||||||
try {
|
try {
|
||||||
const body = {
|
const response = await refreshApi.patch('/auth/refresh', {refresh_token: refreshToken.token});
|
||||||
token: refreshToken.token,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usa la nueva instancia `refreshApi`
|
|
||||||
const response = await refreshApi.patch('/auth/refresh', body);
|
|
||||||
|
|
||||||
const parsed = RefreshTokenResponseSchema.safeParse(response.data);
|
const parsed = RefreshTokenResponseSchema.safeParse(response.data);
|
||||||
|
|
||||||
|
|||||||
@@ -118,15 +118,15 @@ export const getProductById = async (id: number) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const createProductAction = async (payload: FormData) => {
|
export const createProductAction = async (payload: FormData) => {
|
||||||
const session = await auth()
|
// const session = await auth()
|
||||||
const userId = session?.user?.id
|
// const userId = session?.user?.id
|
||||||
|
|
||||||
if (userId) {
|
// if (userId) {
|
||||||
payload.append('userId', String(userId));
|
// payload.append('userId', String(userId));
|
||||||
}
|
// }
|
||||||
|
|
||||||
const [error, data] = await safeFetchApi(
|
const [error, data] = await safeFetchApi(
|
||||||
productMutate,
|
test,
|
||||||
'/products',
|
'/products',
|
||||||
'POST',
|
'POST',
|
||||||
payload,
|
payload,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
} from '@repo/shadcn/select';
|
} from '@repo/shadcn/select';
|
||||||
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 { useCreateUser } from "@/feactures/inventory/hooks/use-mutation";
|
import { useCreateProduct } from "@/feactures/inventory/hooks/use-mutation";
|
||||||
import { editInventory, EditInventory } from '@/feactures/inventory/schemas/inventory';
|
import { editInventory, 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'
|
||||||
@@ -37,7 +37,7 @@ export function CreateForm({
|
|||||||
const {
|
const {
|
||||||
mutate: saveProduct,
|
mutate: saveProduct,
|
||||||
isPending: isSaving,
|
isPending: isSaving,
|
||||||
} = useCreateUser();
|
} = useCreateProduct();
|
||||||
|
|
||||||
const [sizeFile, setSizeFile] = useState('0 bytes');
|
const [sizeFile, setSizeFile] = useState('0 bytes');
|
||||||
const [previewUrls, setPreviewUrls] = useState<string[]>([]);
|
const [previewUrls, setPreviewUrls] = useState<string[]>([]);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const columns: ColumnDef<InventoryTable>[] = [
|
|||||||
header: 'img',
|
header: 'img',
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
return (
|
return (
|
||||||
<img src={`http://localhost:3000/uploads/inventory/${row.original.userId}/${row.original.urlImg}`} alt="" width={64} height={64} className="rounded"/>
|
<img src={`http://localhost:3000/uploads/inventory/${row.original.userId}/${row.original.id}/${row.original.urlImg}`} alt="" width={64} height={64} className="rounded"/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
} from '@repo/shadcn/select';
|
} from '@repo/shadcn/select';
|
||||||
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 { useUpdateUser } 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 { editInventory, EditInventory } 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'
|
||||||
@@ -55,7 +55,7 @@ export function UpdateForm({
|
|||||||
mutate: saveAccountingAccounts,
|
mutate: saveAccountingAccounts,
|
||||||
isPending: isSaving,
|
isPending: isSaving,
|
||||||
isError,
|
isError,
|
||||||
} = useUpdateUser();
|
} = useUpdateProduct();
|
||||||
|
|
||||||
const [sizeFile, setSizeFile] = useState('0 bytes');
|
const [sizeFile, setSizeFile] = useState('0 bytes');
|
||||||
const [previewUrls, setPreviewUrls] = useState<string[]>([]);
|
const [previewUrls, setPreviewUrls] = useState<string[]>([]);
|
||||||
@@ -106,10 +106,10 @@ export function UpdateForm({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- IMPORTANTE: Tu hook `useUpdateUser` DEBE ser capaz de aceptar FormData ---
|
// --- IMPORTANTE: Tu hook `useUpdateProduct` DEBE ser capaz de aceptar FormData ---
|
||||||
// Si `useUpdateUser` llama a `safeFetchApi`, entonces `safeFetchApi` ya está preparado
|
// Si `useUpdateProduct` llama a `safeFetchApi`, entonces `safeFetchApi` ya está preparado
|
||||||
// para recibir `FormData`.
|
// para recibir `FormData`.
|
||||||
saveAccountingAccounts(formData as any, { // Forzamos el tipo a 'any' si `useUpdateUser` no espera FormData en su tipo
|
saveAccountingAccounts(formData as any, { // Forzamos el tipo a 'any' si `useUpdateProduct` no espera FormData en su tipo
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
form.reset();
|
form.reset();
|
||||||
onSuccess?.();
|
onSuccess?.();
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export function ProductList() {
|
|||||||
<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.urlImg}`}
|
src={`http://localhost:3000/uploads/inventory/${data.userId}/${data.id}/${data.urlImg}`}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { allProducts, } from "../../schemas/inventory";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from '@repo/shadcn/card';
|
||||||
|
|
||||||
|
export function ProductList({product}: {product: allProducts}) {
|
||||||
|
return (
|
||||||
|
// <PageContainer>
|
||||||
|
<main className='px-4 lg:px-6 flex flex-col md:flex-row gap-3 lg:gap-4 md:relative'>
|
||||||
|
<div className='w-full flex justify-between flex-col'>
|
||||||
|
|
||||||
|
<img
|
||||||
|
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}`}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className=''>
|
||||||
|
<img
|
||||||
|
className="border-2 object-cover w-[64px] h-[64px] md:w-[96px] md:h-[96px] aspect-square rounded-2xl"
|
||||||
|
src={`http://localhost:3000/uploads/inventory/${product.userId}/${product.id}/${product.urlImg}`}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</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">
|
||||||
|
<CardHeader className='py-2 px-2 md:px-4 lg:px-6'>
|
||||||
|
<CardTitle className="font-bold text-2xl">
|
||||||
|
{product.title.charAt(0).toUpperCase() + product.title.slice(1)}
|
||||||
|
</CardTitle>
|
||||||
|
<p className='font-semibold'>$ {product.price} </p>
|
||||||
|
{product.status === 'AGOTADO' ? (
|
||||||
|
<p className="font-semibold text-lg text-red-900">AGOTADO</p>
|
||||||
|
) : ('')}
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="py-0 px-2 md:px-4 lg:px-6 flex-col justify-between flex-grow md:overflow-auto">
|
||||||
|
<div>
|
||||||
|
<p className='font-semibold text-lg border-t border-b'> Descripción</p>
|
||||||
|
<p className='p-1'>{product.description}</p>
|
||||||
|
{/* <p className='p-1'>{lorem+lorem+lorem+lorem}</p> */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-2'>
|
||||||
|
<p className='font-semibold text-lg border-t border-b'> Dirección</p>
|
||||||
|
<p className='p-1'>{product.address}</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
|
||||||
|
<CardFooter className="px-2 md:px-4 lg:px-6">
|
||||||
|
<div>
|
||||||
|
<p className='font-semibold text-lg border-t border-b mt-4'>Información del vendedor</p>
|
||||||
|
<p>{product.fullname}</p>
|
||||||
|
<p>{product.phone}</p>
|
||||||
|
<p>{product.email}</p>
|
||||||
|
</div>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import { EditInventory } from "../schemas/inventory";
|
|||||||
import { updateUserAction, createProductAction, updateUserAction2 } from "../actions/actions";
|
import { updateUserAction, createProductAction, updateUserAction2 } from "../actions/actions";
|
||||||
|
|
||||||
// Create mutation
|
// Create mutation
|
||||||
export function useCreateUser() {
|
export function useCreateProduct() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationFn: (data: any) => createProductAction(data),
|
mutationFn: (data: any) => createProductAction(data),
|
||||||
@@ -13,7 +13,7 @@ export function useCreateUser() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update mutation
|
// Update mutation
|
||||||
export function useUpdateUser() {
|
export function useUpdateProduct() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
// mutationFn: (data: EditInventory) => updateUserAction(data),
|
// mutationFn: (data: EditInventory) => updateUserAction(data),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// import { user } from '@/feactures/auth/schemas/register';
|
// import { user } from '@/feactures/auth/schemas/register';
|
||||||
// import { all } from 'axios';
|
// import { all } from 'axios';
|
||||||
|
import { url } from 'inspector';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
export type InventoryTable = z.infer<typeof product>;
|
export type InventoryTable = z.infer<typeof product>;
|
||||||
@@ -26,7 +27,11 @@ export const product = z.object({
|
|||||||
userId: z.number().optional()
|
userId: z.number().optional()
|
||||||
})
|
})
|
||||||
|
|
||||||
export const productDetails = product.extend({
|
export const seeProduct = product.extend({
|
||||||
|
urlImg: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const productDetails = seeProduct.extend({
|
||||||
fullname: z.string(),
|
fullname: z.string(),
|
||||||
phone: z.string().nullable(),
|
phone: z.string().nullable(),
|
||||||
email: z.string().email().nullable()
|
email: z.string().email().nullable()
|
||||||
|
|||||||
@@ -100,65 +100,77 @@ const authConfig: NextAuthConfig = {
|
|||||||
signIn: '/', //sigin page
|
signIn: '/', //sigin page
|
||||||
},
|
},
|
||||||
callbacks: {
|
callbacks: {
|
||||||
async jwt({
|
async jwt({ token, user }:{
|
||||||
token,
|
user: User
|
||||||
user,
|
token: any
|
||||||
}: {
|
|
||||||
token: any;
|
|
||||||
user: User;
|
|
||||||
}) {
|
}) {
|
||||||
try {
|
// 1. Manejar el inicio de sesión inicial
|
||||||
// Si es un nuevo login, asignamos los datos
|
// El `user` solo se proporciona en el primer inicio de sesión.
|
||||||
if (user) {
|
if (user) {
|
||||||
token.id = user.id;
|
return {
|
||||||
token.username = user.username;
|
id: user.id,
|
||||||
token.fullname = user.fullname;
|
username: user.username,
|
||||||
token.email = user.email;
|
fullname: user.fullname,
|
||||||
token.role = user.role;
|
email: user.email,
|
||||||
token.access_token = user.access_token;
|
role: user.role,
|
||||||
token.access_expire_in = user.access_expire_in;
|
access_token: user.access_token,
|
||||||
token.refresh_token = user.refresh_token;
|
access_expire_in: user.access_expire_in,
|
||||||
token.refresh_expire_in = user.refresh_expire_in;
|
refresh_token: user.refresh_token,
|
||||||
return token; // IMPORTANTE: retornamos el token aquí para evitar que entre en el siguiente 'if'
|
refresh_expire_in: user.refresh_expire_in
|
||||||
|
}
|
||||||
|
// return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verificar si el access_token ha expirado
|
// 2. Si no es un nuevo login, verificar la expiración del token
|
||||||
const now = Date.now() / 1000;
|
const now = Math.floor(Date.now() / 1000); // Usar Math.floor para un número entero
|
||||||
|
|
||||||
|
// Verificar si el token de acceso aún es válido
|
||||||
if (now < (token.access_expire_in as number)) {
|
if (now < (token.access_expire_in as number)) {
|
||||||
return token; // Si el token no ha expirado, lo retornamos sin cambios
|
return token; // Si no ha expirado, no hacer nada y devolver el token actual
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si el access_token ha expirado, verificar el refresh_token
|
console.log("Now Access Expire:",token.access_expire_in);
|
||||||
|
|
||||||
|
|
||||||
|
// 3. Si el token de acceso ha expirado, verificar el 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 logout
|
return null; // Forzar el logout al devolver null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si el access_token ha expirado pero el refresh_token es válido, renovar
|
console.log("token:", token.refresh_token);
|
||||||
|
|
||||||
|
|
||||||
|
// 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...");
|
||||||
const res = await resfreshTokenAction({
|
try {
|
||||||
token: token.refresh_token as string,
|
const res = await resfreshTokenAction({ token: token.refresh_token as string });
|
||||||
});
|
|
||||||
|
|
||||||
console.log("Pepe");
|
if (!res || !res.tokens) {
|
||||||
|
throw new Error('Fallo en la respuesta de la API de refresco.');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Old Access Expire:", token.access_expire_in);
|
||||||
|
console.log("New Access Expire:", res.tokens.access_expire_in);
|
||||||
|
|
||||||
|
console.log("token:", token.refresh_token);
|
||||||
|
|
||||||
|
|
||||||
if (!res) throw new Error('Fallo al renovar el token');
|
// Actualizar el token directamente con los nuevos valores
|
||||||
|
token.access_token = res.tokens.access_token;
|
||||||
|
token.access_expire_in = res.tokens.access_expire_in;
|
||||||
|
token.refresh_token = res.tokens.refresh_token;
|
||||||
|
token.refresh_expire_in = res.tokens.refresh_expire_in;
|
||||||
|
return token;
|
||||||
|
|
||||||
// Actualizar el token con la nueva información
|
|
||||||
return {
|
|
||||||
...token,
|
|
||||||
access_token: res.tokens.access_token,
|
|
||||||
access_expire_in: res.tokens.access_expire_in,
|
|
||||||
refresh_token: res.tokens.refresh_token,
|
|
||||||
refresh_expire_in: res.tokens.refresh_expire_in,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error al renovar el token: ", error);
|
console.error("Error al renovar el token: ", error);
|
||||||
return null; // Fallo al renovar, forzar logout
|
return null; // Fallo al renovar, forzar logout
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async session({ session, token }: { session: Session; token: DefaultJWT }) {
|
async session({ session, token }: { session: Session; token: any }) {
|
||||||
session.access_token = token.access_token as string;
|
session.access_token = token.access_token as string;
|
||||||
session.access_expire_in = token.access_expire_in as number;
|
session.access_expire_in = token.access_expire_in as number;
|
||||||
session.refresh_token = token.refresh_token as string;
|
session.refresh_token = token.refresh_token as string;
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ 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...");
|
||||||
|
|
||||||
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();
|
||||||
const token = session?.access_token;
|
const token = session?.access_token;
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 786 KiB |
BIN
apps/web/public/uploads/inventory/1/1/1-17102590731691.jpg
Normal file
BIN
apps/web/public/uploads/inventory/1/1/1-17102590731691.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
BIN
apps/web/public/uploads/inventory/1/1/2-MANZNA-ROJA.jpg
Normal file
BIN
apps/web/public/uploads/inventory/1/1/2-MANZNA-ROJA.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.9 KiB |
Reference in New Issue
Block a user