Skip to content

Commit

Permalink
add: accessType to seasonal search filters & result cards
Browse files Browse the repository at this point in the history
- remove unnecessary helper functions from modules/search
- replace remaining instances of removed query param helper functions with toNumber and/or ignoreMaybeArray
  • Loading branch information
vincit-matu committed Feb 12, 2025
1 parent ebe1999 commit 6b6ad63
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 77 deletions.
23 changes: 19 additions & 4 deletions apps/ui/components/search/ReservationUnitCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
IconSize,
ButtonSize,
ButtonVariant,
IconLock,
} from "hds-react";
import React from "react";
import { useTranslation } from "next-i18next";
Expand All @@ -32,7 +33,7 @@ export function ReservationUnitCard({
selectReservationUnit,
containsReservationUnit,
removeReservationUnit,
}: CardProps): JSX.Element {
}: Readonly<CardProps>): JSX.Element {
const { t } = useTranslation();

const name = getReservationUnitName(reservationUnit);
Expand Down Expand Up @@ -79,14 +80,28 @@ export function ReservationUnitCard({
}),
});
}
if (reservationUnit.currentAccessType) {
infos.push({
icon: (
<IconLock
aria-hidden="false"
aria-label={t("reservationUnit:accessType")}
size={IconSize.Small}
/>
),
value: t(
`reservationUnit:accessTypes.${reservationUnit.currentAccessType}`
),
});
}

