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

597: Accept or Decline Cookies #635

Merged
Merged
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
2 changes: 1 addition & 1 deletion src/app/(content-pages)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const ContentPagesLayout = ({ children }: { children: React.ReactNode }) => {
<Header />
<main id="main">
<div className="max-w-[68.75rem] mx-auto pb-[30px] pt-[60px] w-[90%]">
{children}
{children}
</div>
</main>
<Footer />
Expand Down
11 changes: 11 additions & 0 deletions src/app/CookieProviderWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"use client";

import { CookieProvider } from "@/context/CookieContext";

export const CookieProviderWrapper = ({
children,
}: {
children: React.ReactNode;
}) => {
return <CookieProvider>{children}</CookieProvider>;
};
2 changes: 2 additions & 0 deletions src/app/find-properties/[[...opa_id]]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Header, Hotjar } from "@/components";
import CookieConsentBanner from "@/components/CookieConsentBanner";

export const metadata = {
title: "Find Properties",
Expand All @@ -10,6 +11,7 @@ const MapLayout = ({ children }: { children: React.ReactNode }) => {
<Header />
<main id="main">{children}</main>
<Hotjar />
<CookieConsentBanner />
</div>
);
};
Expand Down
3 changes: 2 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Metadata } from "next";
import "./globals.css";
import { CookieProviderWrapper } from "./CookieProviderWrapper";

