Add a "Driver" checkbox column to the User Management page. Checking it creates a linked Driver record so the user appears in the drivers list, can be assigned events, and enrolled for GPS tracking — without changing their primary role. The DRIVER role checkbox is auto-checked and disabled since being a driver is inherent to that role. Promoting a user from DRIVER to Admin/Coordinator preserves their driver record. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
123 lines
3.4 KiB
TypeScript
123 lines
3.4 KiB
TypeScript
import { Injectable, NotFoundException, Logger } from '@nestjs/common';
|
|
import { PrismaService } from '../prisma/prisma.service';
|
|
import { UpdateUserDto, ApproveUserDto } from './dto';
|
|
import { Role } from '@prisma/client';
|
|
|
|
@Injectable()
|
|
export class UsersService {
|
|
private readonly logger = new Logger(UsersService.name);
|
|
|
|
constructor(private prisma: PrismaService) {}
|
|
|
|
async findAll() {
|
|
return this.prisma.user.findMany({
|
|
where: { deletedAt: null },
|
|
include: { driver: true },
|
|
orderBy: { createdAt: 'desc' },
|
|
});
|
|
}
|
|
|
|
async findOne(id: string) {
|
|
const user = await this.prisma.user.findFirst({
|
|
where: { id, deletedAt: null },
|
|
include: { driver: true },
|
|
});
|
|
|
|
if (!user) {
|
|
throw new NotFoundException(`User with ID ${id} not found`);
|
|
}
|
|
|
|
return user;
|
|
}
|
|
|
|
async update(id: string, updateUserDto: UpdateUserDto) {
|
|
const user = await this.findOne(id);
|
|
|
|
this.logger.log(`Updating user ${id}: ${JSON.stringify(updateUserDto)}`);
|
|
|
|
const { isAlsoDriver, ...prismaData } = updateUserDto;
|
|
const effectiveRole = updateUserDto.role || user.role;
|
|
|
|
// Handle role change to DRIVER: auto-create driver record
|
|
if (updateUserDto.role === Role.DRIVER && !user.driver) {
|
|
this.logger.log(
|
|
`Creating Driver record for user ${user.email} (role change to DRIVER)`,
|
|
);
|
|
await this.prisma.driver.create({
|
|
data: {
|
|
name: user.name || user.email,
|
|
phone: user.email,
|
|
userId: user.id,
|
|
},
|
|
});
|
|
}
|
|
|
|
// When promoting FROM DRIVER to Admin/Coordinator, keep the driver record
|
|
// (admin can explicitly uncheck the driver box later if they want)
|
|
|
|
// Handle "Also a Driver" toggle (independent of role)
|
|
if (isAlsoDriver === true && !user.driver) {
|
|
this.logger.log(
|
|
`Creating Driver record for user ${user.email} (isAlsoDriver toggled on)`,
|
|
);
|
|
await this.prisma.driver.create({
|
|
data: {
|
|
name: user.name || user.email,
|
|
phone: user.email,
|
|
userId: user.id,
|
|
},
|
|
});
|
|
} else if (isAlsoDriver === false && user.driver && effectiveRole !== Role.DRIVER) {
|
|
// Only allow removing driver record if user is NOT in the DRIVER role
|
|
this.logger.log(
|
|
`Soft-deleting Driver record for user ${user.email} (isAlsoDriver toggled off)`,
|
|
);
|
|
await this.prisma.driver.update({
|
|
where: { id: user.driver.id },
|
|
data: { deletedAt: new Date() },
|
|
});
|
|
}
|
|
|
|
return this.prisma.user.update({
|
|
where: { id: user.id },
|
|
data: prismaData,
|
|
include: { driver: true },
|
|
});
|
|
}
|
|
|
|
async approve(id: string, approveUserDto: ApproveUserDto) {
|
|
const user = await this.findOne(id);
|
|
|
|
this.logger.log(
|
|
`${approveUserDto.isApproved ? 'Approving' : 'Denying'} user: ${user.email}`,
|
|
);
|
|
|
|
return this.prisma.user.update({
|
|
where: { id: user.id },
|
|
data: { isApproved: approveUserDto.isApproved },
|
|
include: { driver: true },
|
|
});
|
|
}
|
|
|
|
async remove(id: string) {
|
|
const user = await this.findOne(id);
|
|
|
|
this.logger.log(`Soft deleting user: ${user.email}`);
|
|
|
|
return this.prisma.user.update({
|
|
where: { id: user.id },
|
|
data: { deletedAt: new Date() },
|
|
});
|
|
}
|
|
|
|
async getPendingUsers() {
|
|
return this.prisma.user.findMany({
|
|
where: {
|
|
deletedAt: null,
|
|
isApproved: false,
|
|
},
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
}
|
|
}
|