Skip to content
This repository was archived by the owner on Feb 10, 2025. It is now read-only.

Commit fdceed5

Browse files
committed
fix: application rounds tab permission
Filter queries by units the user has permissions, so we don't get permission errors.
1 parent b3440e4 commit fdceed5

4 files changed

Lines changed: 59 additions & 17 deletions

File tree

apps/admin-ui/src/spa/recurring-reservations/application-rounds/[id]/review/AllocatedEventDataLoader.tsx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,24 @@ import { errorToast } from "common/src/common/toast";
1212
import Loader from "@/component/Loader";
1313
import { More } from "@/component/More";
1414
import { useSort } from "@/hooks/useSort";
15-
import { transformApplicantType } from "./utils";
15+
import { getFilteredUnits, transformApplicantType } from "./utils";
1616
import { useSearchParams } from "react-router-dom";
1717
import { AllocatedEventsTable, SORT_KEYS } from "./AllocatedEventsTable";
1818
import { transformWeekday, type Day } from "common/src/conversion";
19+
import { getPermissionErrors } from "common/src/apolloUtils";
1920

2021
type Props = {
2122
applicationRoundPk: number;
23+
unitOptions: { nameFi: string; pk: number }[];
2224
};
2325

24-
export function TimeSlotDataLoader({ applicationRoundPk }: Props): JSX.Element {
25-
const [orderBy, handleSortChanged] = useSort(SORT_KEYS);
26+
export function TimeSlotDataLoader({
27+
unitOptions,
28+
applicationRoundPk,
29+
}: Props): JSX.Element {
30+
const { t } = useTranslation();
2631

32+
const [orderBy, handleSortChanged] = useSort(SORT_KEYS);
2733
const [searchParams] = useSearchParams();
2834
const unitFilter = searchParams.getAll("unit");
2935
const applicantFilter = searchParams.getAll("applicant");
@@ -34,7 +40,7 @@ export function TimeSlotDataLoader({ applicationRoundPk }: Props): JSX.Element {
3440
const query = useAllocatedTimeSlotsQuery({
3541
skip: !applicationRoundPk,
3642
variables: {
37-
allocatedUnit: unitFilter.map(Number).filter(Number.isFinite),
43+
allocatedUnit: getFilteredUnits(unitFilter, unitOptions),
3844
applicationRound: applicationRoundPk,
3945
applicantType: transformApplicantType(applicantFilter),
4046
applicationSectionStatus: [
@@ -54,17 +60,19 @@ export function TimeSlotDataLoader({ applicationRoundPk }: Props): JSX.Element {
5460
orderBy: transformOrderBy(orderBy),
5561
},
5662
onError: (err: ApolloError) => {
57-
errorToast({ text: err.message });
63+
const permErrors = getPermissionErrors(err);
64+
if (permErrors.length > 0) {
65+
errorToast({ text: t("errors.noPermission") });
66+
} else {
67+
errorToast({ text: t("errors.errorFetchingData") });
68+
}
5869
},
5970
fetchPolicy: "cache-and-network",
60-
// TODO enable or no?
6171
nextFetchPolicy: "cache-first",
6272
});
6373

6474
const { fetchMore, previousData, loading, data } = query;
6575

66-
const { t } = useTranslation();
67-
6876
const dataToUse = data ?? previousData;
6977
if (loading && !dataToUse) {
7078
return <Loader />;

apps/admin-ui/src/spa/recurring-reservations/application-rounds/[id]/review/RejectedOccurrencesDataLoader.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,21 @@ import { More } from "@/component/More";
1414
import React from "react";
1515
import Loader from "@/component/Loader";
1616
import { filterNonNullable } from "common/src/helpers";
17+
import { getPermissionErrors } from "common/src/apolloUtils";
18+
import { useTranslation } from "next-i18next";
19+
import { getFilteredUnits } from "./utils";
1720

1821
type Props = {
1922
applicationRoundPk: number;
23+
unitOptions: { nameFi: string; pk: number }[];
2024
};
2125

2226
function RejectedOccurrencesDataLoader({
2327
applicationRoundPk,
28+
unitOptions,
2429
}: Props): JSX.Element {
30+
const { t } = useTranslation();
31+
2532
const [orderBy, handleSortChanged] = useSort(SORT_KEYS);
2633
const [searchParams] = useSearchParams();
2734
const unitFilter = searchParams.getAll("unit");
@@ -32,18 +39,22 @@ function RejectedOccurrencesDataLoader({
3239
useRejectedOccurrencesQuery({
3340
variables: {
3441
applicationRound: applicationRoundPk,
35-
unit: unitFilter.map(Number).filter(Number.isFinite),
42+
unit: getFilteredUnits(unitFilter, unitOptions),
3643
reservationUnit: reservationUnitFilter
3744
.map(Number)
3845
.filter(Number.isFinite),
3946
orderBy: transformOrderBy(orderBy),
4047
textSearch: nameFilter,
4148
},
4249
onError: (err: ApolloError) => {
43-
errorToast({ text: err.message });
50+
const permErrors = getPermissionErrors(err);
51+
if (permErrors.length > 0) {
52+
errorToast({ text: t("errors.noPermission") });
53+
} else {
54+
errorToast({ text: t("errors.errorFetchingData") });
55+
}
4456
},
4557
fetchPolicy: "cache-and-network",
46-
// TODO enable or no?
4758
nextFetchPolicy: "cache-first",
4859
});
4960

apps/admin-ui/src/spa/recurring-reservations/application-rounds/[id]/review/Review.tsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import { isApplicationRoundInProgress } from "@/helpers";
3434
import { breakpoints } from "common";
3535
import RejectedOccurrencesDataLoader from "./RejectedOccurrencesDataLoader";
3636
import { errorToast } from "common/src/common/toast";
37+
import { hasPermission } from "@/modules/permissionHelper";
38+
import { useSession } from "@/hooks/auth";
3739

3840
const HeadingContainer = styled.div`
3941
display: flex;
@@ -247,8 +249,13 @@ export function Review({
247249
applicationRound?.reservationUnits?.flatMap((x) => x)
248250
);
249251

250-
const ds = getUnitOptions(resUnits);
251-
const unitPks = uniqBy(ds, (unit) => unit.pk).sort((a, b) =>
252+
const { user } = useSession();
253+
254+
// need filtered list of units that the user has permission to view
255+
const ds = getUnitOptions(resUnits).filter((unit) =>
256+
hasPermission(user, UserPermissionChoice.CanViewApplications, unit.pk)
257+
);
258+
const unitOptions = uniqBy(ds, (unit) => unit.pk).sort((a, b) =>
252259
a.nameFi.localeCompare(b.nameFi)
253260
);
254261

@@ -341,15 +348,19 @@ export function Review({
341348
</Tabs.TabList>
342349
<Tabs.TabPanel>
343350
<TabContent>
344-
<Filters units={unitPks} enableApplicant />
351+
<Filters units={unitOptions} enableApplicant />
345352
<ApplicationDataLoader
346353
applicationRoundPk={applicationRound.pk ?? 0}
347354
/>
348355
</TabContent>
349356
</Tabs.TabPanel>
350357
<Tabs.TabPanel>
351358
<TabContent>
352-
<Filters units={unitPks} statusOption="event" enableApplicant />
359+
<Filters
360+
units={unitOptions}
361+
statusOption="event"
362+
enableApplicant
363+
/>
353364
<ApplicationEventDataLoader
354365
applicationRoundPk={applicationRound.pk ?? 0}
355366
/>
@@ -358,7 +369,7 @@ export function Review({
358369
<Tabs.TabPanel>
359370
<TabContent>
360371
<Filters
361-
units={unitPks}
372+
units={unitOptions}
362373
reservationUnits={reservationUnitOptions}
363374
enableApplicant
364375
enableWeekday
@@ -367,20 +378,22 @@ export function Review({
367378
/>
368379
<TimeSlotDataLoader
369380
applicationRoundPk={applicationRound.pk ?? 0}
381+
unitOptions={unitOptions}
370382
/>
371383
</TabContent>
372384
</Tabs.TabPanel>
373385
{isApplicationRoundEnded && (
374386
<Tabs.TabPanel>
375387
<TabContent>
376388
<Filters
377-
units={unitPks}
389+
units={unitOptions}
378390
reservationUnits={reservationUnitOptions}
379391
enableReservationUnit
380392
statusOption="eventShort"
381393
/>
382394
<RejectedOccurrencesDataLoader
383395
applicationRoundPk={applicationRound.pk ?? 0}
396+
unitOptions={unitOptions}
384397
/>
385398
</TabContent>
386399
</Tabs.TabPanel>

apps/admin-ui/src/spa/recurring-reservations/application-rounds/[id]/review/utils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,13 @@ export function formatAppliedReservationTime(time: {
109109
const { count, hours } = time;
110110
return `${formatNumber(count, "")} / ${formatters.oneDecimal.format(hours)} t`;
111111
}
112+
113+
/// Clean query param selection and filter by possible units
114+
/// @return array of selected unit pks or all possible unit pks
115+
export function getFilteredUnits(
116+
unitFilter: string[],
117+
possibleUnits: { nameFi: string; pk: number }[]
118+
): number[] {
119+
const units = unitFilter.map(Number).filter(Number.isFinite);
120+
return units.length > 0 ? units : possibleUnits.map((u) => u.pk);
121+
}

0 commit comments

Comments
 (0)