Vista (intefaz y bd), esquema y endpoints de store agregados

This commit is contained in:
2025-07-02 15:10:54 -04:00
parent f5962efb8b
commit 365cbd0d7a
19 changed files with 1909 additions and 76 deletions

View File

@@ -0,0 +1,5 @@
ALTER TABLE "products" ALTER COLUMN "user_id" SET NOT NULL;--> statement-breakpoint
CREATE VIEW "public"."v_product_store" AS (
select p.id as product_id, p.title, p.description, p.price, p.stock, p.url_img, p.user_id, u.fullname
from products p
left join auth.users as u on u.id = p.user_id);

File diff suppressed because it is too large Load Diff

View File

@@ -22,6 +22,13 @@
"when": 1750442271575,
"tag": "0002_polite_franklin_richards",
"breakpoints": true
},
{
"idx": 3,
"version": "7",
"when": 1751482400155,
"tag": "0003_icy_gertrude_yorkes",
"breakpoints": true
}
]
}

View File

@@ -1,6 +1,7 @@
import * as t from 'drizzle-orm/pg-core';
import { timestamps } from '../timestamps';
import { users } from './auth';
import { sql } from 'drizzle-orm';
export const products = t.pgTable(
'products',
@@ -11,7 +12,21 @@ export const products = t.pgTable(
price: t.numeric('price').notNull(),
stock: t.integer('stock').notNull(),
urlImg: t.text('url_img').notNull(),
userId: t.integer('user_id').references(() => users.id, { onDelete: 'cascade' }),
userId: t.integer('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(),
...timestamps,
}
);
);
export const viewProductsStore = t.pgView('v_product_store', {
id: t.integer('product_id'),
title: t.text('title'),
description: t.text('description'),
price: t.numeric('price'),
stock: t.integer('stock'),
urlImg: t.text('url_img'),
userId: t.integer('user_id'),
fullname: t.text('fullname')
}).as(sql`
select p.id as product_id, p.title, p.description, p.price, p.stock, p.url_img, p.user_id, u.fullname
from products p
left join auth.users as u on u.id = p.user_id`);

View File

@@ -1,19 +1,39 @@
export class Product {
id?: number;
title: string;
description: string;
price: string;
stock: number;
urlImg: string;
UserId?: number;
id?: number | null;
title: string | null;
description: string | null;
price: string | null;
stock: number | null;
urlImg: string | null;
userId?: number | null;
}
export class CreateProduct {
id: number;
title: string;
description: string;
price: string;
stock: string;
urlImg: string;
UserId: number;
}
export class Inventory {
id: number | null;
title: string | null;
description: string | null;
price: string | null;
stock: number | null;
urlImg: string | null;
}
export class Store {
id: number | null;
title: string | null;
description: string | null;
price: string | null;
stock: number | null;
urlImg: string | null;
userId: number | null;
fullname?: string | null;
}
// export class CreateProduct {
// id: number;
// title: string;
// description: string;
// price: string;
// stock: string;
// urlImg: string;
// UserId: number;
// }

View File

@@ -1,4 +1,4 @@
import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
import { Controller, Get, Post, Body, Patch, Param, Delete, Query, Req } from '@nestjs/common';
import { InventoryService } from './inventory.service';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';
@@ -6,12 +6,12 @@ import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
// import { Roles } from '../../common/decorators/roles.decorator';
import { PaginationDto } from '../../common/dto/pagination.dto';
@ApiTags('inventory')
@Controller('inventory')
@ApiTags('products')
@Controller('products')
export class UsersController {
constructor(private readonly inventoryService: InventoryService) {}
@Get()
@Get('/store')
// @Roles('admin')
@ApiOperation({ summary: 'Get all products with pagination and filters' })
@ApiResponse({ status: 200, description: 'Return paginated products.' })
@@ -24,6 +24,22 @@ export class UsersController {
};
}
@Get('/inventory')
// @Roles('admin')
@ApiOperation({ summary: 'Get all products with pagination and filters' })
@ApiResponse({ status: 200, description: 'Return paginated products.' })
async findAllByUserId(@Req() req: Request, @Query() paginationDto: PaginationDto) {
console.log(req['user'].id)
const id = 1
// const id = Number(req['user'].id);
const result = await this.inventoryService.findAllByUserId(id,paginationDto);
return {
message: 'products fetched successfully',
data: result.data,
meta: result.meta
};
}
@Get(':id')
// @Roles('admin')
@ApiOperation({ summary: 'Get a product by ID' })

