refactor: complete code efficiency pass (Issues #10, #14, #16)

Backend:
- Add Prisma soft-delete middleware for automatic deletedAt filtering (#10)
- Split 2758-line copilot.service.ts into focused sub-services (#14):
  - copilot-schedule.service.ts (schedule/event tools)
  - copilot-reports.service.ts (reporting/analytics tools)
  - copilot-fleet.service.ts (vehicle/driver tools)
  - copilot-vip.service.ts (VIP management tools)
  - copilot.service.ts now thin orchestrator
- Remove manual deletedAt: null from 50+ queries

Frontend:
- Create SortableHeader component for reusable table sorting (#16)
- Create useListPage hook for shared search/filter/sort state (#16)
- Update VipList, DriverList, EventList to use shared infrastructure

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 16:34:18 +01:00
parent f2b3f34a72
commit 3bc9cd0bca
23 changed files with 2975 additions and 2443 deletions

View File

@@ -86,7 +86,6 @@ export class EventStatusService implements OnModuleInit, OnModuleDestroy {
startTime: { lte: twentyMinutesFromNow, gt: now },
reminder20MinSent: false,
driverId: { not: null },
deletedAt: null,
},
include: {
driver: true,
@@ -110,7 +109,6 @@ export class EventStatusService implements OnModuleInit, OnModuleDestroy {
startTime: { lte: fiveMinutesFromNow, gt: now },
reminder5MinSent: false,
driverId: { not: null },
deletedAt: null,
},
include: {
driver: true,
@@ -218,7 +216,6 @@ Reply:
where: {
status: EventStatus.SCHEDULED,
startTime: { lte: now },
deletedAt: null,
},
include: {
driver: true,
@@ -264,7 +261,6 @@ Reply:
where: {
status: EventStatus.IN_PROGRESS,
endTime: { lte: gracePeriodAgo },
deletedAt: null,
},
include: {
driver: true,
@@ -347,7 +343,6 @@ Reply with 1, 2, or 3`;
const driver = await this.prisma.driver.findFirst({
where: {
phone: { contains: driverPhone.replace(/\D/g, '').slice(-10) },
deletedAt: null,
},
});
@@ -360,7 +355,6 @@ Reply with 1, 2, or 3`;
where: {
driverId: driver.id,
status: EventStatus.IN_PROGRESS,
deletedAt: null,
},
include: { vehicle: true },
});

View File

@@ -20,7 +20,6 @@ export class EventsService {
select: { id: true, title: true, type: true, startTime: true, endTime: true },
},
childEvents: {
where: { deletedAt: null },
select: { id: true, title: true, type: true },
},
} as const;
@@ -35,7 +34,6 @@ export class EventsService {
const vips = await this.prisma.vIP.findMany({
where: {
id: { in: createEventDto.vipIds },
deletedAt: null,
},
});
@@ -90,7 +88,6 @@ export class EventsService {
async findAll() {
const events = await this.prisma.scheduleEvent.findMany({
where: { deletedAt: null },
include: this.eventInclude,
orderBy: { startTime: 'asc' },
});
@@ -107,7 +104,6 @@ export class EventsService {
const vips = await this.prisma.vIP.findMany({
where: {
id: { in: Array.from(allVipIds) },
deletedAt: null,
},
});
vips.forEach((vip) => vipsMap.set(vip.id, vip));
@@ -129,7 +125,7 @@ export class EventsService {
async findOne(id: string) {
const event = await this.prisma.scheduleEvent.findFirst({
where: { id, deletedAt: null },
where: { id },
include: this.eventInclude,
});
@@ -148,7 +144,6 @@ export class EventsService {
const vips = await this.prisma.vIP.findMany({
where: {
id: { in: updateEventDto.vipIds },
deletedAt: null,
},
});
@@ -264,7 +259,7 @@ export class EventsService {
*/
private async checkVehicleCapacity(vehicleId: string, vipIds: string[]) {
const vehicle = await this.prisma.vehicle.findFirst({
where: { id: vehicleId, deletedAt: null },
where: { id: vehicleId },
});
if (!vehicle) {
@@ -272,7 +267,7 @@ export class EventsService {
}
const vips = await this.prisma.vIP.findMany({
where: { id: { in: vipIds }, deletedAt: null },
where: { id: { in: vipIds } },
select: { partySize: true },
});
const totalPeople = vips.reduce((sum, v) => sum + v.partySize, 0);
@@ -302,7 +297,6 @@ export class EventsService {
return this.prisma.scheduleEvent.findMany({
where: {
driverId,
deletedAt: null,
id: excludeEventId ? { not: excludeEventId } : undefined,
OR: [
{
@@ -343,7 +337,6 @@ export class EventsService {
const vips = await this.prisma.vIP.findMany({
where: {
id: { in: event.vipIds },
deletedAt: null,
},
});