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

[FE]: Add an intro modal #202

Merged
merged 3 commits into from
Dec 26, 2024
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: 2 additions & 0 deletions client/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { NuqsAdapter } from "nuqs/adapters/next/app";

import { config } from "@/app/auth/api/[...nextauth]/config";

import IntroModal from "@/containers/intro-modal";
import MainNav from "@/containers/nav";

import { SidebarProvider } from "@/components/ui/sidebar";
Expand Down Expand Up @@ -57,6 +58,7 @@ export default async function RootLayout({
<body className={inter.className}>
<SidebarProvider>
<MainNav />
<IntroModal />
<main className="mx-3 flex h-dvh flex-1">{children}</main>
</SidebarProvider>
<Toaster />
Expand Down
112 changes: 112 additions & 0 deletions client/src/containers/intro-modal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"use client";
import { useCallback, useState } from "react";

import {
ClipboardPenIcon,
FileQuestionIcon,
LayoutDashboardIcon,
UserIcon,
} from "lucide-react";

import { introModalManager } from "@/lib/utils";

import { Button } from "@/components/ui/button";
import { CheckboxWrapper } from "@/components/ui/checkbox";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";

const introItems = [
{
title: "Project Overview",
description:
"Explore and compare project scenarios with ease. Apply filters for location, ecosystem type, activity, cost, abatement potential, and more. The global map visualizes project distributions, enabling comparisons based on cost-to-abatement ratios. Additionally, use the comparison table for detailed cost and score analyses, and select a project for additional details.",
icon: LayoutDashboardIcon,
},
{
title: "Create custom projects",
description:
"Design custom scenarios by modifying original parameters and exploring the resulting data in detail. Save your projects to revisit them anytime and quickly compare all the scenarios you’ve created.",
icon: ClipboardPenIcon,
},
{
title: "Methodology",
description:
"Explore for detailed information on assumptions, estimations, and data sources.",
icon: FileQuestionIcon,
},
{
title: "User area",
description: "Access information about your personal account.",
icon: UserIcon,
},
];

const IntroModal = () => {
const [isOpen, setIsOpen] = useState(introModalManager.showIntroModal());
const [dontShowAgain, setDontShowAgain] = useState(false);
const handleClose = useCallback(
(open = false) => {
setIsOpen(open);

if (dontShowAgain) {
introModalManager.setHideIntroModal();
}
},
[dontShowAgain],
);

return (
<Dialog open={isOpen} onOpenChange={handleClose}>
<DialogContent>
<DialogHeader className="space-y-5">
<DialogTitle className="text-xl font-semibold">
Welcome to Blue Carbon Cost Tool
</DialogTitle>
<DialogDescription className="text-base font-normal text-foreground">
The Blue Carbon Cost Tool estimates project costs and carbon
benefits of Blue Carbon Market projects, providing a high-level view
for comparisons and prioritization among different project
scenarios.
</DialogDescription>
</DialogHeader>
<ul className="mb-6 space-y-4">
{introItems.map((item) => (
<li key={item.title}>
<div className="flex items-start gap-4 pt-2">
<div className="rounded-full bg-sidebar-accent p-2">
<item.icon
className="size-5 text-big-stone-50"
strokeWidth={1}
/>
</div>
<div className="space-y-2.5">
<h3 className="text-base font-bold">{item.title}</h3>
<p className="text-sm font-normal text-foreground">
{item.description}
</p>
</div>
</div>
</li>
))}
</ul>
<DialogFooter className="flex-col gap-4 sm:items-center sm:justify-between">
<CheckboxWrapper
id="dontShowAgain"
label="Don't show this introduction again."
checked={dontShowAgain}
onCheckedChange={(checked) => setDontShowAgain(checked === true)}
/>
<Button onClick={() => handleClose()}>Discover</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};

export default IntroModal;
24 changes: 24 additions & 0 deletions client/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,27 @@ export const parseTableData = <
value: formatCurrency(data[key as K], { maximumFractionDigits: 0 }),
}));
};
class IntroModalManager {
private static instance: IntroModalManager;
private isServer: boolean = typeof window === "undefined";

public static getInstance(): IntroModalManager {
if (!IntroModalManager.instance) {
IntroModalManager.instance = new IntroModalManager();
}
return IntroModalManager.instance;
}

public showIntroModal(): boolean {
if (this.isServer) return false;
return (
!!process.env.HIDE_INTRO_MODAL || !localStorage.getItem("hideIntroModal")
);
}

public setHideIntroModal(): void {
if (this.isServer) return;
localStorage.setItem("hideIntroModal", "true");
}
}
export const introModalManager = IntroModalManager.getInstance();
16 changes: 16 additions & 0 deletions e2e/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,22 @@ export default defineConfig({
baseURL: APP_URL,
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
storageState: {
cookies: [],
origins: [
{
origin: APP_URL,
localStorage: [
// Prevent intro modal from always showing during tests
// TODO: Write a separate test to check if the intro modal is shown
{
name: "hideIntroModal",
value: "true",
},
],
},
],
},
},
/* Configure projects for major browsers */
projects: [
Expand Down
Loading