const buttons = [];
if (containsReservationUnit(reservationUnit)) {
buttons.push(
<Button
size={ButtonSize.Small}
variant={ButtonVariant.Primary}
iconEnd={<IconCheck aria-hidden="true" />}
iconEnd={<IconCheck />}
onClick={() => removeReservationUnit(reservationUnit)}
data-testid="reservation-unit-card__button--select"
key={t("common:removeReservationUnit")}
Expand All @@ -99,7 +114,7 @@ export function ReservationUnitCard({
<Button
size={ButtonSize.Small}
variant={ButtonVariant.Secondary}
iconEnd={<IconPlus aria-hidden="true" />}
iconEnd={<IconPlus />}
onClick={() => selectReservationUnit(reservationUnit)}
data-testid="reservation-unit-card__button--select"
key={t("common:selectReservationUnit")}
Expand All @@ -116,7 +131,7 @@ export function ReservationUnitCard({
data-testid="reservation-unit-card__button--link"
key="show"
>
<IconLinkExternal aria-hidden="true" />
<IconLinkExternal />
{t("common:show")}
</ButtonLikeLink>
);
Expand Down
51 changes: 34 additions & 17 deletions apps/ui/components/search/SeasonalSearchForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@ import { useSearchModify } from "@/hooks/useSearchValues";
import { FilterTagList } from "./FilterTagList";
import { ControlledSelect } from "common/src/components/form/ControlledSelect";
import { BottomContainer, Filters, StyledSubmitButton } from "./styled";
import {
mapQueryParamToNumber,
mapQueryParamToNumberArray,
mapSingleParamToFormValue,
} from "@/modules/search";
import { mapParamToNumber } from "@/modules/search";
import SingleLabelInputGroup from "@/components/common/SingleLabelInputGroup";
import { type URLSearchParams } from "node:url";
import { useSearchParams } from "next/navigation";
import { type ReadonlyURLSearchParams, useSearchParams } from "next/navigation";
import { AccessType } from "@gql/gql-types";
import { toNumber } from "common/src/helpers";

const filterOrder = [
"applicationRound",
Expand All @@ -29,6 +26,7 @@ const filterOrder = [
"reservationUnitTypes",
"unit",
"purposes",
"accessType",
] as const;

type FormValues = {
Expand All @@ -38,19 +36,22 @@ type FormValues = {
reservationUnitTypes: number[];
purposes: number[];
textSearch: string;
accessType: string[];
};

// TODO combine as much as possible with the one in single-search (move them to a common place)
function mapQueryToForm(query: URLSearchParams): FormValues {
function mapQueryToForm(params: ReadonlyURLSearchParams): FormValues {
return {
purposes: mapQueryParamToNumberArray(query.getAll("purposes")),
unit: mapQueryParamToNumberArray(query.getAll("unit")),
reservationUnitTypes: mapQueryParamToNumberArray(
query.getAll("reservationUnitTypes")
purposes: mapParamToNumber(params.getAll("purposes"), 1),
unit: mapParamToNumber(params.getAll("unit"), 1),
reservationUnitTypes: mapParamToNumber(
params.getAll("reservationUnitTypes"),
1
),
minPersons: mapQueryParamToNumber(query.getAll("minPersons")),
maxPersons: mapQueryParamToNumber(query.getAll("maxPersons")),
textSearch: mapSingleParamToFormValue(query.getAll("textSearch")) ?? "",
minPersons: toNumber(params.get("minPersons")),
maxPersons: toNumber(params.get("maxPersons")),
textSearch: params.get("textSearch") ?? "",
accessType: params.getAll("accessType"),
};
}

Expand All @@ -60,12 +61,12 @@ export function SeasonalSearchForm({
purposeOptions,
unitOptions,
isLoading,
}: {
}: Readonly<{
reservationUnitTypeOptions: OptionType[];
purposeOptions: OptionType[];
unitOptions: OptionType[];
isLoading: boolean;
}): JSX.Element | null {
}>): JSX.Element | null {
const { t } = useTranslation();

const { handleSearch } = useSearchModify();
Expand All @@ -79,6 +80,11 @@ export function SeasonalSearchForm({
handleSearch(criteria, true);
};

const accessTypeOptions = Object.values(AccessType).map((value) => ({
value,
label: t(`reservationUnit:accessTypes.${value}`),
}));

const translateTag = (key: string, value: string): string | undefined => {
switch (key) {
case "unit":
Expand All @@ -88,6 +94,8 @@ export function SeasonalSearchForm({
?.label;
case "purposes":
return purposeOptions.find((n) => String(n.value) === value)?.label;
case "accessType":
return accessTypeOptions.find((n) => String(n.value) === value)?.label;
default:
return "";
}
Expand All @@ -97,6 +105,7 @@ export function SeasonalSearchForm({
"unit",
"reservationUnitTypes",
"purposes",
"accessType",
] as const;
const hideList = ["id", "order", "sort", "ref"] as const;

Expand Down Expand Up @@ -159,6 +168,14 @@ export function SeasonalSearchForm({
options={purposeOptions}
label={t("searchForm:purposesFilter")}
/>
<ControlledSelect
multiselect
clearable
name="accessType"
control={control}
options={accessTypeOptions}
label={t("searchForm:accessTypeFilter")}
/>
</Filters>
<BottomContainer>
<FilterTagList
Expand Down
59 changes: 15 additions & 44 deletions apps/ui/modules/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ type ProcessVariablesParams =
language: string;
kind: ReservationKind.Season;
applicationRound: number;
reservationPeriodBegin: string;
reservationPeriodEnd: string;
};

export function processVariables({
values,
language,
Expand Down Expand Up @@ -193,6 +196,14 @@ export function processVariables({
ignoreMaybeArray(values.getAll("showOnlyReservable")) !== "false";
const applicationRound =
"applicationRound" in rest && isSeasonal ? rest.applicationRound : null;
const reservationPeriodBegin =
"reservationPeriodBegin" in rest && isSeasonal
? rest.reservationPeriodBegin
: null;
const reservationPeriodEnd =
"reservationPeriodEnd" in rest && isSeasonal
? rest.reservationPeriodEnd
: null;
const timeEnd = ignoreMaybeArray(values.getAll("timeEnd"));
const timeBegin = ignoreMaybeArray(values.getAll("timeBegin"));
const accessType = values.getAll("accessType").map(transformAccessTypeSafe);
Expand All @@ -217,8 +228,10 @@ export function processVariables({
reservationUnitType: reservationUnitTypes,
equipments,
accessType,
accessTypeStartDate: reservableDateStart,
accessTypeEndDate: reservableDateEnd,
accessTypeStartDate: isSeasonal
? reservationPeriodBegin
: reservableDateStart,
accessTypeEndDate: isSeasonal ? reservationPeriodEnd : reservableDateEnd,
...(startDate != null
? {
reservableDateStart,
Expand Down Expand Up @@ -260,15 +273,6 @@ export function processVariables({
};
}

export function mapSingleParamToFormValue(
param: string | string[] | undefined
): string | null {
if (param == null) return null;
if (param === "") return null;
if (Array.isArray(param)) return param.join(",");
return param;
}

// default to false if the param is present but not true, null if not present
export function mapSingleBooleanParamToFormValue(
param: string | string[] | undefined
Expand All @@ -284,39 +288,6 @@ export function mapSingleBooleanParamToFormValue(
return param === "true";
}

export function mapQueryParamToStringArray(
param: string | string[] | undefined
): string[] {
if (param == null) return [];
if (param === "") return [];
if (Array.isArray(param)) return param;
return [param];
}

export function mapQueryParamToNumber(
param: string | string[] | undefined
): number | null {
if (param == null) return null;
if (param === "") return null;
if (Array.isArray(param)) {
return toNumber(param[0]);
}
return toNumber(param);
}

export function mapQueryParamToNumberArray(
param: string | string[] | undefined
): number[] {
if (param == null) return [];
if (param === "") return [];
if (Array.isArray(param)) {
return param.map(Number).filter(Number.isInteger);
}
const v = Number(param);
if (Number.isNaN(v)) {
return [];
}
return [v];
export function mapParamToNumber(param: string[], min?: number): number[] {
const numbers = param.map(Number).filter(Number.isInteger);
return min != null ? numbers.filter((n) => n >= min) : numbers;
Expand Down
19 changes: 11 additions & 8 deletions apps/ui/pages/recurring/[id]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ import {
type ApplicationRoundsUiQueryVariables,
ApplicationRoundsUiDocument,
} from "@gql/gql-types";
import { filterNonNullable } from "common/src/helpers";
import {
filterNonNullable,
ignoreMaybeArray,
toNumber,
} from "common/src/helpers";
import { SeasonalSearchForm } from "@/components/search/SeasonalSearchForm";
import { createApolloClient } from "@/modules/apolloClient";
import { ReservationUnitCard } from "@/components/search/ReservationUnitCard";
import { useReservationUnitList } from "@/hooks";
import { ListWithPagination } from "@/components/common/ListWithPagination";
import StartApplicationBar from "@/components/common/StartApplicationBar";
import { getCommonServerSideProps } from "@/modules/serverUtils";
import {
getSearchOptions,
mapQueryParamToNumber,
processVariables,
} from "@/modules/search";
import { getSearchOptions, processVariables } from "@/modules/search";
import { useSearchQuery } from "@/hooks/useSearchQuery";
import { SortingComponent } from "@/components/SortingComponent";
import { useRouter } from "next/router";
Expand Down Expand Up @@ -70,12 +70,12 @@ function SeasonalSearch({
unitOptions,
reservationUnitTypeOptions,
purposeOptions,
}: Props): JSX.Element {
}: Readonly<Props>): JSX.Element {
const { t, i18n } = useTranslation();
const router = useRouter();
const searchValues = useSearchParams();

const applicationRoundPk = mapQueryParamToNumber(router.query.id);
const applicationRoundPk = toNumber(ignoreMaybeArray(router.query.id));
const selectedApplicationRound = applicationRounds.find(
(ar) => ar.pk === applicationRoundPk
);
Expand All @@ -93,6 +93,9 @@ function SeasonalSearch({
language: i18n.language,
kind: ReservationKind.Season,
applicationRound: applicationRoundPk ?? 0,
reservationPeriodBegin:
selectedApplicationRound?.reservationPeriodBegin ?? "",
reservationPeriodEnd: selectedApplicationRound?.reservationPeriodEnd ?? "",
});
const query = useSearchQuery(variables);
const { data, isLoading, error, fetchMore, previousData } = query;
Expand Down
4 changes: 2 additions & 2 deletions apps/ui/pages/reservation/cancel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
getReservationByOrderUuid,
} from "@/modules/serverUtils";
import { useTranslation } from "next-i18next";
import { mapSingleParamToFormValue } from "@/modules/search";
import { createApolloClient } from "@/modules/apolloClient";
import {
DeleteReservationDocument,
Expand All @@ -17,6 +16,7 @@ import {
} from "@/gql/gql-types";
import { Breadcrumb } from "@/components/common/Breadcrumb";
import { reservationsPrefix } from "@/modules/urls";
import { ignoreMaybeArray } from "common/src/helpers";

// This is the callback page from webstore if user cancels the order
// TODO this would be nicer if we could use a reservation/[id]/cancelled page (or reservation/[id])
Expand Down Expand Up @@ -49,7 +49,7 @@ function Cancel({ apiBaseUrl }: NarrowedProps): JSX.Element {
export async function getServerSideProps(ctx: GetServerSidePropsContext) {
const { locale, query } = ctx;
const commonProps = getCommonServerSideProps();
const orderId = mapSingleParamToFormValue(query.orderId);
const orderId = ignoreMaybeArray(query.orderId);

const notFoundValue = {
notFound: true,
Expand Down
4 changes: 2 additions & 2 deletions apps/ui/pages/success.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import {
} from "@/modules/serverUtils";
import { getReservationPath } from "@/modules/urls";
import { createApolloClient } from "@/modules/apolloClient";
import { mapSingleParamToFormValue } from "@/modules/search";
import { useEffect } from "react";
import { useRouter } from "next/router";
import { CenterSpinner } from "common/styles/util";
import { ignoreMaybeArray } from "common/src/helpers";

// TODO should be moved to /reservations/success
// but because this is webstore callback page we need to leave the url (use an url rewrite)
Expand All @@ -23,7 +23,7 @@ export async function getServerSideProps(ctx: GetServerSidePropsContext) {
const { locale, query } = ctx;
const commonProps = getCommonServerSideProps();

const orderId = mapSingleParamToFormValue(query.orderId);
const orderId = ignoreMaybeArray(query.orderId);
const notFoundValue = {
notFound: true,
props: {
Expand Down

0 comments on commit 6b6ad63

Please sign in to comment.