Creating a mobile menu at the bottom of the app
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsEmail,
|
||||
IsIn,
|
||||
IsNumber,
|
||||
IsString,
|
||||
Matches,
|
||||
Max,
|
||||
MaxLength,
|
||||
Min,
|
||||
@@ -14,8 +14,11 @@ const INVITE_ROLES = ['admin', 'sales', 'support'] as const;
|
||||
const ALL_ROLES = ['owner', 'admin', 'sales', 'support'] as const;
|
||||
|
||||
export class InviteUserDto {
|
||||
@IsEmail()
|
||||
email!: string;
|
||||
@IsString()
|
||||
@Matches(/^09\d{9}$|^\+98\d{10}$|^98\d{10}$|^9\d{9}$/, {
|
||||
message: 'شماره موبایل معتبر نیست',
|
||||
})
|
||||
mobile!: string;
|
||||
|
||||
@IsString()
|
||||
@MinLength(2)
|
||||
|
||||
@@ -8,8 +8,13 @@ import {
|
||||
import type { InviteUserRequest, OrgUserDto, UserRole } from '@mento/shared';
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { normalizeMobile } from '../auth/auth.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
function inviteEmailFromMobile(mobile: string): string {
|
||||
return `${mobile}@users.mento.local`;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
@@ -31,30 +36,43 @@ export class UsersService {
|
||||
throw new ForbiddenException('فقط مالک یا مدیر میتواند کاربر اضافه کند');
|
||||
}
|
||||
|
||||
const email = dto.email.trim().toLowerCase();
|
||||
const existing = await this.prisma.user.findFirst({
|
||||
where: { organizationId, email },
|
||||
});
|
||||
if (existing) {
|
||||
throw new ConflictException('این ایمیل در سازمان شما وجود دارد');
|
||||
const mobile = normalizeMobile(dto.mobile);
|
||||
if (!mobile) {
|
||||
throw new BadRequestException('شماره موبایل معتبر نیست');
|
||||
}
|
||||
|
||||
const global = await this.prisma.user.findFirst({ where: { email } });
|
||||
if (global) {
|
||||
throw new ConflictException('این ایمیل قبلاً در سیستم ثبت شده است');
|
||||
const existingMobile = await this.prisma.user.findFirst({ where: { mobile } });
|
||||
if (existingMobile) {
|
||||
throw new ConflictException('این شماره موبایل قبلاً ثبت شده است');
|
||||
}
|
||||
|
||||
const email = inviteEmailFromMobile(mobile);
|
||||
const existingEmail = await this.prisma.user.findFirst({
|
||||
where: { organizationId, email },
|
||||
});
|
||||
if (existingEmail) {
|
||||
throw new ConflictException('این شماره موبایل قبلاً ثبت شده است');
|
||||
}
|
||||
|
||||
const passwordHash = await bcrypt.hash(dto.password, 10);
|
||||
const row = await this.prisma.user.create({
|
||||
data: {
|
||||
organizationId,
|
||||
email,
|
||||
fullName: dto.fullName.trim(),
|
||||
passwordHash,
|
||||
role: dto.role,
|
||||
},
|
||||
});
|
||||
return this.toDto(row);
|
||||
try {
|
||||
const row = await this.prisma.user.create({
|
||||
data: {
|
||||
organizationId,
|
||||
email,
|
||||
mobile,
|
||||
fullName: dto.fullName.trim(),
|
||||
passwordHash,
|
||||
role: dto.role,
|
||||
},
|
||||
});
|
||||
return this.toDto(row);
|
||||
} catch (e) {
|
||||
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2002') {
|
||||
throw new ConflictException('این شماره موبایل قبلاً ثبت شده است');
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async updateCommission(
|
||||
@@ -122,6 +140,7 @@ export class UsersService {
|
||||
private toDto(row: {
|
||||
id: string;
|
||||
email: string;
|
||||
mobile?: string | null;
|
||||
fullName: string;
|
||||
role: UserRole;
|
||||
commissionPercent?: { toString(): string } | number;
|
||||
@@ -130,6 +149,7 @@ export class UsersService {
|
||||
return {
|
||||
id: row.id,
|
||||
email: row.email,
|
||||
mobile: row.mobile ?? null,
|
||||
fullName: row.fullName,
|
||||
role: row.role,
|
||||
commissionPercent: Number(row.commissionPercent ?? 0),
|
||||
|
||||
Reference in New Issue
Block a user