refactor: code efficiency improvements (Issues #9-13, #15, #17-20)
Backend: - Extract shared hard-delete authorization utility (#9) - Extract Prisma include constants per entity (#11) - Fix N+1 query pattern in events findAll (#12) - Extract shared date utility functions (#13) - Move vehicle utilization filtering to DB query (#15) - Add ParseBooleanPipe for query params - Add CurrentDriver decorator + ResolveDriverInterceptor (#20) Frontend: - Extract shared form utilities (toDatetimeLocal) and enum labels (#17) - Replace browser confirm() with styled ConfirmModal (#18) - Add centralized query-keys.ts constants (#19) - Clean up unused imports, add useMemo where needed (#19) - Standardize filter button styling across list pages Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,21 @@
|
||||
import { Injectable, NotFoundException, ForbiddenException, Logger } from '@nestjs/common';
|
||||
import { Injectable, NotFoundException, Logger } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { CreateVehicleDto, UpdateVehicleDto } from './dto';
|
||||
import { executeHardDelete } from '../common/utils';
|
||||
|
||||
@Injectable()
|
||||
export class VehiclesService {
|
||||
private readonly logger = new Logger(VehiclesService.name);
|
||||
|
||||
private readonly vehicleInclude = {
|
||||
currentDriver: true,
|
||||
events: {
|
||||
where: { deletedAt: null },
|
||||
include: { driver: true, vehicle: true },
|
||||
orderBy: { startTime: 'asc' as const },
|
||||
},
|
||||
} as const;
|
||||
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async create(createVehicleDto: CreateVehicleDto) {
|
||||
@@ -13,27 +23,14 @@ export class VehiclesService {
|
||||
|
||||
return this.prisma.vehicle.create({
|
||||
data: createVehicleDto,
|
||||
include: {
|
||||
currentDriver: true,
|
||||
events: {
|
||||
where: { deletedAt: null },
|
||||
include: { driver: true, vehicle: true },
|
||||
},
|
||||
},
|
||||
include: this.vehicleInclude,
|
||||
});
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
return this.prisma.vehicle.findMany({
|
||||
where: { deletedAt: null },
|
||||
include: {
|
||||
currentDriver: true,
|
||||
events: {
|
||||
where: { deletedAt: null },
|
||||
include: { driver: true, vehicle: true },
|
||||
orderBy: { startTime: 'asc' },
|
||||
},
|
||||
},
|
||||
include: this.vehicleInclude,
|
||||
orderBy: { name: 'asc' },
|
||||
});
|
||||
}
|
||||
@@ -54,14 +51,7 @@ export class VehiclesService {
|
||||
async findOne(id: string) {
|
||||
const vehicle = await this.prisma.vehicle.findFirst({
|
||||
where: { id, deletedAt: null },
|
||||
include: {
|
||||
currentDriver: true,
|
||||
events: {
|
||||
where: { deletedAt: null },
|
||||
include: { driver: true, vehicle: true },
|
||||
orderBy: { startTime: 'asc' },
|
||||
},
|
||||
},
|
||||
include: this.vehicleInclude,
|
||||
});
|
||||
|
||||
if (!vehicle) {
|
||||
@@ -79,34 +69,24 @@ export class VehiclesService {
|
||||
return this.prisma.vehicle.update({
|
||||
where: { id: vehicle.id },
|
||||
data: updateVehicleDto,
|
||||
include: {
|
||||
currentDriver: true,
|
||||
events: {
|
||||
where: { deletedAt: null },
|
||||
include: { driver: true, vehicle: true },
|
||||
},
|
||||
},
|
||||
include: this.vehicleInclude,
|
||||
});
|
||||
}
|
||||
|
||||
async remove(id: string, hardDelete = false, userRole?: string) {
|
||||
if (hardDelete && userRole !== 'ADMINISTRATOR') {
|
||||
throw new ForbiddenException('Only administrators can permanently delete records');
|
||||
}
|
||||
|
||||
const vehicle = await this.findOne(id);
|
||||
|
||||
if (hardDelete) {
|
||||
this.logger.log(`Hard deleting vehicle: ${vehicle.name}`);
|
||||
return this.prisma.vehicle.delete({
|
||||
where: { id: vehicle.id },
|
||||
});
|
||||
}
|
||||
|
||||
this.logger.log(`Soft deleting vehicle: ${vehicle.name}`);
|
||||
return this.prisma.vehicle.update({
|
||||
where: { id: vehicle.id },
|
||||
data: { deletedAt: new Date() },
|
||||
return executeHardDelete({
|
||||
id,
|
||||
hardDelete,
|
||||
userRole,
|
||||
findOne: (id) => this.findOne(id),
|
||||
performHardDelete: (id) => this.prisma.vehicle.delete({ where: { id } }),
|
||||
performSoftDelete: (id) =>
|
||||
this.prisma.vehicle.update({
|
||||
where: { id },
|
||||
data: { deletedAt: new Date() },
|
||||
}),
|
||||
entityName: 'Vehicle',
|
||||
logger: this.logger,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -114,24 +94,35 @@ export class VehiclesService {
|
||||
* Get vehicle utilization statistics
|
||||
*/
|
||||
async getUtilization() {
|
||||
const vehicles = await this.findAll();
|
||||
const now = new Date();
|
||||
|
||||
const stats = vehicles.map((vehicle) => {
|
||||
const upcomingEvents = vehicle.events.filter(
|
||||
(event) => new Date(event.startTime) > new Date(),
|
||||
);
|
||||
|
||||
return {
|
||||
id: vehicle.id,
|
||||
name: vehicle.name,
|
||||
type: vehicle.type,
|
||||
seatCapacity: vehicle.seatCapacity,
|
||||
status: vehicle.status,
|
||||
upcomingTrips: upcomingEvents.length,
|
||||
currentDriver: vehicle.currentDriver?.name,
|
||||
};
|
||||
// Fetch vehicles with only upcoming events (filtered at database level)
|
||||
const vehicles = await this.prisma.vehicle.findMany({
|
||||
where: { deletedAt: null },
|
||||
include: {
|
||||
currentDriver: true,
|
||||
events: {
|
||||
where: {
|
||||
deletedAt: null,
|
||||
startTime: { gt: now }, // Only fetch upcoming events
|
||||
},
|
||||
include: { driver: true, vehicle: true },
|
||||
orderBy: { startTime: 'asc' },
|
||||
},
|
||||
},
|
||||
orderBy: { name: 'asc' },
|
||||
});
|
||||
|
||||
const stats = vehicles.map((vehicle) => ({
|
||||
id: vehicle.id,
|
||||
name: vehicle.name,
|
||||
type: vehicle.type,
|
||||
seatCapacity: vehicle.seatCapacity,
|
||||
status: vehicle.status,
|
||||
upcomingTrips: vehicle.events.length, // Already filtered at DB level
|
||||
currentDriver: vehicle.currentDriver?.name,
|
||||
}));
|
||||
|
||||
return {
|
||||
totalVehicles: vehicles.length,
|
||||
available: vehicles.filter((v) => v.status === 'AVAILABLE').length,
|
||||
|
||||
Reference in New Issue
Block a user