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;