From fc684a0f65c139129434ac61558a3d000f77220e Mon Sep 17 00:00:00 2001 From: Abubeker Yasin Date: Tue, 14 Jul 2026 20:04:42 +0300 Subject: [PATCH 1/2] feat: ( permissions ) add permssions for master data --- .../src/modules/fleet/fleet.controller.ts | 15 ++- .../modules/packages/packages.controller.ts | 24 ++-- .../modules/payments/payments.controller.ts | 15 ++- .../modules/schedules/routes.controller.ts | 14 +-- .../modules/schedules/schedules.controller.ts | 29 ++--- .../seat-classes/seat-classes.controller.ts | 10 +- .../src/modules/seats/seats.controller.ts | 15 +-- .../modules/stations/stations.controller.ts | 14 +-- .../src/seed/edr-passenger.seed.ts | 15 +++ .../seed/passenger-permissions.registry.ts | 107 ++++++++++++++++++ .../src/app/payment-methods/page.tsx | 9 +- .../src/components/layout/Sidebar.tsx | 26 ++--- .../backoffice/src/lib/permissions.ts | 69 +++++++++-- 13 files changed, 282 insertions(+), 80 deletions(-) diff --git a/apps/edr-passenger-api/src/modules/fleet/fleet.controller.ts b/apps/edr-passenger-api/src/modules/fleet/fleet.controller.ts index d3864e2b2..ee2a048f0 100644 --- a/apps/edr-passenger-api/src/modules/fleet/fleet.controller.ts +++ b/apps/edr-passenger-api/src/modules/fleet/fleet.controller.ts @@ -3,7 +3,8 @@ import { ApiTags, ApiOperation, ApiBearerAuth, ApiParam, ApiQuery, ApiBody, ApiR import { FleetService } from './fleet.service'; import { CreateTrainDto, CreateCoachDto, UpdateCoachDto, AssignCoachDto, ListCoachesDto, CreateCoachTypeDto, UpdateCoachTypeDto, CreateClassDto, UpdateClassDto, GenerateSeatMapDto } from './fleet.dto'; import { JwtGuard } from '../../common/jwt.guard'; -import { PassengerAdmin } from '../../common/passenger-guards'; +import { PassengerAdmin, PassengerStaff } from '../../common/passenger-guards'; +import { PASSENGER_PERMS } from '../../seed/passenger-permissions.registry'; @ApiTags('Fleet') @Controller('fleet') @@ -21,6 +22,7 @@ export class FleetController { } @Post('coach-types') + @PassengerStaff([PASSENGER_PERMS.coaches.manage, PASSENGER_PERMS.admin]) @ApiOperation({ summary: 'Create a coach type' }) @ApiBody({ type: CreateCoachTypeDto }) @ApiResponse({ status: 201, description: 'Coach type created' }) @@ -29,6 +31,7 @@ export class FleetController { } @Patch('coach-types/:id') + @PassengerStaff([PASSENGER_PERMS.coaches.manage, PASSENGER_PERMS.admin]) @ApiOperation({ summary: 'Update a coach type' }) @ApiParam({ name: 'id', description: 'Coach Type UUID' }) @ApiBody({ type: UpdateCoachTypeDto }) @@ -59,6 +62,7 @@ export class FleetController { } @Post('classes') + @PassengerStaff([PASSENGER_PERMS.classes.manage, PASSENGER_PERMS.admin]) @ApiOperation({ summary: 'Create a class' }) @ApiBody({ type: CreateClassDto }) @ApiResponse({ status: 201, description: 'Class created' }) @@ -67,6 +71,7 @@ export class FleetController { } @Patch('classes/:id') + @PassengerStaff([PASSENGER_PERMS.classes.manage, PASSENGER_PERMS.admin]) @ApiOperation({ summary: 'Update a class' }) @ApiParam({ name: 'id', description: 'Class UUID' }) @ApiBody({ type: UpdateClassDto }) @@ -98,6 +103,7 @@ export class FleetController { } @Post('seat-classes') + @PassengerStaff([PASSENGER_PERMS.classes.manage, PASSENGER_PERMS.admin]) @ApiOperation({ summary: 'Create a class (DEPRECATED - use /fleet/classes)' }) @ApiBody({ type: CreateClassDto }) @ApiResponse({ status: 201, description: 'Class created' }) @@ -106,6 +112,7 @@ export class FleetController { } @Patch('seat-classes/:id') + @PassengerStaff([PASSENGER_PERMS.classes.manage, PASSENGER_PERMS.admin]) @ApiOperation({ summary: 'Update a class (DEPRECATED - use /fleet/classes)' }) @ApiParam({ name: 'id', description: 'Class UUID' }) @ApiBody({ type: UpdateClassDto }) @@ -136,6 +143,7 @@ export class FleetController { } @Post('trains') + @PassengerStaff([PASSENGER_PERMS.trains.manage, PASSENGER_PERMS.admin]) @ApiOperation({ summary: 'Create a train service' }) @ApiBody({ type: CreateTrainDto }) @ApiResponse({ status: 201, description: 'Train created' }) @@ -144,6 +152,7 @@ export class FleetController { } @Patch('trains/:id') + @PassengerStaff([PASSENGER_PERMS.trains.manage, PASSENGER_PERMS.admin]) @ApiOperation({ summary: 'Update a train service' }) @ApiParam({ name: 'id', description: 'Train UUID' }) @ApiBody({ type: CreateTrainDto }) @@ -166,6 +175,7 @@ export class FleetController { } @Patch('trains/:id/restore') + @PassengerStaff([PASSENGER_PERMS.trains.manage, PASSENGER_PERMS.admin]) @ApiOperation({ summary: 'Restore (reactivate) a deactivated train' }) @ApiParam({ name: 'id', description: 'Train UUID' }) @ApiResponse({ status: 200, description: 'Train restored' }) @@ -268,6 +278,7 @@ export class FleetController { } @Post('coaches') + @PassengerStaff([PASSENGER_PERMS.coaches.manage, PASSENGER_PERMS.admin]) @ApiOperation({ summary: 'Create a coach with auto-generated seat numbers' }) @ApiBody({ type: CreateCoachDto }) @ApiResponse({ @@ -293,6 +304,7 @@ export class FleetController { } @Patch('coaches/:id') + @PassengerStaff([PASSENGER_PERMS.coaches.manage, PASSENGER_PERMS.admin]) @ApiOperation({ summary: 'Update coach properties' }) @ApiParam({ name: 'id', description: 'Coach UUID' }) @ApiBody({ type: UpdateCoachDto }) @@ -331,6 +343,7 @@ export class FleetController { } @Post('assignments') + @PassengerStaff([PASSENGER_PERMS.coaches.manage, PASSENGER_PERMS.admin]) @ApiOperation({ summary: 'Assign a coach to a schedule' }) @ApiBody({ type: AssignCoachDto }) @ApiResponse({ status: 201, description: 'Coach assigned' }) diff --git a/apps/edr-passenger-api/src/modules/packages/packages.controller.ts b/apps/edr-passenger-api/src/modules/packages/packages.controller.ts index 314f8aeb4..79f04f9d9 100644 --- a/apps/edr-passenger-api/src/modules/packages/packages.controller.ts +++ b/apps/edr-passenger-api/src/modules/packages/packages.controller.ts @@ -3,10 +3,10 @@ import { ApiTags, ApiOperation, ApiBearerAuth, ApiQuery } from '@nestjs/swagger' import { IsPublic } from '@tria-plc/api-common/modules/auth/decorators/public.decorator'; import { PackagesService } from './packages.service'; import { CreatePackageDto, BookPackageDto, CreatePriceTierDto, UpdatePriceTierDto, CreateInquiryDto, UpdateInquiryStatusDto, PackageBookingContextDto } from './packages.dto'; -import { IamGuard } from '../../common/iam-adapter'; import { JwtGuard } from '../../common/jwt.guard'; import { OptionalJwtGuard } from '../verifayda/optional-jwt.guard'; -import { PassengerAdmin } from '../../common/passenger-guards'; +import { PassengerAdmin, PassengerStaff } from '../../common/passenger-guards'; +import { PASSENGER_PERMS } from '../../seed/passenger-permissions.registry'; @ApiTags('Packages') @Controller('packages') @@ -21,7 +21,7 @@ export class PackagesController { } @Get('inquiries') - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.inquiries.view, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'List all inquiries (backoffice)' }) listInquiries( @@ -34,7 +34,7 @@ export class PackagesController { } @Patch('inquiries/:id/status') - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.inquiries.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Update inquiry status (backoffice)' }) updateInquiryStatus(@Param('id') id: string, @Body() dto: UpdateInquiryStatusDto) { @@ -57,7 +57,7 @@ export class PackagesController { } @Get('all') - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.packages.view, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'List all packages (backoffice)' }) listAll(@Query('page') page?: string, @Query('pageSize') pageSize?: string) { @@ -65,7 +65,7 @@ export class PackagesController { } @Get('bookings') - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.packages.view, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'List all package bookings (backoffice)' }) listBookings( @@ -124,7 +124,7 @@ export class PackagesController { } @Post() - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.packages.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Create package (admin)' }) create(@Body() dto: CreatePackageDto) { @@ -132,7 +132,7 @@ export class PackagesController { } @Patch(':id') - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.packages.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Update package (admin)' }) update(@Param('id') id: string, @Body() dto: Partial) { @@ -149,7 +149,7 @@ export class PackagesController { } @Patch(':id/activate') - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.packages.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Activate package (admin)' }) activate(@Param('id') id: string) { @@ -157,7 +157,7 @@ export class PackagesController { } @Patch(':id/deactivate') - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.packages.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Deactivate package (admin)' }) deactivate(@Param('id') id: string) { @@ -165,7 +165,7 @@ export class PackagesController { } @Post(':id/tiers') - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.packages.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Add price tier to package (admin)' }) addTier(@Param('id') id: string, @Body() dto: CreatePriceTierDto) { @@ -173,7 +173,7 @@ export class PackagesController { } @Patch('tiers/:tierId') - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.packages.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Update price tier (admin)' }) updateTier(@Param('tierId') tierId: string, @Body() dto: UpdatePriceTierDto) { diff --git a/apps/edr-passenger-api/src/modules/payments/payments.controller.ts b/apps/edr-passenger-api/src/modules/payments/payments.controller.ts index 8917c75d3..f917a9590 100644 --- a/apps/edr-passenger-api/src/modules/payments/payments.controller.ts +++ b/apps/edr-passenger-api/src/modules/payments/payments.controller.ts @@ -55,7 +55,11 @@ export class PaymentsController { } @Get("all") - @PassengerStaff([PASSENGER_PERMS.payments.viewAll, PASSENGER_PERMS.admin]) + @PassengerStaff([ + PASSENGER_PERMS.payments.view, + PASSENGER_PERMS.payments.viewAll, + PASSENGER_PERMS.admin, + ]) @ApiBearerAuth("IAM-auth") @ApiOperation({ summary: "Get all payments with filters (staff/admin only)" }) @ApiQuery({ name: "search", required: false }) @@ -146,7 +150,11 @@ export class PaymentsController { } @Post("refund") - @PassengerStaff([PASSENGER_PERMS.payments.refund, PASSENGER_PERMS.admin]) + @PassengerStaff([ + PASSENGER_PERMS.payments.manage, + PASSENGER_PERMS.payments.refund, + PASSENGER_PERMS.admin, + ]) @ApiBearerAuth("IAM-auth") @ApiOperation({ summary: "Refund a confirmed booking (staff/agent only)" }) refund(@Body() dto: RefundDto) { @@ -155,6 +163,7 @@ export class PaymentsController { @Post(":bookingId/force-confirm") @PassengerStaff([ + PASSENGER_PERMS.payments.manage, PASSENGER_PERMS.payments.manageMethods, PASSENGER_PERMS.admin, ]) @@ -174,6 +183,7 @@ export class PaymentsController { @Post("methods") @PassengerStaff([ + PASSENGER_PERMS.paymentMethods.manage, PASSENGER_PERMS.payments.manageMethods, PASSENGER_PERMS.admin, ]) @@ -187,6 +197,7 @@ export class PaymentsController { @Patch("methods/:id") @PassengerStaff([ + PASSENGER_PERMS.paymentMethods.manage, PASSENGER_PERMS.payments.manageMethods, PASSENGER_PERMS.admin, ]) diff --git a/apps/edr-passenger-api/src/modules/schedules/routes.controller.ts b/apps/edr-passenger-api/src/modules/schedules/routes.controller.ts index e751278ce..d468bba7c 100644 --- a/apps/edr-passenger-api/src/modules/schedules/routes.controller.ts +++ b/apps/edr-passenger-api/src/modules/schedules/routes.controller.ts @@ -1,9 +1,9 @@ -import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, ParseIntPipe, UseGuards } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, ParseIntPipe } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiBearerAuth, ApiParam, ApiQuery, ApiResponse } from '@nestjs/swagger'; import { RoutesService } from './routes.service'; import { CreateRouteDto, AddRouteStopDto, UpdateRouteDto, SetRouteCoachTemplateDto } from './routes.dto'; -import { JwtGuard } from '../../common/jwt.guard'; -import { PassengerAdmin } from '../../common/passenger-guards'; +import { PassengerAdmin, PassengerStaff } from '../../common/passenger-guards'; +import { PASSENGER_PERMS } from '../../seed/passenger-permissions.registry'; @ApiTags('Routes') @Controller('routes') @@ -13,7 +13,7 @@ export class RoutesController { // ── Routes ───────────────────────────────────────────────────────────────── @Post() - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.routes.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Create a reusable route with its ordered stops', description: `Define the physical corridor once (e.g. ADD→ADM→AWS→DDW→AYS→DJI). @@ -41,7 +41,7 @@ Route stops carry distanceKm for fare-by-distance calculations.`, getRoute(@Param('id') id: string) { return this.service.getRoute(id); } @Patch(':id') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.routes.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Update route metadata (name, description, active flag, effectiveUntil)' }) @ApiParam({ name: 'id', description: 'Route UUID' }) @ApiResponse({ status: 200, description: 'Route updated' }) @@ -68,7 +68,7 @@ Route stops carry distanceKm for fare-by-distance calculations.`, getStops(@Param('id') id: string) { return this.service.getStops(id); } @Post(':id/stops') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.routes.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Add a stop to an existing route' }) @ApiParam({ name: 'id', description: 'Route UUID' }) @ApiResponse({ status: 201, description: 'Stop added' }) @@ -108,7 +108,7 @@ Route stops carry distanceKm for fare-by-distance calculations.`, getCoachTemplate(@Param('id') id: string) { return this.service.getRouteCoachTemplate(id); } @Put(':id/coaches') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.routes.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Set the default coach lineup for this route', description: 'Replaces the entire coach template. Coaches are auto-assigned in this order when a new schedule is created for this route.', diff --git a/apps/edr-passenger-api/src/modules/schedules/schedules.controller.ts b/apps/edr-passenger-api/src/modules/schedules/schedules.controller.ts index 9fc2ba851..a2d62d5aa 100644 --- a/apps/edr-passenger-api/src/modules/schedules/schedules.controller.ts +++ b/apps/edr-passenger-api/src/modules/schedules/schedules.controller.ts @@ -1,10 +1,10 @@ -import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, ParseIntPipe, UseGuards } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, ParseIntPipe } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiBearerAuth, ApiParam, ApiQuery, ApiResponse } from '@nestjs/swagger'; import { IsPublic } from '@tria-plc/api-common/modules/auth/decorators/public.decorator'; import { SchedulesService } from './schedules.service'; import { CreateScheduleDto, UpdateScheduleDto, CreateFareRuleDto, UpdateScheduleStatusDto, UpdateStopTimeDto, ListSchedulesDto, BulkCreateSchedulesDto, BulkSchedulesResponseDto, TripStatus } from './schedules.dto'; -import { JwtGuard } from '../../common/jwt.guard'; -import { PassengerAdmin } from '../../common/passenger-guards'; +import { PassengerAdmin, PassengerStaff } from '../../common/passenger-guards'; +import { PASSENGER_PERMS } from '../../seed/passenger-permissions.registry'; @ApiTags('Schedule') @Controller('schedules') @@ -12,14 +12,14 @@ export class SchedulesController { constructor(private service: SchedulesService) {} @Post('bulk-generate') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.schedules.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Bulk generate repetitive schedules' }) bulkGenerateSchedules(@Body() dto: BulkCreateSchedulesDto) { return this.service.bulkGenerateSchedules(dto); } @Post() - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.schedules.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Create a train schedule from a route template' }) createSchedule(@Body() dto: CreateScheduleDto) { return this.service.createSchedule(dto); } @@ -42,13 +42,13 @@ export class SchedulesController { // ===== SPECIFIC ROUTES (must come BEFORE generic :id routes) ===== @Post('fares') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.schedules.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Create a fare rule scoped to a schedule or route code' }) @ApiResponse({ status: 201, description: 'Fare rule created' }) createFareRule(@Body() dto: CreateFareRuleDto) { return this.service.createFareRule(dto); } @Patch('fares/:id') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.schedules.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Update a fare rule' }) @ApiParam({ name: 'id', description: 'FareRule UUID' }) @ApiResponse({ status: 200, description: 'Fare rule updated' }) @@ -65,7 +65,7 @@ export class SchedulesController { deleteFareRule(@Param('id') id: string) { return this.service.deleteFareRule(id); } @Post('segment-fares') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.schedules.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Create a segment fare rule' }) createSegmentFareRule(@Body() dto: any) { return this.service.createSegmentFareRule(dto); } @@ -76,7 +76,7 @@ export class SchedulesController { getSegmentFares(@Param('routeId') routeId: string) { return this.service.getSegmentFares(routeId); } @Patch('segment-fares/:id') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.schedules.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Update a segment fare rule' }) @ApiParam({ name: 'id', description: 'SegmentFareRule UUID' }) updateSegmentFareRule(@Param('id') id: string, @Body() dto: any) { return this.service.updateSegmentFareRule(id, dto); } @@ -97,7 +97,7 @@ export class SchedulesController { getSchedule(@Param('id') id: string) { return this.service.getSchedule(id); } @Patch(':id') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.schedules.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Update a schedule (partial)' }) @ApiParam({ name: 'id', description: 'TrainSchedule UUID' }) updateSchedule(@Param('id') id: string, @Body() dto: UpdateScheduleDto) { @@ -105,7 +105,7 @@ export class SchedulesController { } @Patch(':id/status') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.schedules.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Update schedule status' }) @ApiParam({ name: 'id', description: 'TrainSchedule UUID' }) updateStatus(@Param('id') id: string, @Body() dto: UpdateScheduleStatusDto) { @@ -127,7 +127,7 @@ export class SchedulesController { getStops(@Param('id') id: string) { return this.service.getStops(id); } @Patch(':id/stops/:sequence') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.schedules.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Update a stop time' }) @ApiParam({ name: 'id', description: 'TrainSchedule UUID' }) @ApiParam({ name: 'sequence', description: 'Stop sequence number' }) @@ -138,7 +138,7 @@ export class SchedulesController { ) { return this.service.updateStop(id, sequence, dto); } @Put(':scheduleId/fares/:seatClassId') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.schedules.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Override fare for a specific seat class on a schedule', description: 'Upserts a schedule-scoped FareRule. Expires any existing active rule for the same schedule+seatClass and creates a new one.', @@ -186,12 +186,13 @@ export class SchedulesController { } @Post(':id/fares/sync') + @PassengerStaff([PASSENGER_PERMS.schedules.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Sync fares from fare engine' }) @ApiParam({ name: 'id', description: 'TrainSchedule UUID' }) syncFares(@Param('id') id: string) { return this.service.syncFaresFromEngine(id); } @Post(':id/coaches') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.schedules.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Assign coaches to a schedule' }) @ApiParam({ name: 'id', description: 'TrainSchedule UUID' }) assignCoaches( diff --git a/apps/edr-passenger-api/src/modules/seat-classes/seat-classes.controller.ts b/apps/edr-passenger-api/src/modules/seat-classes/seat-classes.controller.ts index 4eb6c212b..453256d7e 100644 --- a/apps/edr-passenger-api/src/modules/seat-classes/seat-classes.controller.ts +++ b/apps/edr-passenger-api/src/modules/seat-classes/seat-classes.controller.ts @@ -1,10 +1,10 @@ -import { Body, Controller, Delete, Get, Param, Patch, Post, UseGuards } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiBearerAuth, ApiParam, ApiResponse, ApiBody } from '@nestjs/swagger'; import { IsPublic } from '@tria-plc/api-common/modules/auth/decorators/public.decorator'; import { SeatClassesService } from './seat-classes.service'; import { CreateSeatClassDto, UpdateSeatClassDto } from './seat-classes.dto'; -import { JwtGuard } from '../../common/jwt.guard'; -import { PassengerAdmin } from '../../common/passenger-guards'; +import { PassengerAdmin, PassengerStaff } from '../../common/passenger-guards'; +import { PASSENGER_PERMS } from '../../seed/passenger-permissions.registry'; @ApiTags('Seat Classes') @Controller('seat-classes') @@ -26,7 +26,7 @@ export class SeatClassesController { getSeatClass(@Param('id') id: string) { return this.service.getSeatClass(id); } @Post() - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.tariffRates.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Create a seat class' }) @ApiBody({ type: CreateSeatClassDto }) @ApiResponse({ status: 201, description: 'Seat class created' }) @@ -34,7 +34,7 @@ export class SeatClassesController { createSeatClass(@Body() dto: CreateSeatClassDto) { return this.service.createSeatClass(dto); } @Patch(':id') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.tariffRates.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Update a seat class' }) @ApiParam({ name: 'id', description: 'Seat class UUID' }) @ApiBody({ type: UpdateSeatClassDto }) diff --git a/apps/edr-passenger-api/src/modules/seats/seats.controller.ts b/apps/edr-passenger-api/src/modules/seats/seats.controller.ts index fa233f45c..752b66390 100644 --- a/apps/edr-passenger-api/src/modules/seats/seats.controller.ts +++ b/apps/edr-passenger-api/src/modules/seats/seats.controller.ts @@ -21,7 +21,8 @@ import { import { SeatsService } from "./seats.service"; import { HoldSeatsDto, ReleaseHoldDto } from "./seats.dto"; import { JwtGuard } from "../../common/jwt.guard"; -import { IamGuard } from "../../common/iam-adapter"; +import { PassengerStaff } from "../../common/passenger-guards"; +import { PASSENGER_PERMS } from "../../seed/passenger-permissions.registry"; @ApiTags("Seats") @Controller("seats") @@ -205,7 +206,7 @@ This makes it clear which segment of the route each seat is held for, enabling s // ── Seat Block / Unblock ─────────────────────────────────────────────────── @Post(":seatId/block") - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.seats.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth("IAM-auth") @ApiOperation({ summary: "Block a seat (e.g., maintenance, damage)" }) @ApiParam({ name: "seatId", description: "Seat UUID" }) @@ -215,7 +216,7 @@ This makes it clear which segment of the route each seat is held for, enabling s } @Delete(":seatId/block") - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.seats.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth("IAM-auth") @ApiOperation({ summary: "Unblock a seat" }) @ApiParam({ name: "seatId", description: "Seat UUID" }) @@ -226,7 +227,7 @@ This makes it clear which segment of the route each seat is held for, enabling s // ── Maintenance ─────────────────────────────────────────────────────────── @Post(":seatId/maintenance") - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.seats.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth("IAM-auth") @ApiOperation({ summary: "Set seat status to Under Maintenance" }) @ApiParam({ name: "seatId", description: "Seat UUID" }) @@ -236,7 +237,7 @@ This makes it clear which segment of the route each seat is held for, enabling s } @Delete(":seatId/maintenance") - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.seats.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth("IAM-auth") @ApiOperation({ summary: "Clear seat maintenance status" }) @ApiParam({ name: "seatId", description: "Seat UUID" }) @@ -247,7 +248,7 @@ This makes it clear which segment of the route each seat is held for, enabling s // ── Remove Seat ──────────────────────────────────────────────────────────── @Patch(":seatId/remove") - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.seats.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth("IAM-auth") @ApiOperation({ summary: "Remove a seat by marking with negative seatNumber", @@ -263,7 +264,7 @@ This makes it clear which segment of the route each seat is held for, enabling s } @Patch(":seatId/undo-remove") - @UseGuards(IamGuard) + @PassengerStaff([PASSENGER_PERMS.seats.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth("IAM-auth") @ApiOperation({ summary: "Undo seat removal by restoring original seatNumber", diff --git a/apps/edr-passenger-api/src/modules/stations/stations.controller.ts b/apps/edr-passenger-api/src/modules/stations/stations.controller.ts index 0f367043b..5ddd64383 100644 --- a/apps/edr-passenger-api/src/modules/stations/stations.controller.ts +++ b/apps/edr-passenger-api/src/modules/stations/stations.controller.ts @@ -1,10 +1,10 @@ -import { Body, Controller, Get, Param, Post, Patch, Delete, UseGuards, Query } from '@nestjs/common'; +import { Body, Controller, Get, Param, Post, Patch, Delete, Query } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiBearerAuth, ApiQuery, ApiResponse } from '@nestjs/swagger'; import { IsPublic } from '@tria-plc/api-common/modules/auth/decorators/public.decorator'; import { StationsService } from './stations.service'; import { CreateStationDto } from './stations.dto'; -import { JwtGuard } from '../../common/jwt.guard'; -import { PassengerAdmin } from '../../common/passenger-guards'; +import { PassengerAdmin, PassengerStaff } from '../../common/passenger-guards'; +import { PASSENGER_PERMS } from '../../seed/passenger-permissions.registry'; @ApiTags('Stations') @Controller('stations') @@ -79,8 +79,8 @@ export class StationsController { findOne(@Param('id') id: string) { return this.service.findOne(id); } @Post() - @UseGuards(JwtGuard) - @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.stations.manage, PASSENGER_PERMS.admin]) + @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Create new station' }) @ApiResponse({ status: 201, @@ -105,8 +105,8 @@ export class StationsController { create(@Body() dto: CreateStationDto) { return this.service.create(dto); } @Patch(':id') - @UseGuards(JwtGuard) - @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.stations.manage, PASSENGER_PERMS.admin]) + @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Update station' }) @ApiResponse({ status: 200, diff --git a/apps/edr-passenger-api/src/seed/edr-passenger.seed.ts b/apps/edr-passenger-api/src/seed/edr-passenger.seed.ts index 578cf66ff..4313d5426 100644 --- a/apps/edr-passenger-api/src/seed/edr-passenger.seed.ts +++ b/apps/edr-passenger-api/src/seed/edr-passenger.seed.ts @@ -54,4 +54,19 @@ export const EDR_PASSENGER_ROLES: PassengerSeedRole[] = [ name: { en: 'EDR Passenger Finance' }, permissionKeys: [...ROLE_PERMISSION_PRESETS.finance], }, + { + key: 'edr_passenger_operations_manager', + name: { en: 'EDR Passenger Operations Manager' }, + permissionKeys: [...ROLE_PERMISSION_PRESETS.operationsManager], + }, + { + key: 'edr_passenger_marketing_manager', + name: { en: 'EDR Passenger Marketing Manager' }, + permissionKeys: [...ROLE_PERMISSION_PRESETS.marketingManager], + }, + { + key: 'edr_passenger_finance_manager', + name: { en: 'EDR Passenger Finance Manager' }, + permissionKeys: [...ROLE_PERMISSION_PRESETS.financeManager], + }, ]; diff --git a/apps/edr-passenger-api/src/seed/passenger-permissions.registry.ts b/apps/edr-passenger-api/src/seed/passenger-permissions.registry.ts index bab7787b9..d62d5a261 100644 --- a/apps/edr-passenger-api/src/seed/passenger-permissions.registry.ts +++ b/apps/edr-passenger-api/src/seed/passenger-permissions.registry.ts @@ -34,6 +34,38 @@ export const PASSENGER_PERMISSIONS: PassengerPermissionSeed[] = [ perm('75b5ff62-a8e4-4331-b6e6-d53e1456d10e', 'edr_passenger_app:currencies:manage', 'Manage currencies'), perm('4a47da9b-cf6e-4240-aff8-aadf01641c54', 'edr_passenger_app:notifications:send', 'Send notifications'), perm('bfe3428f-8b85-4a36-87c6-33063b084bf3', 'edr_passenger_app:dashboard:view', 'View dashboard'), + + // ── Master Data ──────────────────────────────────────────────────────────── + perm('102969bc-13a2-4f4a-aa7f-ce4b2599ce82', 'edr_passenger_app:stations:view', 'View stations'), + perm('931cf6fc-8f41-46b6-82b8-b242f75d296e', 'edr_passenger_app:stations:manage', 'Manage stations'), + perm('82a801b3-2451-409b-99e8-9f4d3515d329', 'edr_passenger_app:trains:view', 'View trains'), + perm('e2a7f842-7691-4007-bd71-fd91c467044d', 'edr_passenger_app:trains:manage', 'Manage trains'), + perm('71de593c-ae4a-4d15-9f0a-b889eeb4910c', 'edr_passenger_app:coaches:view', 'View coaches'), + perm('39b3de2e-c779-4010-8ef3-d478f00a15c6', 'edr_passenger_app:coaches:manage', 'Manage coaches'), + perm('ba0fdd0b-6580-48a8-b31f-eb5ef76a2453', 'edr_passenger_app:seats:view', 'View seats'), + perm('6fb9affb-1885-446e-a8e4-962af9fae33f', 'edr_passenger_app:seats:manage', 'Manage seats'), + perm('b7b659d9-f453-41d3-a6db-72e671446214', 'edr_passenger_app:classes:view', 'View classes'), + perm('b2d06665-33f5-46d7-a895-785b5fb1896a', 'edr_passenger_app:classes:manage', 'Manage classes'), + perm('8731ee98-24c2-4f8b-9c06-cf3fa900a95a', 'edr_passenger_app:routes:view', 'View routes'), + perm('5851233c-78de-45b9-9d3f-63816d068622', 'edr_passenger_app:routes:manage', 'Manage routes'), + perm('c453bdf9-496a-4ac8-b733-8eb5dd5d591a', 'edr_passenger_app:schedules:view', 'View schedules'), + perm('d3f3cfd0-c7ce-47ab-be7f-bf3d6b40e488', 'edr_passenger_app:schedules:manage', 'Manage schedules'), + + // ── Tourism ──────────────────────────────────────────────────────────────── + perm('d78d810b-3003-4d81-92d5-41c437f3cc42', 'edr_passenger_app:packages:view', 'View packages'), + perm('dcfab0d9-1f80-4822-892a-e2851b549297', 'edr_passenger_app:packages:manage', 'Manage packages'), + perm('6d7ab68c-1b88-405d-9f92-b130055eece6', 'edr_passenger_app:inquiries:view', 'View package inquiries'), + perm('dbe5a07a-d12f-4a36-b191-0bb4f980054e', 'edr_passenger_app:inquiries:manage', 'Manage package inquiries'), + + // ── Finance ──────────────────────────────────────────────────────────────── + perm('4b6efd87-f230-4109-abe1-593e53cb0c10', 'edr_passenger_app:tariff_rates:view', 'View tariff rates'), + perm('94f17a59-397c-4c9c-a424-38a9c66c9e50', 'edr_passenger_app:tariff_rates:manage', 'Manage tariff rates'), + perm('2dc4eb75-5b28-4ead-a2d4-82ac95cd290c', 'edr_passenger_app:payments:view', 'View payments'), + perm('418b5f64-656b-4d44-a543-930ada9ec1a7', 'edr_passenger_app:payments:manage', 'Manage payments'), + perm('3f3d5479-af33-4883-867e-aae9e2aeeeca', 'edr_passenger_app:currencies:view', 'View currencies'), + perm('b4e63290-cc3a-4df8-9f2a-9ff726e86e36', 'edr_passenger_app:payment_methods:view', 'View payment methods'), + perm('f9fb6af2-e869-4e6e-938c-259643393315', 'edr_passenger_app:payment_methods:manage', 'Manage payment methods'), + perm('49fd28cd-5b58-4403-8e53-1df4b93cbbd2', 'edr_passenger_app:admin', 'Full admin access'), ]; @@ -54,10 +86,57 @@ export const PASSENGER_PERMS = { manage: 'edr_passenger_app:tickets:manage', }, payments: { + view: 'edr_passenger_app:payments:view', + manage: 'edr_passenger_app:payments:manage', + // legacy keys — retained as aliases for backward compatibility viewAll: 'edr_passenger_app:payments:view_all', refund: 'edr_passenger_app:payments:refund', manageMethods: 'edr_passenger_app:payments:manage_methods', }, + paymentMethods: { + view: 'edr_passenger_app:payment_methods:view', + manage: 'edr_passenger_app:payment_methods:manage', + }, + stations: { + view: 'edr_passenger_app:stations:view', + manage: 'edr_passenger_app:stations:manage', + }, + trains: { + view: 'edr_passenger_app:trains:view', + manage: 'edr_passenger_app:trains:manage', + }, + coaches: { + view: 'edr_passenger_app:coaches:view', + manage: 'edr_passenger_app:coaches:manage', + }, + seats: { + view: 'edr_passenger_app:seats:view', + manage: 'edr_passenger_app:seats:manage', + }, + classes: { + view: 'edr_passenger_app:classes:view', + manage: 'edr_passenger_app:classes:manage', + }, + routes: { + view: 'edr_passenger_app:routes:view', + manage: 'edr_passenger_app:routes:manage', + }, + schedules: { + view: 'edr_passenger_app:schedules:view', + manage: 'edr_passenger_app:schedules:manage', + }, + packages: { + view: 'edr_passenger_app:packages:view', + manage: 'edr_passenger_app:packages:manage', + }, + inquiries: { + view: 'edr_passenger_app:inquiries:view', + manage: 'edr_passenger_app:inquiries:manage', + }, + tariffRates: { + view: 'edr_passenger_app:tariff_rates:view', + manage: 'edr_passenger_app:tariff_rates:manage', + }, reports: { view: 'edr_passenger_app:reports:view', }, @@ -73,6 +152,7 @@ export const PASSENGER_PERMS = { manage: 'edr_passenger_app:agents:manage', }, currencies: { + view: 'edr_passenger_app:currencies:view', manage: 'edr_passenger_app:currencies:manage', }, notifications: { @@ -126,9 +206,36 @@ export const ROLE_PERMISSION_PRESETS = { ], finance: [ + PASSENGER_PERMS.payments.view, PASSENGER_PERMS.payments.viewAll, PASSENGER_PERMS.payments.refund, PASSENGER_PERMS.reports.view, PASSENGER_PERMS.dashboard.view, ], + + operationsManager: [ + PASSENGER_PERMS.stations.view, PASSENGER_PERMS.stations.manage, + PASSENGER_PERMS.trains.view, PASSENGER_PERMS.trains.manage, + PASSENGER_PERMS.coaches.view, PASSENGER_PERMS.coaches.manage, + PASSENGER_PERMS.seats.view, PASSENGER_PERMS.seats.manage, + PASSENGER_PERMS.classes.view, PASSENGER_PERMS.classes.manage, + PASSENGER_PERMS.routes.view, PASSENGER_PERMS.routes.manage, + PASSENGER_PERMS.schedules.view, PASSENGER_PERMS.schedules.manage, + PASSENGER_PERMS.dashboard.view, + ], + + marketingManager: [ + PASSENGER_PERMS.packages.view, PASSENGER_PERMS.packages.manage, + PASSENGER_PERMS.inquiries.view, PASSENGER_PERMS.inquiries.manage, + PASSENGER_PERMS.dashboard.view, + ], + + financeManager: [ + PASSENGER_PERMS.tariffRates.view, PASSENGER_PERMS.tariffRates.manage, + PASSENGER_PERMS.payments.view, PASSENGER_PERMS.payments.manage, + PASSENGER_PERMS.currencies.view, PASSENGER_PERMS.currencies.manage, + PASSENGER_PERMS.paymentMethods.view, PASSENGER_PERMS.paymentMethods.manage, + PASSENGER_PERMS.reports.view, + PASSENGER_PERMS.dashboard.view, + ], } as const; diff --git a/apps/edr-passenger-web/backoffice/src/app/payment-methods/page.tsx b/apps/edr-passenger-web/backoffice/src/app/payment-methods/page.tsx index 8937497c6..4e6a71950 100644 --- a/apps/edr-passenger-web/backoffice/src/app/payment-methods/page.tsx +++ b/apps/edr-passenger-web/backoffice/src/app/payment-methods/page.tsx @@ -9,14 +9,13 @@ import ActionButton from '@/components/ui/ActionButton'; import Modal from '@/components/ui/Modal'; import ConfirmDialog from '@/components/ui/ConfirmDialog'; import { apiClient, paymentsApi } from '@/lib/api'; -import { PermissionGuard } from '@/components/layout/PermissionGuard'; import { usePermission } from '@/lib/use-permission'; import { PERMS } from '@/lib/permissions'; export default function PaymentMethodsPage() { - const canManagePayments = usePermission(PERMS.payments.manage); + const canManageMethods = usePermission(PERMS.paymentMethods.manage); const canManageAdmin = usePermission(PERMS.admin); - const canManage = canManagePayments || canManageAdmin; + const canManage = canManageMethods || canManageAdmin; const [createModalOpen, setCreateModalOpen] = useState(false); const [editModalOpen, setEditModalOpen] = useState(false); const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false); @@ -228,11 +227,11 @@ export default function PaymentMethodsPage() {

Payment Methods

Manage supported payment systems

- + {canManage && ( setCreateModalOpen(true)}> Add Method - + )} {successMessage && ( diff --git a/apps/edr-passenger-web/backoffice/src/components/layout/Sidebar.tsx b/apps/edr-passenger-web/backoffice/src/components/layout/Sidebar.tsx index d7fddfac9..2396ca8b7 100644 --- a/apps/edr-passenger-web/backoffice/src/components/layout/Sidebar.tsx +++ b/apps/edr-passenger-web/backoffice/src/components/layout/Sidebar.tsx @@ -70,33 +70,33 @@ const navigationSections: { title: string; items: NavItem[] }[] = [ { title: 'Tourism', items: [ - { name: 'Packages', href: '/packages', icon: Package, permission: PERMS.admin }, - // { name: 'Bookings', href: '/package-bookings', icon: Ticket, permission: PERMS.admin }, - { name: 'Inquiries', href: '/package-inquiries', icon: MessageSquare, permission: PERMS.admin }, + { name: 'Packages', href: '/packages', icon: Package, permission: PERMS.packages.view }, + // { name: 'Bookings', href: '/package-bookings', icon: Ticket, permission: PERMS.packages.view }, + { name: 'Inquiries', href: '/package-inquiries', icon: MessageSquare, permission: PERMS.inquiries.view }, ] }, { title: 'Master Data', items: [ - { name: 'Stations', href: '/stations', icon: MapPin, permission: PERMS.admin }, - { name: 'Trains', href: '/trains', icon: Train, permission: PERMS.admin }, - { name: 'Coaches', href: '/coaches', icon: Grid3x3, permission: PERMS.admin }, - { name: 'Seats', href: '/seats', icon: Armchair, permission: PERMS.admin }, - { name: 'Classes', href: '/classes', icon: Settings, permission: PERMS.admin }, - { name: 'Routes', href: '/routes', icon: Route, permission: PERMS.admin }, - { name: 'Schedules', href: '/schedules', icon: Calendar, permission: PERMS.admin }, + { name: 'Stations', href: '/stations', icon: MapPin, permission: PERMS.stations.view }, + { name: 'Trains', href: '/trains', icon: Train, permission: PERMS.trains.view }, + { name: 'Coaches', href: '/coaches', icon: Grid3x3, permission: PERMS.coaches.view }, + { name: 'Seats', href: '/seats', icon: Armchair, permission: PERMS.seats.view }, + { name: 'Classes', href: '/classes', icon: Settings, permission: PERMS.classes.view }, + { name: 'Routes', href: '/routes', icon: Route, permission: PERMS.routes.view }, + { name: 'Schedules', href: '/schedules', icon: Calendar, permission: PERMS.schedules.view }, ] }, { title: 'Financial', items: [ // { name: 'Pricing & Fares', href: '/pricing', icon: DollarSign, permission: PERMS.admin }, - { name: 'Tariff Rates', href: '/tariff-rates', icon: Banknote, permission: PERMS.admin }, + { name: 'Tariff Rates', href: '/tariff-rates', icon: Banknote, permission: PERMS.tariffRates.view }, // { name: 'Fare Rules', href: '/fare-management', icon: Settings, permission: PERMS.admin }, { name: 'Payments', href: '/payments', icon: CreditCard, permission: PERMS.payments.view }, - { name: 'Currencies', href: '/currencies', icon: Banknote, permission: PERMS.currencies.manage }, + { name: 'Currencies', href: '/currencies', icon: Banknote, permission: PERMS.currencies.view }, // { name: 'Promo Codes', href: '/promos', icon: Gift, permission: PERMS.admin }, - { name: 'Payment Methods', href: '/payment-methods', icon: CreditCard, permission: PERMS.payments.view }, + { name: 'Payment Methods', href: '/payment-methods', icon: CreditCard, permission: PERMS.paymentMethods.view }, // { name: 'Wallet Accounts', href: '/wallet-accounts', icon: Wallet, permission: PERMS.payments.view }, ] }, diff --git a/apps/edr-passenger-web/backoffice/src/lib/permissions.ts b/apps/edr-passenger-web/backoffice/src/lib/permissions.ts index 7731d784e..2a893f971 100644 --- a/apps/edr-passenger-web/backoffice/src/lib/permissions.ts +++ b/apps/edr-passenger-web/backoffice/src/lib/permissions.ts @@ -13,11 +13,69 @@ export const PERMS = { view: 'edr_passenger_app:tickets:view', manage: 'edr_passenger_app:tickets:manage', }, - payments: { - view: 'edr_passenger_app:payments:view_all', - refund: 'edr_passenger_app:payments:refund', - manage: 'edr_passenger_app:payments:manage_methods', + + // ── Master Data ──────────────────────────────────────────────── + stations: { + view: 'edr_passenger_app:stations:view', + manage: 'edr_passenger_app:stations:manage', }, + trains: { + view: 'edr_passenger_app:trains:view', + manage: 'edr_passenger_app:trains:manage', + }, + coaches: { + view: 'edr_passenger_app:coaches:view', + manage: 'edr_passenger_app:coaches:manage', + }, + seats: { + view: 'edr_passenger_app:seats:view', + manage: 'edr_passenger_app:seats:manage', + }, + classes: { + view: 'edr_passenger_app:classes:view', + manage: 'edr_passenger_app:classes:manage', + }, + routes: { + view: 'edr_passenger_app:routes:view', + manage: 'edr_passenger_app:routes:manage', + }, + schedules: { + view: 'edr_passenger_app:schedules:view', + manage: 'edr_passenger_app:schedules:manage', + }, + + // ── Tourism ──────────────────────────────────────────────────── + packages: { + view: 'edr_passenger_app:packages:view', + manage: 'edr_passenger_app:packages:manage', + }, + inquiries: { + view: 'edr_passenger_app:inquiries:view', + manage: 'edr_passenger_app:inquiries:manage', + }, + + // ── Finance ──────────────────────────────────────────────────── + tariffRates: { + view: 'edr_passenger_app:tariff_rates:view', + manage: 'edr_passenger_app:tariff_rates:manage', + }, + payments: { + view: 'edr_passenger_app:payments:view', + manage: 'edr_passenger_app:payments:manage', + // legacy aliases — still honoured by the backend guards + viewAll: 'edr_passenger_app:payments:view_all', + refund: 'edr_passenger_app:payments:refund', + manageMethods: 'edr_passenger_app:payments:manage_methods', + }, + paymentMethods: { + view: 'edr_passenger_app:payment_methods:view', + manage: 'edr_passenger_app:payment_methods:manage', + }, + currencies: { + view: 'edr_passenger_app:currencies:view', + manage: 'edr_passenger_app:currencies:manage', + }, + reports: { view: 'edr_passenger_app:reports:view', }, @@ -32,9 +90,6 @@ export const PERMS = { view: 'edr_passenger_app:agents:view', manage: 'edr_passenger_app:agents:manage', }, - currencies: { - manage: 'edr_passenger_app:currencies:manage', - }, notifications: { send: 'edr_passenger_app:notifications:send', }, From c8fe2a78f8dc48fdbd1190e133e8ad7e547e6dde Mon Sep 17 00:00:00 2001 From: Abubeker Yasin Date: Tue, 14 Jul 2026 20:18:11 +0300 Subject: [PATCH 2/2] Update schedules.controller.ts --- .../src/modules/schedules/schedules.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/edr-passenger-api/src/modules/schedules/schedules.controller.ts b/apps/edr-passenger-api/src/modules/schedules/schedules.controller.ts index 5263f4a22..b11ac3098 100644 --- a/apps/edr-passenger-api/src/modules/schedules/schedules.controller.ts +++ b/apps/edr-passenger-api/src/modules/schedules/schedules.controller.ts @@ -80,7 +80,7 @@ export class SchedulesController { } @Patch('routes/fare-rules/:id') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.schedules.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Update a route-level fare override' }) @ApiParam({ name: 'id', description: 'RouteFareRule UUID' }) updateRouteFareRule(@Param('id') id: string, @Body() dto: any) { @@ -96,7 +96,7 @@ export class SchedulesController { } @Post('routes/:routeId/fare-rules') - @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') + @PassengerStaff([PASSENGER_PERMS.schedules.manage, PASSENGER_PERMS.admin]) @ApiBearerAuth('IAM-auth') @ApiOperation({ summary: 'Create a route-level fare override' }) @ApiParam({ name: 'routeId', description: 'Route UUID' }) createRouteFareRule(@Param('routeId') routeId: string, @Body() dto: any) {