diff --git a/frontend/public/screenshots/train.png b/frontend/public/screenshots/train.png new file mode 100644 index 0000000..9e60582 Binary files /dev/null and b/frontend/public/screenshots/train.png differ diff --git a/frontend/src/components/drawer/WelcomeLetterOverlay.tsx b/frontend/src/components/drawer/WelcomeLetterOverlay.tsx index a45d2b2..5b59420 100644 --- a/frontend/src/components/drawer/WelcomeLetterOverlay.tsx +++ b/frontend/src/components/drawer/WelcomeLetterOverlay.tsx @@ -1,6 +1,6 @@ import { AnimatePresence, motion } from "framer-motion"; import { useEffect, useRef, useState } from "react"; -import { getWelcomeContent } from "../../config/welcomeLetter"; +import { getWelcomeLetterContent } from "../../config/welcomeLetter"; import { formatDate } from "../../utils/dateFormat"; import { type CanvasTools, ComposeCanvas } from "../editor/ComposeCanvas"; import { EnvelopeReveal } from "../reader/EnvelopeReveal"; @@ -21,7 +21,7 @@ export function WelcomeLetterOverlay({ useEffect(() => { if (revealState === "REVEALED" && canvasRef.current) { - const welcomeContent = getWelcomeContent(userName); + const welcomeContent = getWelcomeLetterContent(userName); canvasRef.current.loadData(welcomeContent); } }, [revealState, userName]); diff --git a/frontend/src/pages/Drawer.test.tsx b/frontend/src/pages/Drawer.test.tsx index 05feade..206ec94 100644 --- a/frontend/src/pages/Drawer.test.tsx +++ b/frontend/src/pages/Drawer.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from "@testing-library/react"; +import { fireEvent, render, screen } from "@testing-library/react"; import { MemoryRouter } from "react-router-dom"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { mockUser } from "../../test/fixtures/user.fixture"; @@ -7,77 +7,117 @@ import { useAuthStore } from "../store/useAuthStore"; import Drawer from "./Drawer"; vi.mock("../hooks/useLetters"); +vi.mock("../components/drawer/WelcomeLetterOverlay", () => ({ + WelcomeLetterOverlay: ({ onComplete }: any) => ( +
+ +
+ ), +})); describe("Drawer Page", () => { - beforeEach(() => { - // Setup authenticated state for the test - useAuthStore.setState({ - user: mockUser, - accessToken: "fake-token", - isInitializing: false, - }); - - vi.mocked(useLetters).mockReturnValue({ - drafts: [], - kept: [], - sent: [], - vault: [], - loading: false, - isAuthRequired: false, - }); + beforeEach(() => { + // Setup authenticated state for the test + useAuthStore.setState({ + user: mockUser, + accessToken: "fake-token", + isInitializing: false, }); - it("renders the cabinet sections and empty state message", () => { - render( - - - , - ); + vi.mocked(useLetters).mockReturnValue({ + drafts: [], + kept: [], + sent: [], + vault: [], + loading: false, + isAuthRequired: false, + }); + }); - expect(screen.getByText(/Drafts/i)).toBeInTheDocument(); - expect(screen.getAllByText(/Kept/i).length).toBeGreaterThanOrEqual(1); - expect(screen.getByText(/Vault/i)).toBeInTheDocument(); - expect(screen.getByText(/This drawer remains silent/i)).toBeInTheDocument(); + it("renders the cabinet sections and empty state message", () => { + render( + + + , + ); + + expect(screen.getByText(/Drafts/i)).toBeInTheDocument(); + expect(screen.getAllByText(/Kept/i).length).toBeGreaterThanOrEqual(1); + expect(screen.getByText(/Vault/i)).toBeInTheDocument(); + expect(screen.getByText(/This drawer remains silent/i)).toBeInTheDocument(); + }); + + it("renders the loading state", () => { + vi.mocked(useLetters).mockReturnValue({ + drafts: [], + kept: [], + sent: [], + vault: [], + loading: true, + isAuthRequired: false, }); - it("renders the loading state", () => { - vi.mocked(useLetters).mockReturnValue({ - drafts: [], - kept: [], - sent: [], - vault: [], - loading: true, - isAuthRequired: false, - }); + render( + + + , + ); - render( - - - , - ); + expect(screen.getByText(/Opening your cabinet/i)).toBeInTheDocument(); + }); - expect(screen.getByText(/Opening your cabinet/i)).toBeInTheDocument(); + it("renders the authentication required modal when api requires auth", () => { + vi.mocked(useLetters).mockReturnValue({ + drafts: [], + kept: [], + sent: [], + vault: [], + loading: false, + isAuthRequired: true, }); - it("renders the authentication required modal when api requires auth", () => { - vi.mocked(useLetters).mockReturnValue({ - drafts: [], - kept: [], - sent: [], - vault: [], - loading: false, - isAuthRequired: true, - }); + render( + + + , + ); - render( - - - , - ); + expect(screen.getByText(/You've been away a while./i)).toBeInTheDocument(); + expect(screen.getByPlaceholderText(/password/i)).toBeInTheDocument(); + }); - expect( - screen.getByText(/You've been away a while./i), - ).toBeInTheDocument(); - expect(screen.getByPlaceholderText(/password/i)).toBeInTheDocument(); - }); + it("renders the welcome letter when firstTime state is present", () => { + render( + + + , + ); + + expect(screen.getByTestId("welcome-letter-overlay")).toBeInTheDocument(); + }); + + it("renders the drawer content when the letter is closed", () => { + render( + + + , + ); + + const completeButton = screen.getByTestId("overlay-exit-button"); + fireEvent.click(completeButton); + + expect( + screen.queryByTestId("welcome-letter-overlay"), + ).not.toBeInTheDocument(); + }); }); diff --git a/frontend/src/pages/Drawer.tsx b/frontend/src/pages/Drawer.tsx index 5fe14f4..b21f941 100644 --- a/frontend/src/pages/Drawer.tsx +++ b/frontend/src/pages/Drawer.tsx @@ -1,9 +1,10 @@ import { FeatherIcon } from "@phosphor-icons/react"; import { useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { useLocation, useNavigate } from "react-router-dom"; import { DrawerSection } from "../components/drawer/DrawerSection.tsx"; import { LetterItem } from "../components/drawer/LetterItem.tsx"; import { PasskeyModal } from "../components/drawer/PasskeyModal.tsx"; +import { WelcomeLetterOverlay } from "../components/drawer/WelcomeLetterOverlay.tsx"; import Logo from "../components/Logo"; import Saajan from "../components/ui/Saajan.tsx"; import { PATHS } from "../config/routes"; @@ -19,6 +20,10 @@ export default function Drawer() { const [openSection, setOpenSection] = useState(null); const navigate = useNavigate(); + const location = useLocation(); + const [showWelcomeLetter, setShowWelcomeLetter] = useState( + !!location.state?.firstTime, + ); const { drafts, kept, sent, vault, loading, isAuthRequired } = useLetters(); if (!user) return null; @@ -30,6 +35,16 @@ export default function Drawer() {
+ {showWelcomeLetter && ( + { + setShowWelcomeLetter(false); + navigate(location.pathname, { replace: true, state: {} }); + }} + /> + )} + {isAuthRequired && }
@@ -166,12 +181,14 @@ export default function Drawer() {
For your unsaid.
-
- -
+ {!showWelcomeLetter && ( +
+ +
+ )}
); }