diff --git a/frontend/src/components/drawer/DrawerSection.tsx b/frontend/src/components/drawer/DrawerSection.tsx
index 7fe6ff0..14874e4 100644
--- a/frontend/src/components/drawer/DrawerSection.tsx
+++ b/frontend/src/components/drawer/DrawerSection.tsx
@@ -1,95 +1,101 @@
import { GearFineIcon } from "@phosphor-icons/react";
interface DrawerSectionProps {
- id: string;
- title: string;
- count: number;
- subtext: string;
- isOpen: boolean;
- onClick: () => void;
- children: React.ReactNode;
- icon: React.ReactNode;
+ id: string;
+ title: string;
+ count: number;
+ subtext: string;
+ isOpen: boolean;
+ onClick: () => void;
+ children: React.ReactNode;
+ icon: React.ReactNode;
}
export function DrawerSection({
- id,
- title,
- count,
- subtext,
- isOpen,
- onClick,
- children,
- icon,
+ id,
+ title,
+ count,
+ subtext,
+ isOpen,
+ onClick,
+ children,
+ icon,
}: DrawerSectionProps) {
- return (
+ return (
+
+
-
-
- {children}
- {count === 0 && (
-
- This drawer remains silent
-
- )}
-
-
-
-
+ This drawer remains silent
+
+ )}
- );
+
+
+
+
+ );
}
diff --git a/frontend/src/components/drawer/PasskeyModal.tsx b/frontend/src/components/drawer/PasskeyModal.tsx
index 04ba0ed..e33a38b 100644
--- a/frontend/src/components/drawer/PasskeyModal.tsx
+++ b/frontend/src/components/drawer/PasskeyModal.tsx
@@ -12,7 +12,10 @@ export function PasskeyModal() {
className="text-primary mx-auto mb-8 animate-pulse"
weight="duotone"
/>
-
+
You've been away a while.
diff --git a/frontend/src/components/reader/EnvelopeReveal.tsx b/frontend/src/components/reader/EnvelopeReveal.tsx
index a830307..9ac9f14 100644
--- a/frontend/src/components/reader/EnvelopeReveal.tsx
+++ b/frontend/src/components/reader/EnvelopeReveal.tsx
@@ -123,7 +123,12 @@ export function EnvelopeReveal({
to
-
{recipient}
+
+ {recipient}
+
{date}

({
- WelcomeLetterOverlay: ({ onComplete }: WelcomeLetterOverlayProps) => (
-
-
-
- ),
+ WelcomeLetterOverlay: ({ onComplete }: WelcomeLetterOverlayProps) => (
+
+
+
+ ),
}));
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 drawer sections and empty state message", () => {
- render(
-
-
- ,
- );
+ vi.mocked(useLetters).mockReturnValue({
+ drafts: [],
+ kept: [],
+ sent: [],
+ vault: [],
+ loading: false,
+ isAuthRequired: false,
+ });
+ });
- expect(screen.getByTestId("drawer-section-drafts")).toBeInTheDocument();
- expect(screen.getAllByTestId("drawer-section-title").length).toBeGreaterThanOrEqual(1);
- expect(screen.getByTestId("drawer-section-vault")).toBeInTheDocument();
- expect(screen.getByTestId("empty-drawer-message-drafts")).toBeInTheDocument();
+ it("renders the drawer sections and empty state message", () => {
+ render(
+
+
+ ,
+ );
+
+ expect(screen.getByTestId("drawer-section-drafts")).toBeInTheDocument();
+ expect(
+ screen.getAllByTestId("drawer-section-title").length,
+ ).toBeGreaterThanOrEqual(1);
+ expect(screen.getByTestId("drawer-section-vault")).toBeInTheDocument();
+ expect(
+ screen.getByTestId("empty-drawer-message-drafts"),
+ ).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.getByTestId("drawer-loading-state")).toBeInTheDocument();
+ });
- expect(screen.getByTestId("drawer-loading-state")).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.getByTestId("passkey-modal-title")).toBeInTheDocument();
+ expect(screen.getByPlaceholderText(/password/i)).toBeInTheDocument();
+ });
- expect(screen.getByTestId("passkey-modal-title")).toBeInTheDocument();
- expect(screen.getByPlaceholderText(/password/i)).toBeInTheDocument();
- });
+ it("renders the welcome letter when firstTime state is present", () => {
+ render(
+
+
+ ,
+ );
- it("renders the welcome letter when firstTime state is present", () => {
- render(
-
-
- ,
- );
+ expect(screen.getByTestId("welcome-letter-overlay")).toBeInTheDocument();
+ });
- expect(screen.getByTestId("welcome-letter-overlay")).toBeInTheDocument();
- });
+ it("renders the drawer content when the letter is closed", () => {
+ render(
+
+
+ ,
+ );
- it("renders the drawer content when the letter is closed", () => {
- render(
-
-
- ,
- );
+ const completeButton = screen.getByTestId("overlay-exit-button");
+ fireEvent.click(completeButton);
- const completeButton = screen.getByTestId("overlay-exit-button");
- fireEvent.click(completeButton);
-
- expect(
- screen.queryByTestId("welcome-letter-overlay"),
- ).not.toBeInTheDocument();
- });
+ expect(
+ screen.queryByTestId("welcome-letter-overlay"),
+ ).not.toBeInTheDocument();
+ });
});
diff --git a/frontend/src/pages/Drawer.tsx b/frontend/src/pages/Drawer.tsx
index 425956a..0a61541 100644
--- a/frontend/src/pages/Drawer.tsx
+++ b/frontend/src/pages/Drawer.tsx
@@ -1,9 +1,9 @@
import {
- ArchiveIcon,
- FeatherIcon,
- FileDashedIcon,
- PaperPlaneTiltIcon,
- VaultIcon,
+ ArchiveIcon,
+ FeatherIcon,
+ FileDashedIcon,
+ PaperPlaneTiltIcon,
+ VaultIcon,
} from "@phosphor-icons/react";
import { useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
@@ -17,188 +17,191 @@ import { PATHS } from "../config/routes";
import { useAuth } from "../hooks/useAuth";
import { useLetters } from "../hooks/useLetters";
import {
- formatRelativeDate,
- formatRelativeDateWithoutTime,
+ formatRelativeDate,
+ formatRelativeDateWithoutTime,
} from "../utils/dateFormat.ts";
export default function Drawer() {
- const { user, logout } = useAuth();
+ const { user, logout } = useAuth();
- 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();
+ 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;
+ if (!user) return null;
- const toggleSection = (id: string) =>
- setOpenSection(openSection === id ? null : id);
+ const toggleSection = (id: string) =>
+ setOpenSection(openSection === id ? null : id);
- return (
-
-
+ return (
+
+
- {showWelcomeLetter && (
-
{
- setShowWelcomeLetter(false);
- navigate(location.pathname, { replace: true, state: {} });
- }}
- />
- )}
+ {showWelcomeLetter && (
+ {
+ setShowWelcomeLetter(false);
+ navigate(location.pathname, { replace: true, state: {} });
+ }}
+ />
+ )}
- {isAuthRequired && }
-
-
-
- {loading ? (
-
-
-
- Opening your cabinet...
-
-
- ) : (
- <>
-
toggleSection("drafts")}
- icon={}
- >
- {drafts.map((draft) => (
-
- ))}
-
-
-
toggleSection("kept")}
- icon={}
- >
- {kept.map((letter) => (
-
- ))}
-
-
toggleSection("sent")}
- icon={}
- >
- {sent.map((letter) => (
-
- ))}
-
-
toggleSection("vault")}
- icon={}
- >
- {vault.map((letter) => (
- new Date().toISOString()}
- />
- ))}
-
- >
- )}
-
-
-
-
-
- {!showWelcomeLetter && (
-
-
-
- )}
+ {isAuthRequired && }
+
+
+
+ {loading ? (
+
+
+
+ Opening your cabinet...
+
+
+ ) : (
+ <>
+
toggleSection("drafts")}
+ icon={}
+ >
+ {drafts.map((draft) => (
+
+ ))}
+
+
+
toggleSection("kept")}
+ icon={}
+ >
+ {kept.map((letter) => (
+
+ ))}
+
+
toggleSection("sent")}
+ icon={}
+ >
+ {sent.map((letter) => (
+
+ ))}
+
+
toggleSection("vault")}
+ icon={}
+ >
+ {vault.map((letter) => (
+ new Date().toISOString()}
+ />
+ ))}
+
+ >
+ )}
+
+
+
+
+
+ {!showWelcomeLetter && (
+
+
+
+ )}
+
+ );
}
diff --git a/frontend/src/pages/Editor.test.tsx b/frontend/src/pages/Editor.test.tsx
index 6b32945..7b8ad34 100644
--- a/frontend/src/pages/Editor.test.tsx
+++ b/frontend/src/pages/Editor.test.tsx
@@ -1,4 +1,9 @@
-import { fireEvent, render, screen, waitForElementToBeRemoved } from "@testing-library/react";
+import {
+ fireEvent,
+ render,
+ screen,
+ waitForElementToBeRemoved,
+} from "@testing-library/react";
import { HttpResponse, http } from "msw";
import { MemoryRouter, Route, Routes } from "react-router-dom";
import { beforeEach, describe, expect, it, vi } from "vitest";
@@ -79,7 +84,9 @@ describe("Editor Page", () => {
);
// Wait for initial load to complete
- await waitForElementToBeRemoved(() => screen.queryByTestId("opening-draft-overlay"));
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId("opening-draft-overlay"),
+ );
const canvas = screen.getByTestId("canvas");
expect(canvas.getAttribute("data-readonly")).toBe("false");
@@ -136,7 +143,9 @@ describe("Editor Page", () => {
,
);
- await waitForElementToBeRemoved(() => screen.queryByTestId("opening-draft-overlay"));
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId("opening-draft-overlay"),
+ );
const canvas = screen.getByTestId("canvas");
diff --git a/frontend/src/pages/Login.test.tsx b/frontend/src/pages/Login.test.tsx
index 7f58b75..3b77068 100644
--- a/frontend/src/pages/Login.test.tsx
+++ b/frontend/src/pages/Login.test.tsx
@@ -31,7 +31,9 @@ describe("Login Page", () => {
await userEvent.type(screen.getByLabelText(/password/i), "password123");
await userEvent.click(screen.getByRole("button", { name: /sign in/i }));
- expect(await screen.findByTestId("login-error-message")).toHaveTextContent(/technical issues/i);
+ expect(await screen.findByTestId("login-error-message")).toHaveTextContent(
+ /technical issues/i,
+ );
});
it.each([
@@ -73,8 +75,14 @@ describe("Login Page", () => {
>
} />
- Drawer } />
- Reader