cat
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
2026-07-09 17:35:17 +03:30
parent 6b477adcc2
commit ea43307983
3 changed files with 89 additions and 20 deletions
@@ -56,6 +56,8 @@ export class ProductController {
}
@Get('admin/category/:id')
@UseGuards(AdminAuthGuard)
@ApiOperation({ summary: 'Get a category by id' })
@@ -89,9 +91,23 @@ export class ProductController {
@UseGuards(AuthGuard)
@ApiOperation({ summary: 'Get list of categories' })
async findAllCategoriesAsUser() {
return this.categoryService.findAll({ limit: 1000, isActive: true });
const result = await this.categoryService.findAll({ limit: 1000, isActive: true });
return result.data;
}
@Get('public/category/tree')
@UseGuards(AuthGuard)
@ApiOperation({ summary: 'Get a tree list of Categories' })
@ApiQuery({ name: 'search', required: false, type: String })
@ApiQuery({ name: 'orderBy', required: false, type: String })
@ApiQuery({ name: 'order', required: false, enum: ['asc', 'desc'] })
@ApiQuery({ name: 'isActive', required: false, type: Boolean })
async findTreeCategories(@Query() dto: FindCategoriesDto) {
const result = await this.categoryService.findTree(dto);
return result;
}
@Get('public/products')
@UseGuards(AuthGuard)
@ApiOperation({ summary: 'Get list of products' })
@@ -2,7 +2,7 @@ import { BadGatewayException, Injectable, NotFoundException } from '@nestjs/comm
import { EntityManager } from '@mikro-orm/postgresql';
import { RequiredEntityData } from '@mikro-orm/core';
import { CategoryMessage, productMessage } from 'src/common/enums/message.enum';
import { CategoryRepository } from '../repositories/category.repository';
import { CategoryRepository, CategoryTreeNode } from '../repositories/category.repository';
import { CreateCategoryDto } from '../dto/create-category.dto';
import { Category } from '../entities/category.entity';
import { FindCategoriesDto } from '../dto/find-categories.dto';
@@ -70,10 +70,14 @@ export class CategoryService {
}
findAll(dto: FindCategoriesDto) {
findAll(dto: FindCategoriesDto): Promise<{ data: Category[]; meta: { total: number; page: number; limit: number; totalPages: number } }> {
return this.categoryRepository.findAllPaginated(dto);
}
findTree(dto: FindCategoriesDto): Promise<CategoryTreeNode[]> {
return this.categoryRepository.findTree(dto);
}
async findById(catId: string): Promise<Category> {
const category = await this.categoryRepository.findOne({ id: catId },
{ populate: ['children', 'parent'] });
@@ -1,7 +1,6 @@
import { Injectable } from '@nestjs/common';
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
import { FilterQuery } from '@mikro-orm/core';
import { Product } from '../entities/product.entity';
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
import { Category } from '../entities/category.entity';
@@ -14,31 +13,60 @@ type FindCategoriesOpts = {
isActive?: boolean;
};
export type CategoryTreeNode = {
id: string;
createdAt: Date;
deletedAt?: Date;
parent: { id: string } | null;
title: string;
isActive: boolean;
avatarUrl?: string;
order?: number;
children: CategoryTreeNode[];
};
@Injectable()
export class CategoryRepository extends EntityRepository<Category> {
constructor(readonly em: EntityManager) {
super(em, Category);
}
private buildCategoryTree(categories: Category[]): Category[] {
const roots: Category[] = [];
private buildCategoryTree(categories: Category[]): CategoryTreeNode[] {
const roots: CategoryTreeNode[] = [];
const nodesById = new Map<string, CategoryTreeNode>();
for (const category of categories) {
category.children.removeAll();
const parentId = category.parent?.id ?? null;
nodesById.set(category.id, {
id: category.id,
createdAt: category.createdAt,
deletedAt: category.deletedAt,
parent: parentId ? { id: parentId } : null,
title: category.title,
isActive: category.isActive,
avatarUrl: category.avatarUrl,
order: category.order,
children: [],
});
}
for (const category of categories) {
const parentId = category.parent?.id;
const node = nodesById.get(category.id);
if (!node) continue;
if (parentId) {
const parent = categories.find((item) => item.id === parentId);
if (parent) {
parent.children.add(category);
continue;
}
const parentId = category.parent?.id;
if (!parentId) {
roots.push(node);
continue;
}
roots.push(category);
const parentNode = nodesById.get(parentId);
if (parentNode) {
parentNode.children.push(node);
continue;
}
roots.push(node);
}
return roots;
@@ -57,8 +85,8 @@ export class CategoryRepository extends EntityRepository<Category> {
if (search) {
where.$or = [
]
{ title: { $ilike: `%${search}%` } },
];
}
const allCategories = await this.find(where, {
@@ -66,9 +94,8 @@ export class CategoryRepository extends EntityRepository<Category> {
populate: ['parent'],
});
const roots = this.buildCategoryTree(allCategories);
const data = roots.slice(offset, offset + limit);
const total = roots.length;
const total = allCategories.length;
const data = allCategories.slice(offset, offset + limit);
const totalPages = Math.ceil(total / limit);
return {
@@ -81,4 +108,26 @@ export class CategoryRepository extends EntityRepository<Category> {
},
};
}
async findTree(opts: FindCategoriesOpts = {}): Promise<CategoryTreeNode[]> {
const { search, orderBy = 'createdAt', order = 'desc', isActive } = opts;
const where: FilterQuery<Category> = {};
if (typeof isActive === 'boolean') {
where.isActive = isActive;
}
if (search) {
where.$or = [
{ title: { $ilike: `%${search}%` } },
];
}
const allCategories = await this.find(where, {
orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
populate: ['parent'],
});
return this.buildCategoryTree(allCategories);
}
}