Skip to content

Commit

Permalink
pulling admin banner from other apps
Browse files Browse the repository at this point in the history
  • Loading branch information
ailZhou committed Nov 12, 2024
1 parent 4a40ee6 commit 481f66d
Show file tree
Hide file tree
Showing 15 changed files with 378 additions and 3 deletions.
129 changes: 129 additions & 0 deletions services/ui-src/src/components/banners/AdminBannerProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { createContext, ReactNode, useMemo, useEffect } from "react";
// utils
import { AdminBannerData, AdminBannerShape } from "types/banners";
import { bannerId } from "../../constants";
import { bannerErrors } from "verbiage/errors";
// api
import {
checkDateRangeStatus,
deleteBanner,
getBanner,
useStore,
writeBanner,
} from "utils";

const ADMIN_BANNER_ID = bannerId;

export const AdminBannerContext = createContext<AdminBannerShape>({
fetchAdminBanner: Function,
writeAdminBanner: Function,
deleteAdminBanner: Function,
});

export const AdminBannerProvider = ({ children }: Props) => {
// state management
const {
bannerData,
setBannerData,
bannerActive,
setBannerActive,
bannerLoading,
setBannerLoading,
bannerErrorMessage,
setBannerErrorMessage,
bannerDeleting,
setBannerDeleting,
} = useStore();

const fetchAdminBanner = async () => {
setBannerLoading(true);
try {
const currentBanner = await getBanner(ADMIN_BANNER_ID);
const newBannerData = currentBanner as AdminBannerData | undefined;
setBannerData(newBannerData);
setBannerErrorMessage(undefined);
} catch (e: any) {
// 404 expected when no current banner exists
if (!e.toString().includes("404")) {
setBannerErrorMessage(bannerErrors.GET_BANNER_FAILED);
}
}
setBannerLoading(false);
};

const deleteAdminBanner = async () => {
setBannerDeleting(true);
try {
await deleteBanner(ADMIN_BANNER_ID);
await fetchAdminBanner();
} catch {
setBannerErrorMessage(bannerErrors.DELETE_BANNER_FAILED);
}
setBannerDeleting(false);
};

const writeAdminBanner = async (newBannerData: AdminBannerData) => {
try {
await writeBanner(newBannerData);
} catch {
setBannerErrorMessage(bannerErrors.CREATE_BANNER_FAILED);
}
await fetchAdminBanner();
};

useEffect(() => {
let bannerActivity = false;
if (bannerData) {
bannerActivity = checkDateRangeStatus(
bannerData.startDate,
bannerData.endDate
);
}
setBannerActive(bannerActivity);
}, [bannerData]);

useEffect(() => {
fetchAdminBanner();
}, []);

const providerValue = useMemo(
() => ({
// Banner Data
bannerData,
setBannerData,
// Banner showing
bannerActive,
setBannerActive,
// Banner Loading
bannerLoading,
setBannerLoading,
// Banner Error State
bannerErrorMessage,
setBannerErrorMessage,
// Banner Deleting State
bannerDeleting,
setBannerDeleting,
// Banner API calls
fetchAdminBanner,
writeAdminBanner,
deleteAdminBanner,
}),
[
bannerData,
bannerActive,
bannerLoading,
bannerErrorMessage,
bannerDeleting,
]
);

return (
<AdminBannerContext.Provider value={providerValue}>
{children}
</AdminBannerContext.Provider>
);
};

interface Props {
children?: ReactNode;
}
20 changes: 20 additions & 0 deletions services/ui-src/src/components/banners/Banner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// components
import { Alert } from "components";
// types
import { BannerData } from "types";

export const Banner = ({ bannerData, ...props }: Props) => {
if (bannerData) {
const { title, description, link } = bannerData;
return (
bannerData && (
<Alert title={title} description={description} link={link} {...props} />
)
);
} else return <></>;
};

interface Props {
bannerData: BannerData | undefined;
[key: string]: any;
}
22 changes: 22 additions & 0 deletions services/ui-src/src/components/banners/PreviewBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useFormContext } from "react-hook-form";
// components
import { Banner } from "components";

