admin user and ui panel

This commit is contained in:
s.esmaeili
2026-09-21 08:32:31 +03:30
parent 79226c9f13
commit ef1d4638cc
341 changed files with 2270 additions and 657 deletions
@@ -81,7 +81,7 @@ export class AdminUsersService {
orderBy: orderBy as Prisma.AdminUserOrderByWithRelationInput,
skip,
take,
include: { userType: true, branch: true },
include: { userType: true, branch: true, avatarFile: true },
}),
this.prisma.adminUser.count({ where: where as Prisma.AdminUserWhereInput }),
]);
@@ -90,7 +90,11 @@ export class AdminUsersService {
}
/** Legacy field names preserved; the password hash is never exposed. */
private toRow(u: Prisma.AdminUserGetPayload<{ include: { userType: true; branch: true } }>) {
private toRow(
u: Prisma.AdminUserGetPayload<{
include: { userType: true; branch: true; avatarFile: true };
}>,
) {
return {
id: u.id,
name: u.fullName,
@@ -106,27 +110,45 @@ export class AdminUsersService {
active: u.isActive ? 1 : -1,
is_active: u.isActive,
caj: u.createdAt,
// Sales-agent fields (ReagentCustomer.vue) — set only for role=SALES_EXPERT,
// null for every other admin. Same shapes admin-customers.service.ts used
// when this data still lived on Customer, so the form binds unchanged.
address: u.address,
store_name: u.storeName,
pasazh_name: u.pasazhName,
mcode: u.memberCode,
image_id: u.avatarFileId,
image: u.avatarFile ? { id: u.avatarFile.id, image: u.avatarFile.url } : null,
};
}
async findOne(id: string) {
const u = await this.prisma.adminUser.findUnique({
where: { id },
include: { userType: true, branch: true },
include: { userType: true, branch: true, avatarFile: true },
});
if (!u) throw new NotFoundException('کاربر یافت نشد');
return this.toRow(u);
}
/** Legacy `POST users`. */
/**
* Legacy `POST users`.
*
* `role` is REQUIRED — it used to default to SALES_EXPERT whenever the
* caller omitted it, which silently turned every admin created without an
* explicit role into a sales agent regardless of the access level ("نوع
* کاربری") actually chosen. UserCreate.vue now always sends one.
*/
async create(data: Record<string, unknown>) {
const fullName = String(data.name ?? data.fname ?? '').trim();
const mobile = String(data.mobile ?? '').trim();
const password = String(data.password ?? '');
const role = data.role as AdminRole | undefined;
if (!fullName) throw new BadRequestException('نام الزامی است');
if (!mobile) throw new BadRequestException('موبایل الزامی است');
if (!password) throw new BadRequestException('رمز عبور الزامی است');
if (!role) throw new BadRequestException('نقش کاربر الزامی است');
const existing = await this.prisma.adminUser.findUnique({ where: { mobile } });
if (existing) throw new BadRequestException('این شماره موبایل قبلاً ثبت شده است');
@@ -136,7 +158,7 @@ export class AdminUsersService {
fullName,
mobile,
passwordHash: await bcrypt.hash(password, 10),
role: (data.role as AdminRole) ?? AdminRole.SALES_EXPERT,
role,
userTypeId: data.ut !== undefined && data.ut !== null ? Number(data.ut) : null,
branchId: (data.branch_id as string) || null,
wagePercent:
@@ -144,8 +166,15 @@ export class AdminUsersService {
? new Prisma.Decimal(String(data.percent))
: null,
isActive: data.is_active !== undefined ? Boolean(data.is_active) : true,
// Sales-agent-only fields (ReagentCustomer.vue); undefined for every
// other role, which Prisma leaves at column default (null).
address: (data.address as string) || null,
storeName: (data.store_name as string) || null,
pasazhName: (data.pasazh_name as string) || null,
memberCode: (data.mcode as string) || null,
avatarFileId: (data.image_id as string) || null,
},
include: { userType: true, branch: true },
include: { userType: true, branch: true, avatarFile: true },
});
}
@@ -180,11 +209,21 @@ export class AdminUsersService {
data.percent === null ? null : new Prisma.Decimal(String(data.percent));
}
if (data.is_active !== undefined) patch.isActive = Boolean(data.is_active);
if (data.address !== undefined) patch.address = (data.address as string) || null;
if (data.store_name !== undefined) patch.storeName = (data.store_name as string) || null;
if (data.pasazh_name !== undefined) patch.pasazhName = (data.pasazh_name as string) || null;
if (data.mcode !== undefined) patch.memberCode = (data.mcode as string) || null;
if (data.image_id !== undefined) {
// legacy sent '' to mean "avatar removed"
patch.avatarFile = data.image_id
? { connect: { id: String(data.image_id) } }
: { disconnect: true };
}
return this.prisma.adminUser.update({
where: { id },
data: patch,
include: { userType: true, branch: true },
include: { userType: true, branch: true, avatarFile: true },
});
}