diff --git a/apps/admin-ui/gql/gql-types.ts b/apps/admin-ui/gql/gql-types.ts index 2f57bd3b9..c0a2bc58b 100644 --- a/apps/admin-ui/gql/gql-types.ts +++ b/apps/admin-ui/gql/gql-types.ts @@ -8408,6 +8408,20 @@ export type ReservationsQuery = { } | null; }; +export type GetReservationPermissionsQueryVariables = Exact<{ + id: Scalars["ID"]["input"]; +}>; + +export type GetReservationPermissionsQuery = { + reservation?: { + id: string; + reservationUnits: Array<{ + id: string; + unit?: { id: string; pk?: number | null } | null; + }>; + } | null; +}; + export type DeleteResourceMutationVariables = Exact<{ input: ResourceDeleteMutationInput; }>; @@ -15502,6 +15516,95 @@ export type ReservationsQueryResult = Apollo.QueryResult< ReservationsQuery, ReservationsQueryVariables >; +export const GetReservationPermissionsDocument = gql` + query GetReservationPermissions($id: ID!) { + reservation(id: $id) { + id + reservationUnits { + id + unit { + id + pk + } + } + } + } +`; + +/** + * __useGetReservationPermissionsQuery__ + * + * To run a query within a React component, call `useGetReservationPermissionsQuery` and pass it any options that fit your needs. + * When your component renders, `useGetReservationPermissionsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGetReservationPermissionsQuery({ + * variables: { + * id: // value for 'id' + * }, + * }); + */ +export function useGetReservationPermissionsQuery( + baseOptions: Apollo.QueryHookOptions< + GetReservationPermissionsQuery, + GetReservationPermissionsQueryVariables + > & + ( + | { variables: GetReservationPermissionsQueryVariables; skip?: boolean } + | { skip: boolean } + ) +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery< + GetReservationPermissionsQuery, + GetReservationPermissionsQueryVariables + >(GetReservationPermissionsDocument, options); +} +export function useGetReservationPermissionsLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions< + GetReservationPermissionsQuery, + GetReservationPermissionsQueryVariables + > +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery< + GetReservationPermissionsQuery, + GetReservationPermissionsQueryVariables + >(GetReservationPermissionsDocument, options); +} +export function useGetReservationPermissionsSuspenseQuery( + baseOptions?: + | Apollo.SkipToken + | Apollo.SuspenseQueryHookOptions< + GetReservationPermissionsQuery, + GetReservationPermissionsQueryVariables + > +) { + const options = + baseOptions === Apollo.skipToken + ? baseOptions + : { ...defaultOptions, ...baseOptions }; + return Apollo.useSuspenseQuery< + GetReservationPermissionsQuery, + GetReservationPermissionsQueryVariables + >(GetReservationPermissionsDocument, options); +} +export type GetReservationPermissionsQueryHookResult = ReturnType< + typeof useGetReservationPermissionsQuery +>; +export type GetReservationPermissionsLazyQueryHookResult = ReturnType< + typeof useGetReservationPermissionsLazyQuery +>; +export type GetReservationPermissionsSuspenseQueryHookResult = ReturnType< + typeof useGetReservationPermissionsSuspenseQuery +>; +export type GetReservationPermissionsQueryResult = Apollo.QueryResult< + GetReservationPermissionsQuery, + GetReservationPermissionsQueryVariables +>; export const DeleteResourceDocument = gql` mutation DeleteResource($input: ResourceDeleteMutationInput!) { deleteResource(input: $input) { diff --git a/apps/admin-ui/src/App.tsx b/apps/admin-ui/src/App.tsx index 4e6f7ce1c..1c4865186 100644 --- a/apps/admin-ui/src/App.tsx +++ b/apps/admin-ui/src/App.tsx @@ -127,7 +127,7 @@ function ClientApp({ , + , apiBaseUrl, feedbackUrl )} diff --git a/apps/admin-ui/src/spa/reservations/router.tsx b/apps/admin-ui/src/spa/reservations/router.tsx index 99ea8c2cb..da9a0c452 100644 --- a/apps/admin-ui/src/spa/reservations/router.tsx +++ b/apps/admin-ui/src/spa/reservations/router.tsx @@ -1,21 +1,99 @@ import React from "react"; -import { Route, Routes } from "react-router-dom"; +import { Route, Routes, useParams } from "react-router-dom"; import { RequestedPage } from "./requested"; import { ListReservationsPage } from "."; import { EditPage } from "./[id]/edit"; import { ReservationPage } from "./[id]"; import { SeriesPage } from "./[id]/series"; +import { useGetReservationPermissionsQuery, UserPermissionChoice } from "@gql/gql-types"; +import { gql } from "@apollo/client"; +import { base64encode, filterNonNullable } from "common/src/helpers"; +import { useCheckPermission } from "@/hooks"; +import Error403 from "@/common/Error403"; +import { CenterSpinner } from "common/styles/util"; // TODO there is no index? (all and requested works like index but not really) -const ReservationsRouter = (): JSX.Element => ( - - } /> - } /> - } /> - } /> - } /> - } /> - -); +function ReservationsRouter({ + apiBaseUrl, + feedbackUrl, +}: RouterProps): JSX.Element { + + return ( + + } /> + } /> + } /> + } /> + } + apiBaseUrl={apiBaseUrl} + feedbackUrl={feedbackUrl} + /> } + /> + } + apiBaseUrl={apiBaseUrl} + feedbackUrl={feedbackUrl} + />} + /> + + ); +} + +export const GET_RESERVATION_PERMISSION_QUERY = gql` + query GetReservationPermissions($id: ID!) { + reservation(id: $id) { + id + reservationUnits { + id + unit { + id + pk + } + } + } + } +`; + +function useCheckReservationPermissions(pk?: string) { + const id = base64encode(`ReservationNode:${pk}`); + const { data, loading: qLoading } = useGetReservationPermissionsQuery({ + variables: { id }, + skip: !pk, + }); + const units = filterNonNullable(data?.reservation?.reservationUnits.map((ru) => ru.unit?.pk)); + const { hasPermission, isLoading } = useCheckPermission({ + units, + permission: UserPermissionChoice.CanManageReservations, + }); + return { hasPermission, loading: qLoading || isLoading }; +} + +type RouterProps = { + apiBaseUrl: string; + feedbackUrl: string; +} + +type PermCheckedRouteProps = { + element: JSX.Element; +} & RouterProps; + +/// Custom permission checks that use backend queries for the check +/// requires the query because we don't known the unit the reservation is for +function PermCheckedRoute({ element, apiBaseUrl, feedbackUrl }: PermCheckedRouteProps) { + const { id } = useParams(); + const { hasPermission, loading } = useCheckReservationPermissions(id); + if (loading) { + return ; + } + if (!hasPermission) { + return ; + } + return element; +} export default ReservationsRouter;