Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add canceling seasonal reservation #1569

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/admin-ui/gql/gql-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2435,7 +2435,7 @@ export type RecurringReservationNode = Node & {
description: Scalars["String"]["output"];
endDate?: Maybe<Scalars["Date"]["output"]>;
endTime?: Maybe<Scalars["Time"]["output"]>;
extId: Scalars["UUID"]["output"];
extUuid: Scalars["UUID"]["output"];
/** The ID of the object */
id: Scalars["ID"]["output"];
name: Scalars["String"]["output"];
Expand Down Expand Up @@ -2954,7 +2954,7 @@ export type ReservationNode = Node & {
denyReason?: Maybe<ReservationDenyReasonNode>;
description?: Maybe<Scalars["String"]["output"]>;
end: Scalars["DateTime"]["output"];
extId: Scalars["UUID"]["output"];
extUuid: Scalars["UUID"]["output"];
freeOfChargeReason?: Maybe<Scalars["String"]["output"]>;
handledAt?: Maybe<Scalars["DateTime"]["output"]>;
handlingDetails?: Maybe<Scalars["String"]["output"]>;
Expand Down
4 changes: 2 additions & 2 deletions apps/ui/components/AccordionWithIcons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const ClosedAccordionWrapper = styled.div`
padding: var(--spacing-s);
`;

const IconListWrapper = styled.div`
const IconListWrapper = styled.ul`
display: grid;
gap: var(--spacing-xs);

Expand Down Expand Up @@ -123,7 +123,7 @@ export function AccordionWithIcons({
<Heading as={`h${headingLevel}`}>{heading}</Heading>
<IconListWrapper>
{icons.map(({ text, icon, textPostfix }) => (
<IconLabel key={text}>
<IconLabel key={text} as="li">
{icon}
<span>{text}</span>
<span>{textPostfix}</span>
Expand Down
96 changes: 42 additions & 54 deletions apps/ui/components/application/ApprovedReservations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ReservationStateChoice,
} from "@/gql/gql-types";
import {
getApplicationPath,
getApplicationSectionPath,
getReservationUnitPath,
} from "@/modules/urls";
Expand All @@ -20,11 +21,9 @@ import { IconButton, StatusLabel } from "common/src/components";
import {
filterNonNullable,
formatApiTimeInterval,
formatMinutes,
fromMondayFirst,
getLocalizationLang,
LocalizationLanguages,
timeToMinutes,
type LocalizationLanguages,
} from "common/src/helpers";
import {
Button,
Expand All @@ -50,6 +49,12 @@ import { AccordionWithIcons } from "../AccordionWithIcons";
import { CenterSpinner } from "../common/common";
import { useMedia } from "react-use";
import { ButtonContainer, Flex } from "common/styles/util";
import { useRouter } from "next/router";
import {
isReservationCancellableReason,
ReservationCancellableReason,
} from "@/modules/reservation";
import { formatDateTimeStrings } from "@/modules/util";

const N_RESERVATIONS_TO_SHOW = 20;

Expand Down Expand Up @@ -242,7 +247,7 @@ export function ApprovedReservations({ application }: Props) {
return slots.length > 0;
})
);
const initiallyOpen = app?.applicationSections?.length === 1;
const initiallyOpen = sections.length === 1;
return (
<Flex>
{loading && <CenterSpinner />}
Expand Down Expand Up @@ -447,6 +452,7 @@ type ReservationsTableElem = {
"nameSv" | "nameFi" | "nameEn" | "id" | "pk"
>;
status: "" | "rejected" | "modified";
isCancellableReason: ReservationCancellableReason;
pk: number;
};

Expand Down Expand Up @@ -510,16 +516,21 @@ const StyledStatusLabel = styled(StatusLabel)`

function ReservationsTable({
reservations,
application,
}: {
reservations: ReservationsTableElem[];
application: Pick<ApplicationT, "pk">;
}) {
const { i18n } = useTranslation();
const { t } = useTranslation();

const lang = getLocalizationLang(i18n.language);
const router = useRouter();

const handleCancel = (pk: number) => {
errorToast({ text: `Not implemented: cancel reservation: ${pk}` });
const appPath = getApplicationPath(application.pk, "view");
const url = `${appPath}/${pk}/cancel`;
router.push(url);
};

const cols = [
Expand Down Expand Up @@ -598,10 +609,15 @@ function ReservationsTable({
key: "cancelButton",
headerName: "",
isSortable: false,
transform: ({ pk, status }: ReservationsTableElem) => (
transform: ({ pk, isCancellableReason }: ReservationsTableElem) => (
<CancelButton
onClick={() => handleCancel(pk)}
disabled={status === "rejected"}
disabled={isCancellableReason !== ""}
title={
isCancellableReason === ""
? t("common:cancel")
: t(`reservations:modifyTimeReasons.${isCancellableReason}`)
}
// TODO on mobile this should be hidden behind a popover (for now it's hidden)
className="hide-on-mobile"
>
Expand All @@ -618,49 +634,6 @@ function ReservationsTable({
);
}

/// Converts a date to minutes discarding date and seconds
function toMinutes(d: Date): number {
return d.getHours() * 60 + d.getMinutes();
}

/// Creates time and date strings for reservations
/// @param t - translation function
/// @param res - reservation object
/// @param orig - original reservation object (use undefined if not possible to modify)
function toTimeString(
t: TFunction,
reservation: {
begin: string;
end: string;
},
orig?: {
beginTime: string;
endTime: string;
}
): { date: Date; time: string; dayOfWeek: string; isModified: boolean } {
const start = new Date(reservation.begin);
const end = new Date(reservation.end);
const dayOfWeek = t(`weekDayLong.${start.getDay()}`);

const originalBeginMins = orig != null ? timeToMinutes(orig.beginTime) : -1;
const originalEndMins = orig != null ? timeToMinutes(orig.endTime) : -1;

const beginMins = toMinutes(start);
const endMins = toMinutes(end);
const isModified =
orig != null &&
(originalBeginMins !== beginMins || originalEndMins !== endMins);
const btime = formatMinutes(beginMins);
const etime = formatMinutes(endMins);
const time = `${btime} - ${etime}`;
return {
date: start,
time,
dayOfWeek,
isModified,
};
}

function sectionToreservations(
t: TFunction,
section: ApplicationSectionT
Expand All @@ -687,11 +660,12 @@ function sectionToreservations(
): ReservationsTableElem[] {
return r.rejectedOccurrences.map((res) => {
const reservation = { begin: res.beginDatetime, end: res.endDatetime };
const rest = toTimeString(t, reservation);
const rest = formatDateTimeStrings(t, reservation);
return {
...rest,
reservationUnit: r.reservationUnit,
status: "rejected",
isCancellableReason: "ALREADY_CANCELLED",
pk: 0,
};
});
Expand All @@ -701,9 +675,14 @@ function sectionToreservations(
r: (typeof recurringReservations)[0]
): ReservationsTableElem[] {
return r.reservations.map((res) => {
const { isModified, ...rest } = toTimeString(t, res, r.allocatedTimeSlot);
const { isModified, ...rest } = formatDateTimeStrings(
t,
res,
r.allocatedTimeSlot
);

const status =
res.state === ReservationStateChoice.Cancelled ||
res.state === ReservationStateChoice.Denied
? "rejected"
: isModified
Expand All @@ -713,6 +692,7 @@ function sectionToreservations(
...rest,
reservationUnit: r.reservationUnit,
status,
isCancellableReason: isReservationCancellableReason(res),
pk: res.pk ?? 0,
};
});
Expand Down Expand Up @@ -768,8 +748,10 @@ function sectionToReservationUnits(

export function AllReservations({
applicationSection,
application,
}: {
applicationSection: ApplicationSectionT;
application: Pick<ApplicationT, "pk">;
}) {
const { t } = useTranslation();
const reservations = sectionToreservations(t, applicationSection);
Expand All @@ -778,7 +760,10 @@ export function AllReservations({
<H3 $noMargin>
{t("application:view.reservationsTab.reservationsTitle")}
</H3>
<ReservationsTable reservations={reservations} />
<ReservationsTable
reservations={reservations}
application={application}
/>
</>
);
}
Expand All @@ -804,7 +789,10 @@ export function ApplicationSection({
<H3>{t("application:view.reservationsTab.reservationUnitsTitle")}</H3>
<ReservationUnitTable reservationUnits={reservationUnits} />
<H3>{t("application:view.reservationsTab.reservationsTitle")}</H3>
<ReservationsTable reservations={reservations} />
<ReservationsTable
reservations={reservations}
application={application}
/>
<ButtonContainer $justifyContent="center">
<ButtonLikeLink
href={getApplicationSectionPath(
Expand Down
4 changes: 2 additions & 2 deletions apps/ui/components/application/Page1.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { getTranslation } from "@/modules/util";
import { useOptions } from "@/hooks/useOptions";
import { ApplicationEvent } from "./ApplicationEvent";
import { type ApplicationFormValues } from "./Form";
import useReservationUnitsList from "@/hooks/useReservationUnitList";
import { useReservationUnitList } from "@/hooks";
import { ButtonContainer } from "common/styles/util";

type Node = NonNullable<ApplicationQuery["application"]>;
Expand Down Expand Up @@ -52,7 +52,7 @@ export function Page1({ applicationRound, onNext }: Props): JSX.Element | null {
const { setValue, register, unregister, watch, handleSubmit } = form;
// get the user selected defaults for reservationUnits field
const { reservationUnits: selectedReservationUnits } =
useReservationUnitsList(applicationRound);
useReservationUnitList(applicationRound);

const applicationSections = watch("applicationSections");

Expand Down
6 changes: 3 additions & 3 deletions apps/ui/components/application/ViewApplication.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import { ApplicantInfoPreview } from "./ApplicantInfoPreview";
import {
ApplicationSection,
ApplicationSectionHeader,
CompactTermsBox,
StyledNotification,
TermsAccordion as Accordion,
} from "./styled";
import { ApplicationEventList } from "./ApplicationEventList";
import Sanitize from "../common/Sanitize";
import TermsBox from "common/src/termsbox/TermsBox";

type Node = NonNullable<ApplicationQuery["application"]>;
export function ViewApplication({
Expand Down Expand Up @@ -54,15 +54,15 @@ export function ViewApplication({
heading={t("reservationUnit:termsOfUse")}
open
>
<CompactTermsBox
<TermsBox
id="preview.acceptTermsOfUse"
body={<Sanitize html={getTranslation(tos, "text")} />}
/>
</Accordion>
)}
{tos2 && (
<Accordion heading={t("application:preview.reservationUnitTerms")} open>
<CompactTermsBox
<TermsBox
id="preview.acceptServiceSpecificTerms"
body={<Sanitize html={getTranslation(tos2, "text")} />}
/* TODO TermsBox has accepted and checkbox we could use but for now leaving the single
Expand Down
5 changes: 0 additions & 5 deletions apps/ui/components/application/styled.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import styled from "styled-components";
import { Notification } from "hds-react";
import TermsBox from "common/src/termsbox/TermsBox";
import { breakpoints, fontMedium, fontRegular } from "common";
import { AccordionWithState } from "@/components/Accordion";
import { H5 } from "common/src/common/typography";
Expand Down Expand Up @@ -168,10 +167,6 @@ export const TermsAccordion = styled(AccordionWithState)`
}
`;

export const CompactTermsBox = styled(TermsBox)`
margin-bottom: 0;
`;

export const SpanTwoColumns = styled(FullRow).attrs({ as: "span" })``;

export const FormSubHeading = styled(H5).attrs({ as: "h2" })`
Expand Down
5 changes: 3 additions & 2 deletions apps/ui/components/reservation/AcceptTerms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useTranslation } from "next-i18next";
import { getTranslation } from "@/modules/util";
import Sanitize from "../common/Sanitize";
import { type ReservationUnitPageQuery } from "@/gql/gql-types";
import { Flex } from "common/styles/util";

type ReservationUnitNodeT = NonNullable<
ReservationUnitPageQuery["reservationUnit"]
Expand Down Expand Up @@ -49,7 +50,7 @@ export function AcceptTerms({
);

return (
<>
<Flex>
<TermsBox
id="cancellation-and-payment-terms"
heading={paymentTermsHeading}
Expand Down Expand Up @@ -90,6 +91,6 @@ export function AcceptTerms({
accepted={isTermsAccepted.space}
setAccepted={(val) => setIsTermsAccepted("space", val)}
/>
</>
</Flex>
);
}
2 changes: 1 addition & 1 deletion apps/ui/components/reservation/EditStep0.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ export function EditStep0({
data-testid="reservation-edit__button--cancel"
>
<IconCross aria-hidden />
{t("reservations:cancelEditReservationTime")}
{t("reservations:cancelButton")}
</ButtonLikeLink>
<Button
variant="primary"
Expand Down
2 changes: 1 addition & 1 deletion apps/ui/components/reservation/EditStep1.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export function EditStep1({
data-testid="reservation-edit__button--cancel"
>
<IconCross aria-hidden />
{t("reservations:cancelEditReservationTime")}
{t("reservations:cancelButton")}
</ButtonLikeLink>
<Button
variant="primary"
Expand Down
Loading
Loading