export const PreviewBanner = ({ ...props }: Props) => {
// get the form context
const form = useFormContext();

// set banner preview data
const formData = form.getValues();
const bannerData = {
title: formData["bannerTitle"] || "New banner title",
description: formData["bannerDescription"] || "New banner description",
link: formData["bannerLink"] || "",
};

return <Banner bannerData={bannerData} {...props} />;
};

interface Props {
[key: string]: any;
}
7 changes: 7 additions & 0 deletions services/ui-src/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ export { ErrorAlert } from "./alerts/ErrorAlert";
// app
export { App } from "./app/App";
export { Error } from "./app/Error";
// banners
export {
AdminBannerContext,
AdminBannerProvider,
} from "./banners/AdminBannerProvider";
export { Banner } from "./banners/Banner";
export { PreviewBanner } from "./banners/PreviewBanner";
// layout
export { HomePage } from "./layout/HomePage";
export { Header } from "./layout/Header";
Expand Down
3 changes: 3 additions & 0 deletions services/ui-src/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// BANNERS
export const bannerId = "admin-banner-id";

// HOST DOMAIN
export const PRODUCTION_HOST_DOMAIN = "mdcthcbs.cms.gov";

Expand Down
23 changes: 23 additions & 0 deletions services/ui-src/src/types/banners.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// BANNER

export interface BannerData {
title: string;
description: string;
link?: string;
[key: string]: any;
}

export interface AdminBannerData extends BannerData {
key: string;
startDate: number;
endDate: number;
isActive?: boolean;
}

export interface AdminBannerMethods {
fetchAdminBanner: Function;
writeAdminBanner: Function;
deleteAdminBanner: Function;
}

export interface AdminBannerShape extends AdminBannerMethods {}
1 change: 1 addition & 0 deletions services/ui-src/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./banners";
export * from "./users";
export * from "./states";
export * from "./other";
Expand Down
21 changes: 20 additions & 1 deletion services/ui-src/src/types/states.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
import { ParentPageTemplate, PageData, Report } from "types/report";
import React from "react";
import { HcbsUser } from "types";
import { AdminBannerData, ErrorVerbiage, HcbsUser } from "types";

// initial admin banner state
export interface AdminBannerState {
// INITIAL STATE
bannerData: AdminBannerData | undefined;
bannerActive: boolean;
bannerLoading: boolean;
bannerErrorMessage: ErrorVerbiage | undefined;
bannerDeleting: boolean;
// ACTIONS
setBannerData: (newBannerData: AdminBannerData | undefined) => void;
clearAdminBanner: () => void;
setBannerActive: (bannerStatus: boolean) => void;
setBannerLoading: (bannerLoading: boolean) => void;
setBannerErrorMessage: (
bannerErrorMessage: ErrorVerbiage | undefined
) => void;
setBannerDeleting: (bannerDeleting: boolean) => void;
}

