From 4f1fc6c43950a094b0706abe142ceb4885678eb0 Mon Sep 17 00:00:00 2001 From: Stephanos A Date: Wed, 1 Jul 2026 13:31:31 +0300 Subject: [PATCH] Route stops distance and other fixes --- .../src/modules/schedules/routes.dto.ts | 1 + .../src/modules/schedules/routes.service.ts | 20 ++++- .../backoffice/src/app/boarding/page.tsx | 70 +++------------ .../backoffice/src/app/dashboard/page.tsx | 88 +------------------ .../backoffice/src/app/routes/page.tsx | 36 ++++---- 5 files changed, 52 insertions(+), 163 deletions(-) diff --git a/apps/edr-passenger-api/src/modules/schedules/routes.dto.ts b/apps/edr-passenger-api/src/modules/schedules/routes.dto.ts index 8339bf477..bce25fea5 100644 --- a/apps/edr-passenger-api/src/modules/schedules/routes.dto.ts +++ b/apps/edr-passenger-api/src/modules/schedules/routes.dto.ts @@ -42,4 +42,5 @@ export class UpdateRouteDto { @ApiPropertyOptional() @IsOptional() @IsString() description?: string; @ApiPropertyOptional({ example: true }) @IsOptional() @IsBoolean() active?: boolean; @ApiPropertyOptional({ example: '2027-12-31T23:59:59Z' }) @IsOptional() @IsDateString() effectiveUntil?: string; + @ApiPropertyOptional({ type: [RouteStopInputDto] }) @IsOptional() @IsArray() @ValidateNested({ each: true }) @Type(() => RouteStopInputDto) stops?: RouteStopInputDto[]; } diff --git a/apps/edr-passenger-api/src/modules/schedules/routes.service.ts b/apps/edr-passenger-api/src/modules/schedules/routes.service.ts index d8df838ca..2a7254f37 100644 --- a/apps/edr-passenger-api/src/modules/schedules/routes.service.ts +++ b/apps/edr-passenger-api/src/modules/schedules/routes.service.ts @@ -81,7 +81,8 @@ export class RoutesService { async updateRoute(id: string, dto: UpdateRouteDto) { const route = await this.prisma.route.findUnique({ where: { id } }); if (!route) throw new NotFoundException('Route not found'); - return this.prisma.route.update({ + + await this.prisma.route.update({ where: { id }, data: { name: dto.name, @@ -89,6 +90,22 @@ export class RoutesService { active: dto.active, effectiveUntil: dto.effectiveUntil ? new Date(dto.effectiveUntil) : undefined, }, + }); + + if (dto.stops && dto.stops.length >= 2) { + await this.prisma.routeStop.deleteMany({ where: { routeId: id } }); + await this.prisma.routeStop.createMany({ + data: dto.stops.map(s => ({ + routeId: id, + stationId: s.stationId, + sequence: s.sequence, + distanceKm: s.distanceKm != null ? parseFloat(String(s.distanceKm)) : null, + })), + }); + } + + return this.prisma.route.findUnique({ + where: { id }, include: { stops: { orderBy: { sequence: 'asc' } } }, }); } @@ -128,6 +145,7 @@ export class RoutesService { const station = await this.prisma.station.findUnique({ where: { id: dto.stationId } }); if (!station) throw new NotFoundException(`Station ${dto.stationId} not found`); + if (!station.isOperational) throw new BadRequestException(`Station ${dto.stationId} is not operational`); const existing = await this.prisma.routeStop.findUnique({ where: { routeId_sequence: { routeId, sequence: dto.sequence } }, diff --git a/apps/edr-passenger-web/backoffice/src/app/boarding/page.tsx b/apps/edr-passenger-web/backoffice/src/app/boarding/page.tsx index fd55a9c88..db5f362dc 100644 --- a/apps/edr-passenger-web/backoffice/src/app/boarding/page.tsx +++ b/apps/edr-passenger-web/backoffice/src/app/boarding/page.tsx @@ -24,8 +24,6 @@ function QRScanner({ onScan, onError }: { onScan: (data: string) => void; onErro setIsInitializing(true); setCameraError(null); - console.log('Starting camera...'); - // Check if mediaDevices is supported if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { const errorMsg = 'Camera not supported in this browser. Please use a modern browser like Chrome, Firefox, or Safari.'; @@ -45,7 +43,6 @@ function QRScanner({ onScan, onError }: { onScan: (data: string) => void; onErro let mediaStream: MediaStream | null = null; try { - console.log('Requesting back camera...'); // Try with environment (back) camera first mediaStream = await navigator.mediaDevices.getUserMedia({ video: { @@ -55,18 +52,14 @@ function QRScanner({ onScan, onError }: { onScan: (data: string) => void; onErro }, audio: false }); - console.log('Back camera acquired'); - } catch (err) { - console.warn('Back camera not available, trying default camera:', err); + } catch { // Fallback to any available camera with simple constraints try { mediaStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false }); - console.log('Default camera acquired'); } catch (fallbackErr) { - console.error('All camera attempts failed:', fallbackErr); throw fallbackErr; } } @@ -79,7 +72,6 @@ function QRScanner({ onScan, onError }: { onScan: (data: string) => void; onErro throw new Error('Video element not found'); } - console.log('Setting video source...'); const video = videoRef.current; video.srcObject = mediaStream; @@ -98,29 +90,16 @@ function QRScanner({ onScan, onError }: { onScan: (data: string) => void; onErro if (!resolved) { resolved = true; cleanup(); - console.log('Video ready!'); resolve(); } }; - const onLoadedMetadata = () => { - console.log('Metadata loaded'); - finishResolve(); - }; + const onLoadedMetadata = () => finishResolve(); + const onLoadedData = () => finishResolve(); + const onCanPlay = () => finishResolve(); - const onLoadedData = () => { - console.log('Data loaded'); - finishResolve(); - }; - - const onCanPlay = () => { - console.log('Can play'); - finishResolve(); - }; - - const onVideoError = (e: Event) => { + const onVideoError = (_e: Event) => { cleanup(); - console.error('Video error:', e); reject(new Error('Video failed to load')); }; @@ -130,40 +109,22 @@ function QRScanner({ onScan, onError }: { onScan: (data: string) => void; onErro video.addEventListener('canplay', onCanPlay); video.addEventListener('error', onVideoError); - // Fallback timeout - but shorter since we have multiple events - setTimeout(() => { - console.log('Video load timeout, proceeding anyway'); - finishResolve(); - }, 2000); + setTimeout(() => finishResolve(), 2000); }); - // Play the video - console.log('Playing video...'); try { await video.play(); - console.log('Video playing'); - } catch (playError) { - console.warn('Play attempt 1 failed, retrying:', playError); - // Retry play after a short delay + } catch { await new Promise(resolve => setTimeout(resolve, 100)); - try { - await video.play(); - console.log('Video playing (retry succeeded)'); - } catch (retryError) { - console.warn('Play retry also failed (continuing anyway):', retryError); - } + try { await video.play(); } catch { /* continue */ } } // Set state to show video setStream(mediaStream); setIsScanning(true); setIsInitializing(false); - console.log('Camera started successfully'); - console.log('isScanning state set to:', true); - console.log('isInitializing state set to:', false); } catch (error: any) { - console.error('Camera start error:', error); let errorMsg = 'Camera access failed. Please check permissions and try again.'; @@ -224,21 +185,17 @@ function QRScanner({ onScan, onError }: { onScan: (data: string) => void; onErro const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); try { - // Try to use jsqr if available const jsQR = (window as any).jsQR; if (jsQR) { const code = jsQR(imageData.data, imageData.width, imageData.height, { inversionAttempts: 'dontInvert', }); - if (code) { onScan(code.data); stopCamera(); } } - } catch (err) { - console.error('QR scan error:', err); - } + } catch { /* ignore scan errors */ } } }, [isScanning, onScan, stopCamera]); @@ -269,11 +226,6 @@ function QRScanner({ onScan, onError }: { onScan: (data: string) => void; onErro return (
- {/* Debug info */} -
- Debug: isScanning={String(isScanning)}, isInitializing={String(isInitializing)}, stream={stream ? 'active' : 'null'} -
- {/* Video viewer - always rendered, visibility controlled by display style */}
@@ -337,9 +289,7 @@ function QRScanner({ onScan, onError }: { onScan: (data: string) => void; onErro

Please allow camera access when prompted by your browser

-

- Check console (F12) for detailed camera logs if this takes too long -

+
- +