add group to user
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
2026-07-22 11:52:10 +03:30
parent 096297d1d1
commit 549da39473
6 changed files with 117 additions and 9 deletions
+11
View File
@@ -0,0 +1,11 @@
{
"folders": [
{
"path": "."
},
{
"path": "../dmenu-admin"
}
],
"settings": {}
}
@@ -215,6 +215,17 @@ export class UsersController {
return this.userService.adminUpdateUser(userId, restId, dto);
}
@UseGuards(AdminAuthGuard)
@ApiBearerAuth()
@Permissions(Permission.MANAGE_USERS)
@ApiHeader(API_HEADER_SLUG)
@ApiOperation({ summary: 'Get user groups for a customer (admin)' })
@ApiParam({ name: 'userId', description: 'User ID' })
@Get('admin/users/:userId/groups')
async adminGetUserGroups(@Param('userId') userId: string, @RestId() restId: string) {
return this.userService.getUserGroups(userId, restId);
}
@UseGuards(AdminAuthGuard)
@ApiBearerAuth()
@Permissions(Permission.MANAGE_USERS)
+12 -1
View File
@@ -1,5 +1,5 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsBoolean, IsMobilePhone, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { IsArray, IsBoolean, IsMobilePhone, IsNotEmpty, IsOptional, IsString } from 'class-validator';
export class CreateUserDto {
@ApiProperty({ description: "User's phone number", example: '09121234567' })
@@ -37,4 +37,15 @@ export class CreateUserDto {
@IsOptional()
@IsString()
avatarUrl?: string;
@ApiPropertyOptional({
description: 'User group IDs to assign the customer to',
type: [String],
example: ['01HXABCDEFGHIJKLMNOPQRSTUV'],
})
@IsOptional()
@IsArray()
@IsString({ each: true })
@IsNotEmpty({ each: true })
groupIds?: string[];
}
+12 -1
View File
@@ -1,4 +1,4 @@
import { IsString, IsOptional, IsBoolean } from 'class-validator';
import { IsString, IsOptional, IsBoolean, IsArray, IsNotEmpty } from 'class-validator';
import { ApiPropertyOptional } from '@nestjs/swagger';
export class UpdateUserDto {
@@ -32,4 +32,15 @@ export class UpdateUserDto {
@IsOptional()
@IsString()
avatarUrl?: string;
@ApiPropertyOptional({
description: 'User group IDs to assign the customer to (replaces current groups)',
type: [String],
example: ['01HXABCDEFGHIJKLMNOPQRSTUV'],
})
@IsOptional()
@IsArray()
@IsString({ each: true })
@IsNotEmpty({ each: true })
groupIds?: string[];
}
@@ -138,4 +138,42 @@ export class UserGroupService {
group.count = Math.max(0, group.count - 1);
return group;
}
async assignUserToGroups(restId: string, userId: string, groupIds: string[]): Promise<void> {
const uniqueGroupIds = [...new Set(groupIds)];
for (const groupId of uniqueGroupIds) {
await this.addUsers(restId, groupId, { userIds: [userId] });
}
}
async getGroupsForUser(restId: string, userId: string): Promise<UserGroup[]> {
const links = await this.userGroupUserRepository.find(
{
user: { id: userId },
userGroup: { restaurant: { id: restId }, deletedAt: null },
},
{ populate: ['userGroup'], orderBy: { createdAt: 'asc' } },
);
return links.map(link => link.userGroup);
}
async syncUserGroups(restId: string, userId: string, groupIds: string[]): Promise<void> {
const targetGroupIds = [...new Set(groupIds)];
const currentGroups = await this.getGroupsForUser(restId, userId);
const currentGroupIds = new Set(currentGroups.map(group => group.id));
const targetGroupIdsSet = new Set(targetGroupIds);
for (const group of currentGroups) {
if (!targetGroupIdsSet.has(group.id)) {
await this.removeUser(restId, group.id, userId);
}
}
for (const groupId of targetGroupIds) {
if (!currentGroupIds.has(groupId)) {
await this.addUsers(restId, groupId, { userIds: [userId] });
}
}
}
}
+33 -7
View File
@@ -25,6 +25,7 @@ import { UserRestaurant } from '../entities/user-restuarant.entity';
import { ImportUsersResult } from '../interfaces/import-users.interface';
import { assertExcelMimeType, parseUsersExcel } from '../utils/import-users-excel.util';
import { AuthMessage, UserImportMessage } from 'src/common/enums/message.enum';
import { UserGroupService } from './user-group.service';
@Injectable()
export class UserService {
@@ -35,6 +36,7 @@ export class UserService {
private readonly walletService: WalletService,
private readonly em: EntityManager,
private readonly userRestaurantRepository: UserRestaurantRepository,
private readonly userGroupService: UserGroupService,
) { }
async findOrCreateByPhone(phone: string): Promise<User> {
@@ -189,6 +191,10 @@ export class UserService {
this.em.persist(userRestaurant);
await this.em.flush();
if (dto.groupIds?.length) {
await this.userGroupService.assignUserToGroups(restId, user.id, dto.groupIds);
}
await this.em.populate(userRestaurant, ['user']);
return userRestaurant;
}
@@ -202,7 +208,26 @@ export class UserService {
throw new NotFoundException(`User with ID ${userId} not found.`);
}
return this.updateUser(userId, restId, dto);
const { groupIds, ...userDto } = dto;
const user = await this.updateUser(userId, restId, userDto);
if (groupIds !== undefined) {
await this.userGroupService.syncUserGroups(restId, userId, groupIds);
}
return user;
}
async getUserGroups(userId: string, restId: string) {
const userRestaurant = await this.userRestaurantRepository.findOne({
user: { id: userId },
restaurant: { id: restId },
});
if (!userRestaurant) {
throw new NotFoundException(`User with ID ${userId} not found.`);
}
return this.userGroupService.getGroupsForUser(restId, userId);
}
async updateUser(userId: string, restId: string, dto: UpdateUserDto): Promise<User> {
@@ -211,15 +236,16 @@ export class UserService {
throw new NotFoundException(`User with ID ${userId} not found.`);
}
const { groupIds: _groupIds, ...userFields } = dto;
// Normalize date strings into Date objects if provided
const assignData: Partial<User> = { ...dto } as Partial<User>;
if (dto.birthDate) {
assignData.birthDate = new Date(dto.birthDate);
const assignData: Partial<User> = { ...userFields } as Partial<User>;
if (userFields.birthDate) {
assignData.birthDate = new Date(userFields.birthDate);
}
if (dto.marriageDate) {
assignData.marriageDate = new Date(dto.marriageDate);
if (userFields.marriageDate) {
assignData.marriageDate = new Date(userFields.marriageDate);
}
// get restuarant
this.em.assign(user, assignData);
await this.em.flush();