View File

@@ -1,12 +1,12 @@
import { DRIZZLE_PROVIDER } from 'src/database/drizzle-provider';
import { Inject, Injectable, HttpException, HttpStatus, NotFoundException, UnauthorizedException } from '@nestjs/common';
import { Inject, Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
import * as schema from 'src/database/index';
import { products } from 'src/database/index';
import { products, viewProductsStore } from 'src/database/index';
import { eq, like, or, SQL, sql, and, not } from 'drizzle-orm';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';
import { Product, CreateProduct } from './entities/inventory.entity';
import { Product, Store, Inventory } from './entities/inventory.entity';
import { PaginationDto } from '../../common/dto/pagination.dto';
@Injectable()
@@ -15,7 +15,7 @@ export class InventoryService {
@Inject(DRIZZLE_PROVIDER) private drizzle: NodePgDatabase<typeof schema>,
) { }
async findAll(paginationDto?: PaginationDto): Promise<{ data: Product[], meta: any }> {
async findAllByUserId(id: number, paginationDto?: PaginationDto): Promise<{ data: Inventory[], meta: any }> {
const { page = 1, limit = 10, search = '', sortBy = 'id', sortOrder = 'asc' } = paginationDto || {};
// Calculate offset
@@ -23,12 +23,14 @@ export class InventoryService {
// Build search condition
let searchCondition: SQL<unknown> | undefined;
if (search) {
searchCondition = or(
searchCondition = and(or(
like(products.title, `%${search}%`),
like(products.description, `%${search}%`),
// like(users.fullname, `%${search}%`)
);
), eq(products.userId, id));
}else{
searchCondition = eq(products.userId, id);
}
// Build sort condition
@@ -52,8 +54,8 @@ export class InventoryService {
title: products.title,
description: products.description,
price: products.price,
urlImg: products.urlImg,
stock: products.stock,
urlImg: products.urlImg
})
.from(products)
.where(searchCondition)
@@ -75,6 +77,67 @@ export class InventoryService {
return { data, meta };
}
async findAll(paginationDto?: PaginationDto): Promise<{ data: Store[], meta: any }> {
const { page = 1, limit = 10, search = '', sortBy = 'id', sortOrder = 'asc' } = paginationDto || {};
// Calculate offset
const offset = (page - 1) * limit;
// Build search condition
let searchCondition: SQL<unknown> | undefined;
if (search) {
searchCondition = or(
like(viewProductsStore.title, `%${search}%`),
like(viewProductsStore.description, `%${search}%`),
);
}
// Build sort condition
const orderBy = sortOrder === 'asc'
? sql`${viewProductsStore[sortBy as keyof typeof viewProductsStore]} asc`
: sql`${viewProductsStore[sortBy as keyof typeof viewProductsStore]} desc`;
// Get total count for pagination
const totalCountResult = await this.drizzle
.select({ count: sql<number>`count(*)` })
.from(viewProductsStore)
.where(searchCondition);
const totalCount = Number(totalCountResult[0].count);
const totalPages = Math.ceil(totalCount / limit);
// Get paginated data
const data = await this.drizzle
.select({
id: viewProductsStore.id,
title: viewProductsStore.title,
description: viewProductsStore.description,
price: viewProductsStore.price,
urlImg: viewProductsStore.urlImg,
stock: viewProductsStore.stock,
userId: viewProductsStore.userId,
fullname: viewProductsStore.fullname
})
.from(viewProductsStore)
.where(searchCondition)
.orderBy(orderBy)
.limit(limit)
.offset(offset);
// Build pagination metadata
const meta = {
page,
limit,
totalCount,
totalPages,
hasNextPage: page < totalPages,
hasPreviousPage: page > 1,
nextPage: page < totalPages ? page + 1 : null,
previousPage: page > 1 ? page - 1 : null,
};
return { data, meta };
}
async findOne(id: string): Promise<Product> {
const find = await this.drizzle
.select({