Skip to content

Commit

Permalink
bookings countdown: extend bookings debug to allow for updates to ext…
Browse files Browse the repository at this point in the history
…eded date as well

* create BookingDateDebugController
* update the UI to accommodate for extended functionality
  • Loading branch information
ikusteu committed Mar 21, 2024
1 parent 1317491 commit 4c3cd69
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 114 deletions.
109 changes: 109 additions & 0 deletions packages/client/src/controllers/BookingDateDebugController.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React, { useState, useEffect } from "react";
import { DateTime } from "luxon";
import { useDispatch, useSelector } from "react-redux";

import { BookingDateDebugDialog as DebugDialog } from "@eisbuk/ui";
import { updateLocalDocuments } from "@eisbuk/react-redux-firebase-firestore";
import { OrgSubCollection } from "@eisbuk/shared";

import { db } from "@/setup";

import { getOrganization } from "@/lib/getters";

import {
getBookingsCustomer,
getMonthDeadline,
} from "@/store/selectors/bookings";
import {
getCalendarDay,
getSecretKey,
getSystemDate,
} from "@/store/selectors/app";
import { resetSystemDate, setSystemDate } from "@/store/actions/appActions";

import {
FirestoreVariant,
doc,
getBookingsDocPath,
getDoc,
} from "@/utils/firestore";

const BookingDateDebugDialog: React.FC = () => {
const secretKey = useSelector(getSecretKey) || "";
const currentDate = useSelector(getCalendarDay);

const dispatch = useDispatch();

const systemDateValue = useSelector(getSystemDate).value;
const systemDate = useDate({
value: systemDateValue,
onChange: (date) => dispatch(setSystemDate(date)),
// Reset the system date on unmount, as it could have only been used (if it had been used at all) for debugging of
// the current page
onDestroy: () => dispatch(resetSystemDate()),
});

const customer = useSelector(getBookingsCustomer(secretKey));
const extendedDateValue = DateTime.fromISO(
customer?.extendedDate || getMonthDeadline(currentDate).toISODate()
);
// On extended date change, we're not persisting the change, merely updating the local (redux) state
const handleExtendedDateChange = (date: DateTime) => {
const data = { ...customer, extendedDate: date.toISODate() };
dispatch(
updateLocalDocuments(OrgSubCollection.Bookings, { [secretKey]: data })
);
};
const resetBookingsCustomer = async () => {
const firestore = FirestoreVariant.client({ instance: db });
const docRef = doc(
firestore,
getBookingsDocPath(getOrganization(), secretKey)
);
const data = await getDoc(docRef).then((d) => d.data()!);
dispatch(
updateLocalDocuments(OrgSubCollection.Bookings, { [secretKey]: data })
);
};
const extendedDate = useDate({
value: extendedDateValue,
onChange: handleExtendedDateChange,
onDestroy: resetBookingsCustomer,
});

return <DebugDialog systemDate={systemDate} extendedDate={extendedDate} />;
};

type UseDateParams = {
value: DateTime;
onChange: (date: DateTime) => void;
onDestroy?: () => void;
};

const useDate = ({ value, onChange, onDestroy }: UseDateParams) => {
useEffect(() => {
return onDestroy;
}, []);

const navigate = (days: -1 | 1) => () => onChange(value.plus({ days }));

const [localDate, setLocalDate] = useState(value.toISODate());
useEffect(() => {
setLocalDate(value.toISODate());
}, [value]);
const handleChange = (_date: string) => {
if (isIsoDate(_date)) {
// If date is a valid ISO string, update the DateTime value
// Local value is updated as side effect
onChange(DateTime.fromISO(_date));
}
// Update the local value
setLocalDate(_date);
};

return { value: localDate, navigate, onChange: handleChange };
};

const isIsoDate = (date: string): boolean => /^\d{4}-\d{2}-\d{2}$/.test(date);

export default BookingDateDebugDialog;
65 changes: 38 additions & 27 deletions packages/client/src/pages/customer_area/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import React, { useState } from "react";
import { useSelector } from "react-redux";
import { Redirect } from "react-router-dom";
import i18n, { CustomerNavigationLabel } from "@eisbuk/translations";
import { CustomerNavigationLabel, useTranslation } from "@eisbuk/translations";

