diff --git a/frontend/public/logo.svg b/frontend/public/logo.svg new file mode 100644 index 0000000..5e4619f --- /dev/null +++ b/frontend/public/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4db6ed3..bf5cc26 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,6 +1,6 @@ import { lazy, Suspense, useEffect, useRef } from "react"; import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom"; -import { ProtectedRoute, PublicRoute } from "./components/RouteGuards"; +import { AutoRedirectRoute, ProtectedRoute } from "./components/RouteGuards"; import SplashScreen from "./components/SplashScreen"; import { ROUTES } from "./config/routes"; import { useAuth } from "./hooks/useAuth"; @@ -34,38 +34,45 @@ export default function App() {
}> - } /> + + + + } + /> + - + } /> + - + } /> + - + } /> + - + } /> diff --git a/frontend/src/components/Logo.tsx b/frontend/src/components/Logo.tsx index 27151c5..aa9bf44 100644 --- a/frontend/src/components/Logo.tsx +++ b/frontend/src/components/Logo.tsx @@ -3,10 +3,15 @@ import "@fontsource/knewave/400.css"; interface LogoProps { scale?: number; - type?: "inline" | "mono" | "logo"; + type?: "inline" | "mono" | "logo" | null; + ul?: boolean; } -export default function Logo({ scale = 1, type = "logo" }: LogoProps) { +export default function Logo({ + scale = 1, + type = null, + ul = false, +}: LogoProps) { if (type === "inline") { return ( @@ -24,24 +29,35 @@ export default function Logo({ scale = 1, type = "logo" }: LogoProps) { ); } + if (type === "logo") { + return ( + Pi. Ku. logo + ); + } + return (
- Pi + Pi -  Ku +  Ku
); diff --git a/frontend/src/components/RouteGuards.test.tsx b/frontend/src/components/RouteGuards.test.tsx index 6914576..79606f9 100644 --- a/frontend/src/components/RouteGuards.test.tsx +++ b/frontend/src/components/RouteGuards.test.tsx @@ -3,14 +3,20 @@ import { MemoryRouter, Route, Routes } from "react-router-dom"; import { beforeEach, describe, expect, it } from "vitest"; import { mockUser } from "../../test/fixtures/user.fixture"; import { useAuthStore } from "../store/useAuthStore"; -import { ProtectedRoute, PublicRoute } from "./RouteGuards"; +import { AutoRedirectRoute, ProtectedRoute } from "./RouteGuards"; function renderGuard(ui: React.ReactNode, mountPath: "/protected" | "/public") { return render( - Login Page} /> - Drawer Page} /> + Login Page} + /> + Drawer Page} + /> @@ -85,9 +91,9 @@ describe("PublicRoute", () => { user: null, }); renderGuard( - +
Login Page
-
, + , "/public", ); expect(screen.getByTestId("splash-screen")).toBeInTheDocument(); @@ -101,9 +107,9 @@ describe("PublicRoute", () => { user: mockUser, }); renderGuard( - +
Login Form
-
, + , "/public", ); expect(screen.getByTestId("drawer-page")).toBeInTheDocument(); @@ -117,9 +123,9 @@ describe("PublicRoute", () => { user: null, }); renderGuard( - +
Login Form
-
, + , "/public", ); expect(screen.getByTestId("login-form")).toBeInTheDocument(); diff --git a/frontend/src/components/RouteGuards.tsx b/frontend/src/components/RouteGuards.tsx index 5c4d53e..9fa20af 100644 --- a/frontend/src/components/RouteGuards.tsx +++ b/frontend/src/components/RouteGuards.tsx @@ -22,10 +22,10 @@ export function ProtectedRoute({ children }: { children: React.ReactNode }) { } /** - * Public - auth route guard. + * Auto-redirect - auth route guard. * If authenticated, redirect all the auth related flows to the drawer */ -export function PublicRoute({ children }: { children: React.ReactNode }) { +export function AutoRedirectRoute({ children }: { children: React.ReactNode }) { const { isAuthenticated, isInitializing } = useAuth(); if (isInitializing) return ; diff --git a/frontend/src/components/SplashScreen.tsx b/frontend/src/components/SplashScreen.tsx index 7c4297e..0874d18 100644 --- a/frontend/src/components/SplashScreen.tsx +++ b/frontend/src/components/SplashScreen.tsx @@ -3,7 +3,10 @@ import Logo from "./Logo"; export default function SplashScreen() { return ( -
+
diff --git a/frontend/src/components/drawer/DrawerSection.tsx b/frontend/src/components/drawer/DrawerSection.tsx index 50b44ca..14874e4 100644 --- a/frontend/src/components/drawer/DrawerSection.tsx +++ b/frontend/src/components/drawer/DrawerSection.tsx @@ -3,19 +3,23 @@ import { GearFineIcon } from "@phosphor-icons/react"; interface DrawerSectionProps { id: string; title: string; - count: 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, }: DrawerSectionProps) { return (
- {children} +
+ {children} + {count === 0 && ( +

+ This drawer remains silent +

+ )} +
-
- - -
- {loading ? ( -
- - - Opening your cabinet... - -
- ) : ( - <> - toggleSection("drafts")} - > - {drafts.map((draft) => ( - - ))} - {drafts.length === 0 && ( -

- This drawer remains silent -

- )} -
- - toggleSection("kept")} - > - {kept.map((letter) => ( - - ))} - - toggleSection("sent")} - > - {sent.map((letter) => ( - - ))} - {sent.length === 0 && ( -

- This drawer remains silent -

- )} -
- toggleSection("vault")} - > - {vault.map((letter) => ( - new Date().toISOString()} - /> - ))} - - - )} -
- - - -
- For your unsaid. -
- {!showWelcomeLetter && ( -
- -
- )} + {isAuthRequired && } +
+ +
+ Personal Archive
- ); +
+ Welcome Back{" "} + {user.full_name} + +
+
+ +
+ {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()} + /> + ))} + + + )} +
+ + + +
+ For your unsaid. +
+ {!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/Home.tsx b/frontend/src/pages/Home.tsx index 697f8d7..8164748 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -14,6 +14,9 @@ import Saajan from "../components/ui/Saajan.tsx"; import { ROUTES } from "../config/routes.ts"; import { formatDate } from "../utils/dateFormat.ts"; +import "@fontsource/space-mono/index.css"; +import "@fontsource/architects-daughter/index.css"; + export default function Home() { const sectionContainer1 = useRef(null); const { scrollYProgress } = useScroll({ @@ -53,7 +56,7 @@ export default function Home() {
{/* Intro */} @@ -64,12 +67,12 @@ export default function Home() { scale: useTransform(scrollYProgress, [0, 0.12], [1, 10]), }} > -

+

You've been carrying something

-

+ unsaid -

+ - + - is a{" "} + is a{" "} safe space ,
@@ -168,11 +171,11 @@ export default function Home() { className="absolute text-4xl md:text-6xl text-center px-10 leading-tight" > seal it{" "} - + secure {" "} and{" "} - + private . @@ -252,7 +255,7 @@ export default function Home() { {/* Outro */}
} /> - Reader
} /> + Drawer
} + /> + Reader} + />
, ); @@ -83,7 +91,8 @@ describe("Login Page", () => { await userEvent.type(screen.getByLabelText(/password/i), "password123"); await userEvent.click(screen.getByRole("button", { name: /sign in/i })); - const expectedTestId = nextRoute.toLowerCase() === "drawer" ? "drawer-page" : "reader-page"; + const expectedTestId = + nextRoute.toLowerCase() === "drawer" ? "drawer-page" : "reader-page"; expect(await screen.findByTestId(expectedTestId)).toBeInTheDocument(); }); }); diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx index 4ed7089..2f4b32e 100644 --- a/frontend/src/pages/Login.tsx +++ b/frontend/src/pages/Login.tsx @@ -83,8 +83,8 @@ export default function Login() { {showWelcome && }
-

- Enter Archive +

+   Enter Archive

{apiError && ( diff --git a/frontend/src/pages/Reader.test.tsx b/frontend/src/pages/Reader.test.tsx index 50164b1..ec19415 100644 --- a/frontend/src/pages/Reader.test.tsx +++ b/frontend/src/pages/Reader.test.tsx @@ -76,7 +76,9 @@ describe("Reader Page", () => { , ); - expect(await screen.findByTestId("envelope-recipient")).toHaveTextContent(/Guest/i); + expect(await screen.findByTestId("envelope-recipient")).toHaveTextContent( + /Guest/i, + ); }); it("should display an error message if the server request fails", async () => { @@ -97,9 +99,9 @@ describe("Reader Page", () => { , ); - expect( - await screen.findByTestId("log-modal-message"), - ).toHaveTextContent(/Failed to load letter/i); + expect(await screen.findByTestId("log-modal-message")).toHaveTextContent( + /Failed to load letter/i, + ); }); it("should navigate to the login page with redirect url when the letter has no sharing key and the user is not logged in", async () => { diff --git a/frontend/src/pages/Register.tsx b/frontend/src/pages/Register.tsx index a7e9fb4..b28403a 100644 --- a/frontend/src/pages/Register.tsx +++ b/frontend/src/pages/Register.tsx @@ -77,7 +77,7 @@ export default function Register() {
- Create a Account + Create a Account
{apiError && (