export const metadata: Metadata = {
title: {
Expand Down Expand Up @@ -28,7 +29,7 @@ export default function RootLayout({
>
Skip to main content
</a>
{children}
<CookieProviderWrapper>{children}</CookieProviderWrapper>
</body>
</html>
);
Expand Down
53 changes: 53 additions & 0 deletions src/components/CookieConsentBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"use client";

import { ThemeButton } from "./ThemeButton";
import { Check, X } from "@phosphor-icons/react";
import { useCookieContext } from "@/context/CookieContext";

const CookieConsentBanner = () => {
const { shouldShowBanner, setShouldAllowCookies, setShouldShowBanner } =
useCookieContext();

const onClickButton = (shouldSaveCookies: boolean) => {
setShouldAllowCookies(shouldSaveCookies);
setShouldShowBanner(false);
};

return (
<div
className={`${
shouldShowBanner
? "md:flex fixed inset-x-0 bottom-0 z-20 justify-between bg-blue-200 p-4 sm:p-6"
: "hidden"
}`}
>
<div>
<div className="heading-md">Can we store cookies to your browser?</div>
<div className="body-sm">
This provides you a nice experience preserving your filtering, your
position on the map and your property saved list.
</div>
</div>

<div className="flex justify-end gap-2 pt-4 sm:pt-0">
<div className="flex flex-none items-center gap-x-2">
<ThemeButton
color="tertiary"
label="Decline"
startContent={<X />}
onPress={() => onClickButton(false)}
/>

<ThemeButton
color="primary"
label="Accept"
startContent={<Check />}
onPress={() => onClickButton(true)}
/>
</div>
</div>
</div>
);
};

export default CookieConsentBanner;
91 changes: 61 additions & 30 deletions src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,63 @@
const Footer = () => (
<div className="flex flex-col">
<footer className="px-6 h-16 flex flex-grow w-full items-center">
<nav className="w-full" aria-label="content info">
<ul className="flex flex-wrap gap-x-4 justify-between items-center w-full backdrop-saturate-150 bg-background/70">
<li>
<span className="base-sm text-gray-600">
© 2024 Clean & Green Philly
</span>
</li>

<li className="base-sm underline text-gray-600 mx-auto">
<a href="/legal-disclaimer" className="hover:text-gray-800">
Legal Disclaimer
</a>
</li>

<li>
<a
href="mailto:[email protected]"
className="base-sm underline text-gray-600 hover:text-gray-800"
>
Contact Us
</a>
</li>
</ul>
</nav>
</footer>
</div>
);
"use client";

import { useCookieContext } from "@/context/CookieContext";
import CookieConsentBanner from "./CookieConsentBanner";
import Link from "next/link";

const Footer = () => {
let { setShouldShowBanner } = useCookieContext();

const onClickCookieSettings = () => {
setShouldShowBanner(true);
};

return (
<div className="flex flex-col">
<footer className="px-6 h-16 flex flex-grow justify-center items-center">
<nav aria-label="content info">
<ul className="flex flex-wrap gap-x-2 justify-between items-center w-full backdrop-saturate-150 bg-background/70">
<li>
<span className="base-sm text-gray-600">
© 2024 Clean & Green Philly
</span>
</li>

<span className="max-sm:hidden">—</span>

<li className="base-sm underline text-gray-600 mx-auto">
<a
className="hover:text-gray-800 cursor-pointer"
onClick={onClickCookieSettings}
>
Cookie Settings
</a>
</li>

<span className="max-sm:hidden">—</span>

<li className="base-sm underline text-gray-600 mx-auto">
<Link href="/legal-disclaimer" className="hover:text-gray-800">
Legal Disclaimer
</Link>
</li>

<span className="max-sm:hidden">—</span>

<li>
<a
href="mailto:[email protected]"
className="base-sm underline text-gray-600 hover:text-gray-800"
>
Contact Us
</a>
</li>
</ul>
</nav>

<CookieConsentBanner />
</footer>
</div>
);
};

export default Footer;
3 changes: 2 additions & 1 deletion src/components/IconLink.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"use client";

import { Button, Link, NavbarItem } from "@nextui-org/react";
import { Button, NavbarItem } from "@nextui-org/react";
import { usePathname } from "next/navigation";
import Link from "next/link";

interface IconLinkProps {
icon: React.ReactElement;
Expand Down
43 changes: 28 additions & 15 deletions src/components/SinglePropertyDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import ContentCard from "./ContentCard";
import cleanup from "@/images/transform-a-property.png";
import { PiEyeSlash } from "react-icons/pi";
import { useFilter } from "@/context/FilterContext";
import { useCookieContext } from "@/context/CookieContext";
import { getPropertyIdsFromLocalStorage } from "@/utilities/localStorage";

interface PropertyDetailProps {
Expand Down Expand Up @@ -61,6 +62,7 @@ const SinglePropertyDetail = ({
const [hover, setHover] = useState<boolean>(false);
const [isPropertySavedToLocalStorage, setIsPropertySavedToLocalStorage] =
useState(false);
let { shouldAllowCookies, setShouldShowBanner } = useCookieContext();

useEffect(() => {
if (!localStorage.getItem("opa_ids")) {
Expand Down Expand Up @@ -139,6 +141,14 @@ const SinglePropertyDetail = ({
};

const onClickSaveButton = () => {
if (shouldAllowCookies) {
findPropertyIdInLocalStorage();
} else {
setShouldShowBanner(true);
}
};

const findPropertyIdInLocalStorage = () => {
const localStorageData = localStorage.getItem("opa_ids");
const parsedLocalStorageData = localStorageData
? JSON.parse(localStorageData)
Expand All @@ -147,27 +157,30 @@ const SinglePropertyDetail = ({
if (parsedLocalStorageData.opa_ids[OPA_ID]) {
removePropertyIdFromLocalStorage(parsedLocalStorageData);
setIsPropertySavedToLocalStorage(false);
dispatchFilterAction(parsedLocalStorageData);
} else {
savePropertyIdToLocalStorage(parsedLocalStorageData);
setIsPropertySavedToLocalStorage(true);
}
};

if (parsedLocalStorageData.count === 0) {
const dispatchFilterAction = (data: any) => {
if (data.count === 0) {
dispatch({
type: "SET_DIMENSIONS",
property: "OPA_ID",
dimensions: [],
});
setShouldFilterSavedProperties(false);
} else {
if (shouldFilterSavedProperties) {
let propertyIds = getPropertyIdsFromLocalStorage();
dispatch({
type: "SET_DIMENSIONS",
property: "OPA_ID",
dimensions: [],
dimensions: [...propertyIds],
});
setShouldFilterSavedProperties(false);
} else {
if (shouldFilterSavedProperties) {
let propertyIds = getPropertyIdsFromLocalStorage();
dispatch({
type: "SET_DIMENSIONS",
property: "OPA_ID",
dimensions: [...propertyIds],
});
}
}
} else {
savePropertyIdToLocalStorage(parsedLocalStorageData);
setIsPropertySavedToLocalStorage(true);
}
};

Expand Down
48 changes: 48 additions & 0 deletions src/context/CookieContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, {
FC,
createContext,
useContext,
useState,
ReactNode,
} from "react";

interface CookieContextProps {
shouldAllowCookies: boolean;
shouldShowBanner: boolean;
setShouldAllowCookies: React.Dispatch<React.SetStateAction<boolean>>;
setShouldShowBanner: React.Dispatch<React.SetStateAction<boolean>>;
}

interface CookieProviderProps {
children: ReactNode;
}

export const CookieContext = createContext<CookieContextProps | undefined>(
undefined
);

export const useCookieContext = () => {
const context = useContext(CookieContext);
if (!context) {
throw new Error("useCookieContext must be used within a CookieProvider");
}
return context;
};

export const CookieProvider: FC<CookieProviderProps> = ({ children }) => {
const [shouldAllowCookies, setShouldAllowCookies] = useState(false);
const [shouldShowBanner, setShouldShowBanner] = useState(true);

return (
<CookieContext.Provider
value={{
shouldAllowCookies,
shouldShowBanner,
setShouldAllowCookies,
setShouldShowBanner,
}}
>
{children}
</CookieContext.Provider>
);
};