import { CalendarNav, DateDebug, LayoutContent, TabItem } from "@eisbuk/ui";
import {
Button,
ButtonColor,
CalendarNav,
LayoutContent,
TabItem,
} from "@eisbuk/ui";
import { Calendar, AccountCircle, ClipboardList } from "@eisbuk/svg";
import {
BookingSubCollection,
Expand All @@ -28,6 +34,7 @@ import useSecretKey from "@/hooks/useSecretKey";
import Layout from "@/controllers/Layout";
import PrivacyPolicyToast from "@/controllers/PrivacyPolicyToast";
import AthleteAvatar from "@/controllers/AthleteAvatar";
import BookingDateDebugDialog from "@/controllers/BookingDateDebugController";

import ErrorBoundary from "@/components/atoms/ErrorBoundary";

Expand All @@ -36,9 +43,6 @@ import {
getOtherBookingsAccounts,
} from "@/store/selectors/bookings";
import { getAllSecretKeys, getIsAdmin } from "@/store/selectors/auth";
import { getSystemDate } from "@/store/selectors/app";

import { resetSystemDate, setSystemDate } from "@/store/actions/appActions";

enum Views {
Book = "BookView",
Expand All @@ -57,20 +61,10 @@ const viewsLookup = {
* Customer area page component
*/
const CustomerArea: React.FC = () => {
const dispatch = useDispatch();

const secretKey = useSecretKey();
const isAdmin = useSelector(getIsAdmin);

const { value: systemDate } = useSelector(getSystemDate);

// Reset the system date on unmount, as it could have only been used (if it had been used at all) for debugging of
// the current page
useEffect(() => {
return () => {
dispatch(resetSystemDate());
};
}, []);
const { t } = useTranslation();

// We're providing a fallback [secretKey] as we have multiple ways of authenticating. If authenticating
// using firebase auth, the user will have all of their secret keys in the store (this is the preferred way).
Expand Down Expand Up @@ -118,26 +112,29 @@ const CustomerArea: React.FC = () => {
const [view, setView] = useState<keyof typeof viewsLookup>(Views.Book);
const CustomerView = viewsLookup[view];

const [debugOn, setDebugOn] = useState(false);
const toggleDebug = () => setDebugOn(!debugOn);

const additionalButtons = (
<>
<TabItem
key="book-view-button"
Icon={Calendar as any}
label={i18n.t(CustomerNavigationLabel.Book)}
label={t(CustomerNavigationLabel.Book)}
onClick={() => setView(Views.Book)}
active={view === Views.Book}
/>
<TabItem
key="calendar-view-button"
Icon={AccountCircle as any}
label={i18n.t(CustomerNavigationLabel.Calendar)}
label={t(CustomerNavigationLabel.Calendar)}
onClick={() => setView(Views.Calendar)}
active={view === Views.Calendar}
/>
<TabItem
key="profile-view-button"
Icon={ClipboardList as any}
label={i18n.t(CustomerNavigationLabel.Profile)}
label={t(CustomerNavigationLabel.Profile)}
onClick={() => setView(Views.Profile)}
active={view === Views.Profile}
/>
Expand All @@ -148,6 +145,19 @@ const CustomerArea: React.FC = () => {
return <Redirect to={`${Routes.Deleted}/${secretKey}`} />;
}

const debugButton = (
<Button
onClick={toggleDebug}
color={debugOn ? ButtonColor.Primary : undefined}
className={
!debugOn ? "!text-black outline outline-gray-300 border-box" : ""
}
// aria-label={t(SlotsAria.EnableEdit)}
>
Debug
</Button>
);

return (
<Layout
additionalButtons={additionalButtons}
Expand All @@ -167,17 +177,18 @@ const CustomerArea: React.FC = () => {
// additionalContent={<AddToCalendar />}
jump="month"
additionalContent={
isAdmin ? (
<DateDebug
value={systemDate}
onChange={(date) => dispatch(setSystemDate(date))}
/>
) : undefined
isAdmin && view === "BookView" ? debugButton : undefined
}
/>
)}
<LayoutContent>
<ErrorBoundary resetKeys={[calendarNavProps]}>
{debugOn && (
<div className="mt-4">
<BookingDateDebugDialog />
</div>
)}

<div className="px-[44px] py-4">
<CustomerView />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from "react";
import { ComponentMeta } from "@storybook/react";
import { DateTime } from "luxon";

import BookingDateDebugDialog from "./BookingDateDebugDialog";

export default {
title: "Booking Date Debug Dialog",
component: BookingDateDebugDialog,
} as ComponentMeta<typeof BookingDateDebugDialog>;

export const Interactive = (): JSX.Element => {
const [_systemDate, setSystemDate] = React.useState(
DateTime.now().toISODate()
);
const systemDate = {
value: _systemDate,
onChange: setSystemDate,
navigate: (delta: -1 | 1) => () =>
setSystemDate(
DateTime.fromISO(_systemDate).plus({ days: delta }).toISODate()
),
};

const [_extendedDate, setExtendedDate] = React.useState(
DateTime.now().toISODate()
);
const extendedDate = {
value: _extendedDate,
onChange: setExtendedDate,
navigate: (delta: -1 | 1) => () =>
setExtendedDate(
DateTime.fromISO(_extendedDate).plus({ days: delta }).toISODate()
),
};

return (
<BookingDateDebugDialog
systemDate={systemDate}
extendedDate={extendedDate}
/>
);
};
50 changes: 50 additions & 0 deletions packages/ui/src/BookingDateDebugDialog/BookingDateDebugDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";

import { DateNavigation } from "../CalendarNav";

type DateFieldProps = {
value: string;
navigate: (days: -1 | 1) => () => void;
onChange: (date: string) => void;
onDestroy?: () => void;
};

type Props = {
systemDate: DateFieldProps;
extendedDate: DateFieldProps;
};

const DateDebug: React.FC<Props> = ({ systemDate, extendedDate }) => (
<div className="flex items-center justify-between flex-wrap gap-4 rounded-lg p-4 border-2 border-gray-300">
<div className="flex items-center gap-4">
<span className="text-base font-semibold">System date:</span>
<DateNavigation
className="w-full md:w-[320px] border-gray-300 border-2 rounded"
onPrev={systemDate.navigate(-1)}
onNext={systemDate.navigate(1)}
>
<input
className="h-full w-full border-none text-center"
value={systemDate.value}
onChange={(e) => systemDate.onChange(e.target.value)}
/>
</DateNavigation>
</div>

<div className="flex items-center gap-4">
<span className="text-base font-semibold">Extended booking date:</span>
<DateNavigation
className="w-full md:w-[320px] border-gray-300 border-2 rounded"
onPrev={extendedDate.navigate(-1)}
onNext={extendedDate.navigate(1)}
>
<input
className="h-full w-full border-none text-center"
value={extendedDate.value}
onChange={(e) => extendedDate.onChange(e.target.value)}
/>
</DateNavigation>
</div>
</div>
);
export default DateDebug;
1 change: 1 addition & 0 deletions packages/ui/src/BookingDateDebugDialog/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./BookingDateDebugDialog";
14 changes: 0 additions & 14 deletions packages/ui/src/CalendarNav/CalendarNav.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { DateTime } from "luxon";

import CalendarNav from "./CalendarNav";
import EmptySpace from "../EmptySpace";
import DateDebug from "./DateDebug";

export default {
title: "Calendar Nav",
Expand Down Expand Up @@ -56,16 +55,3 @@ export const WithCountdown = (): JSX.Element => (
additionalContent={countdown}
/>
);

const DateDebugWrapper: React.FC = () => {
const [date, setDate] = React.useState(DateTime.now());

return <DateDebug value={date} onChange={setDate} />;
};
export const WithDateDebug = (): JSX.Element => (
<CalendarNav
date={DateTime.fromISO("2022-04-01")}
jump="month"
additionalContent={<DateDebugWrapper />}
/>
);
Loading

0 comments on commit 4c3cd69

Please sign in to comment.