Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin' into feature/single-account-mul…
Browse files Browse the repository at this point in the history
…tiple-athletes
  • Loading branch information
ikusteu committed Jan 27, 2024
2 parents 48bddcb + ff8410e commit c677149
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 40 deletions.
9 changes: 7 additions & 2 deletions packages/client/src/AppContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { __isDevStrict__ } from "@/lib/constants";
import { getOrganization } from "@/lib/getters";

import PrivateRoute from "@/components/auth/PrivateRoute";
import Deleted from "@/components/auth/Deleted";
import LoginRoute from "@/components/auth/LoginRoute";

import AttendancePage from "@/pages/attendance";
Expand All @@ -32,6 +31,7 @@ import DebugPage from "@/pages/debug";
import AdminPreferencesPage from "@/pages/admin_preferences";
import SelfRegister from "@/pages/self_register";
import PrivacyPolicy from "@/pages/privacy_policy";
import Deleted from "@/pages/deleted";

import { getIsAdmin } from "@/store/selectors/auth";

Expand Down Expand Up @@ -124,7 +124,12 @@ const AppContent: React.FC = () => {
/>
<Route path={Routes.SelfRegister} component={SelfRegister} exact />
<PrivateRoute path={Routes.Debug} component={DebugPage} />
<Route path={Routes.Deleted} component={Deleted} />
<Route
exact
path={Routes.Deleted}
component={() => <Redirect to="/" />}
/>
<Route path={`${Routes.Deleted}/:secretKey`} component={Deleted} />
<Route path={Routes.PrivacyPolicy} component={PrivacyPolicy} />
</Switch>

Expand Down
31 changes: 31 additions & 0 deletions packages/client/src/hooks/useSecretKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { useParams } from "react-router-dom";

import { removeSecretKey, storeSecretKey } from "@/store/actions/appActions";

/**
* Secret key logic abstracted away in a hook for easier readability
*/
const useSecretKey = (): string => {
// Secret key is provided as a route param to the customer_area page
const { secretKey } = useParams<{
secretKey: string;
}>();
const dispatch = useDispatch();

// Store secretKey to redux store
// for easier access
useEffect(() => {
dispatch(storeSecretKey(secretKey));

return () => {
// remove secretKey from local storage on unmount
dispatch(removeSecretKey);
};
}, [secretKey]);

return secretKey;
};

export default useSecretKey;
32 changes: 1 addition & 31 deletions packages/client/src/pages/customer_area/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,10 @@
import { useEffect } from "react";
import { DateTime } from "luxon";
import { useParams } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";

import { CalendarNavProps } from "@eisbuk/ui";

import { getCalendarDay } from "@/store/selectors/app";
import {
changeCalendarDate,
removeSecretKey,
storeSecretKey,
} from "@/store/actions/appActions";

/**
* Secret key logic abstracted away in a hook for easier readability
*/
export const useSecretKey = (): string => {
// Secret key is provided as a route param to the customer_area page
const { secretKey } = useParams<{
secretKey: string;
}>();
const dispatch = useDispatch();

// Store secretKey to redux store
// for easier access
useEffect(() => {
dispatch(storeSecretKey(secretKey));

return () => {
// remove secretKey from local storage on unmount
dispatch(removeSecretKey);
};
}, [secretKey]);

return secretKey;
};
import { changeCalendarDate } from "@/store/actions/appActions";

/**
* Date logic abstracted away in a hook for readability.
Expand Down
6 changes: 4 additions & 2 deletions packages/client/src/pages/customer_area/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import { getOrganization } from "@/lib/getters";
import BookView from "./views/Book";
import CalendarView from "./views/Calendar";
import ProfileView from "./views/Profile";
import { useSecretKey, useDate } from "./hooks";

import { useDate } from "./hooks";
import useSecretKey from "@/hooks/useSecretKey";

import Layout from "@/controllers/Layout";
import PrivacyPolicyToast from "@/controllers/PrivacyPolicyToast";
Expand Down Expand Up @@ -127,7 +129,7 @@ const CustomerArea: React.FC = () => {
);

if (secretKey && currentAthlete.deleted) {
return <Redirect to={Routes.Deleted} />;
return <Redirect to={`${Routes.Deleted}/${secretKey}`} />;
}

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import React from "react";
import { useSelector } from "react-redux";
import _ from "lodash";
import { Redirect } from "react-router-dom";

import { useTranslation, AuthMessage } from "@eisbuk/translations";
import { useFirestoreSubscribe } from "@eisbuk/react-redux-firebase-firestore";
import { OrgSubCollection } from "@eisbuk/shared";

import figureSkatingSilhouetteCouple from "@/assets/images/login/figure-skating-silhouette-couple.svg";
import figureSkatingSilhouetteSkirt from "@/assets/images/login/figure-skating-silhouette-skirt.svg";
Expand All @@ -11,6 +14,12 @@ import girlIceSkating from "@/assets/images/login/girl-ice-skating-silhouette.sv
import iceSkatingSilhouette from "@/assets/images/login/ice-skating-silhouette.svg";

import { getOrgEmail } from "@/store/selectors/orgInfo";
import { getBookingsCustomer } from "@/store/selectors/bookings";

import useSecretKey from "@/hooks/useSecretKey";

import { getOrganization } from "@/lib/getters";
import { Routes } from "@eisbuk/shared/ui";

// #region backgroundImages
const backgrounds = [
Expand Down Expand Up @@ -43,8 +52,25 @@ interface Props {
* @returns
*/
const Deleted: React.FC<Props> = ({ backgroundIndex }: Props) => {
const { t } = useTranslation();

const adminEmail = useSelector(getOrgEmail)!;

const secretKey = useSecretKey();
useFirestoreSubscribe(getOrganization(), [
{
collection: OrgSubCollection.Bookings,
meta: { secretKeys: [secretKey] },
},
]);
const customer = useSelector(getBookingsCustomer(secretKey));

// If customer's not deleted - you shouldn't be here
// If there's no customer - the customer is either not yet loaded, or not found - both are valid reasons to stick around
if (customer && !customer.deleted) {
return <Redirect to={[Routes.CustomerArea, secretKey].join("/")} />;
}

const background = _.isNil(backgroundIndex)
? _.sample(backgrounds)
: backgrounds[backgroundIndex % backgrounds.length];
Expand All @@ -53,7 +79,6 @@ const Deleted: React.FC<Props> = ({ backgroundIndex }: Props) => {
...baseStyle,
backgroundImage: `url(${background})`,
};
const { t } = useTranslation();

return (
<div className="content-container" style={style}>
Expand Down
29 changes: 25 additions & 4 deletions packages/e2e/integration/athlete_deleted_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,40 @@ import { customers } from "../__testData__/customers.json";

const saul = customers.saul as Customer;

describe("Deleted athlete redirect", () => {
beforeEach(() => {
describe("Deleted athlete redirects", () => {
it("Navigates to deleted athlete customer_area and redirects to deleted page", () => {
cy.initAdminApp().then((organization) =>
cy.updateCustomers(organization, {
customers: { ...saul, deleted: true },
} as Record<string, Customer>)
);
});

it("Navigates to deleted athlete customer_area and redirects to deleted page", () => {
cy.visit([Routes.CustomerArea, saul.secretKey].join("/"));
cy.url().should("include", "deleted");

cy.contains("[email protected]");
});

it("Redirects to appropriate customer area page if the athlete is not deleted", () => {
cy.initAdminApp()
.then((organization) =>
cy.updateCustomers(organization, {
customers: { ...saul, deleted: false },
} as Record<string, Customer>)
)
// We want the unauthenticated context for this one
.then(() => cy.visit([Routes.Deleted, saul.secretKey].join("/")));

cy.url().should("include", "customer_area");
cy.url().should("include", saul.secretKey);
});

it("Redirects to the login route if no secret key and user not authenticated", () => {
// We're testing with unauthenticated context -> default route is /login
cy.initAdminApp()
.then(() => cy.signOut())
.then(() => cy.visit(Routes.Deleted));

cy.url().should("include", "login");
});
});

0 comments on commit c677149

Please sign in to comment.