mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 15:18:11 +00:00
fix: ( bookings ) require explicit tickets:generate to issue reservations
This commit is contained in:
@@ -4,7 +4,7 @@ import { useState } from 'react';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { seatsApi, schedulesApi, fleetApi, routeCoachTemplatesApi, bookingsApi } from '@/lib/api';
|
||||
import { routesApi } from '@/lib/api/routes';
|
||||
import { usePermission } from '@/lib/use-permission';
|
||||
import { usePermissionStrict } from '@/lib/use-permission';
|
||||
import { PERMS } from '@/lib/permissions';
|
||||
import Modal from '@/components/ui/Modal';
|
||||
import ActionButton from '@/components/ui/ActionButton'
|
||||
@@ -44,9 +44,10 @@ export default function SeatsPage() {
|
||||
const [issueBookingResult, setIssueBookingResult] = useState<{ payUrl?: string; bookingRef?: string } | null>(null);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
// Issuing a booking off a reserved seat is admin-only (POST /bookings/reservations/:seatId/issue
|
||||
// is guarded by @PassengerAdmin) — hide the ticket action for everyone else.
|
||||
const canIssueBooking = usePermission(PERMS.admin);
|
||||
// Issuing a booking off a reserved seat needs edr_passenger_app:tickets:generate
|
||||
// (POST /bookings/reservations/:seatId/issue is guarded by @PassengerStaffStrict).
|
||||
// Strict: being an admin is not enough, the permission has to be granted.
|
||||
const canIssueBooking = usePermissionStrict(PERMS.tickets.generate);
|
||||
|
||||
const { data: schedulesData } = useQuery({
|
||||
queryKey: ['schedules'],
|
||||
|
||||
@@ -23,6 +23,7 @@ interface AuthState {
|
||||
setUser: (user: AdminUser, token: string) => void;
|
||||
initialize: () => void;
|
||||
hasPermission: (key: string) => boolean;
|
||||
hasPermissionStrict: (key: string) => boolean;
|
||||
}
|
||||
|
||||
export const useAuthStore = create<AuthState>((set, get) => ({
|
||||
@@ -123,4 +124,12 @@ export const useAuthStore = create<AuthState>((set, get) => ({
|
||||
if (user.isSuperAdmin || user.isOrgAdmin) return true;
|
||||
return user.permissions.includes(key);
|
||||
},
|
||||
|
||||
// No super-admin / org-admin bypass — mirrors PassengerStaffStrict on the API,
|
||||
// so we don't render actions that would 403.
|
||||
hasPermissionStrict: (key: string) => {
|
||||
const { user } = get();
|
||||
if (!user) return false;
|
||||
return user.permissions.includes(key);
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -13,3 +13,15 @@ import { useAuthStore } from './auth-store';
|
||||
export function usePermission(key: string): boolean {
|
||||
return useAuthStore((s) => s.hasPermission(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as usePermission but WITHOUT the super-admin / org-admin bypass — the
|
||||
* permission must be explicitly granted. Use it wherever the API endpoint is
|
||||
* guarded with PassengerStaffStrict, so the UI matches what the API allows.
|
||||
*
|
||||
* Usage:
|
||||
* const canIssue = usePermissionStrict(PERMS.tickets.generate);
|
||||
*/
|
||||
export function usePermissionStrict(key: string): boolean {
|
||||
return useAuthStore((s) => s.hasPermissionStrict(key));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user