// initial user state
export interface HcbsUserState {
Expand Down
31 changes: 31 additions & 0 deletions services/ui-src/src/utils/api/requestMethods/banner.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { getBanner, writeBanner, deleteBanner } from "./banner";
// utils
import { bannerId } from "../../../constants";
import { mockBannerData } from "utils/testing/setupJest";
import { initAuthManager } from "utils/auth/authLifecycle";

describe("utils/banner", () => {
beforeEach(async () => {
jest.useFakeTimers();
initAuthManager();
jest.runAllTimers();
});

describe("getBanner()", () => {
test("executes", () => {
expect(getBanner(bannerId)).toBeTruthy();
});
});

describe("writeBanner()", () => {
test("executes", () => {
expect(writeBanner(mockBannerData)).toBeTruthy();
});
});

describe("deleteBanner()", () => {
test("executes", () => {
expect(deleteBanner(bannerId)).toBeTruthy();
});
});
});
40 changes: 40 additions & 0 deletions services/ui-src/src/utils/api/requestMethods/banner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { API } from "aws-amplify";
import { getRequestHeaders } from "./getRequestHeaders";
import { AdminBannerData } from "types/banners";
import { updateTimeout } from "utils";

async function getBanner(bannerKey: string) {
const requestHeaders = await getRequestHeaders();
const request = {
headers: { ...requestHeaders },
};

updateTimeout();
const response = await API.get("mfp", `/banners/${bannerKey}`, request);
return response;
}

async function writeBanner(bannerData: AdminBannerData) {
const requestHeaders = await getRequestHeaders();
const request = {
headers: { ...requestHeaders },
body: bannerData,
};

updateTimeout();
const response = await API.post("mfp", `/banners/${bannerData.key}`, request);
return response;
}

async function deleteBanner(bannerKey: string) {
const requestHeaders = await getRequestHeaders();
const request = {
headers: { ...requestHeaders },
};

updateTimeout();
const response = await API.del("mfp", `/banners/${bannerKey}`, request);
return response;
}

export { getBanner, writeBanner, deleteBanner };
1 change: 1 addition & 0 deletions services/ui-src/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
export * from "./api/apiLib";
export * from "./api/providers/ApiProvider";
export * from "./api/requestMethods/getTemplateUrl";
export * from "./api/requestMethods/banner";
// auth
export * from "./auth/UserProvider";
export * from "./auth/authLifecycle";
Expand Down
34 changes: 33 additions & 1 deletion services/ui-src/src/utils/state/useStore.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
import { HcbsUserState, HcbsUser, HcbsReportState } from "types";
import { HcbsUserState, HcbsUser, HcbsReportState, AdminBannerData, ErrorVerbiage } from "types";
import { Report } from "types/report";
import React from "react";
import { buildState, mergeAnswers, setPage } from "./management/reportState";
Expand All @@ -19,6 +19,37 @@ const userStore = (set: Function) => ({
set(() => ({ showLocalLogins: true }), false, { type: "showLocalLogins" }),
});

// BANNER STORE
const bannerStore = (set: Function) => ({
// initial state
bannerData: undefined,
bannerActive: false,
bannerLoading: false,
bannerErrorMessage: undefined,
bannerDeleting: false,
// actions
setBannerData: (newBanner: AdminBannerData | undefined) =>
set(() => ({ bannerData: newBanner }), false, { type: "setBannerData" }),
clearAdminBanner: () =>
set(() => ({ bannerData: undefined }), false, { type: "clearAdminBanner" }),
setBannerActive: (bannerStatus: boolean) =>
set(() => ({ bannerActive: bannerStatus }), false, {
type: "setBannerActive",
}),
setBannerLoading: (loading: boolean) =>
set(() => ({ bannerLoading: loading }), false, {
type: "setBannerLoading",
}),
setBannerErrorMessage: (errorMessage: ErrorVerbiage | undefined) =>
set(() => ({ bannerErrorMessage: errorMessage }), false, {
type: "setBannerErrorMessage",
}),
setBannerDeleting: (deleting: boolean) =>
set(() => ({ bannerDeleting: deleting }), false, {
type: "setBannerDeleting",
}),
});

// REPORT STORE
const reportStore = (set: Function): HcbsReportState => ({
// initial state
Expand Down Expand Up @@ -56,6 +87,7 @@ export const useStore = create(
persist(
devtools<HcbsUserState & HcbsReportState>((set) => ({
...userStore(set),
...bannerStore(set),
...reportStore(set),
})),
{
Expand Down
9 changes: 9 additions & 0 deletions services/ui-src/src/utils/testing/mockBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { bannerId } from "../../constants";

export const mockBannerData = {
key: bannerId,
title: "Yes here I am, a banner",
description: "I have a description too thank you very much",
startDate: 1640995200000, // 1/1/2022 00:00:00 UTC
endDate: 1672531199000, // 12/31/2022 23:59:59 UTC
};
Loading

0 comments on commit 481f66d

Please sign in to comment.