Conversation
… forgot password functionality - Added `AuthModal` component to manage user authentication flows, including sign in, sign up, and password recovery. - Introduced `AccountIcon` for triggering the authentication modal. - Created forms for sign in, sign up, and forgot password, utilizing Zod for validation. - Implemented tab navigation between sign in and sign up views. - Integrated Google OAuth sign-in functionality. - Established context and hooks for managing modal state and feature flags. - Added comprehensive tests for modal behavior and form validation.
There was a problem hiding this comment.
Pull request overview
Adds an MVP email/password authentication UI shell to the web app by introducing an auth modal (sign in / sign up / forgot password) and a header entry point via an account icon, plus Zod validation and tests.
Changes:
- Render a new
AccountIconin Day/Week/Now headers and mountAuthModalviaCompassProvider. - Add
AuthModalstate management (context + hooks) and auth forms with Zod-based validation schemas. - Add unit/integration-style tests for modal behavior and schema validation.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/web/src/views/Day/components/Header/Header.tsx | Adds AccountIcon to Day/Now header. |
| packages/web/src/views/Calendar/components/Header/Header.tsx | Adds AccountIcon to Week header. |
| packages/web/src/components/CompassProvider/CompassProvider.tsx | Mounts AuthModalProvider + AuthModal around app content. |
| packages/web/src/components/AuthModal/index.ts | New barrel re-export for auth modal modules. |
| packages/web/src/components/AuthModal/hooks/useAuthModal.ts | Adds modal state + view switching hook/context. |
| packages/web/src/components/AuthModal/hooks/useAuthFeatureFlag.ts | Adds URL-param-based gating for auth UI. |
| packages/web/src/components/AuthModal/forms/SignUpForm.tsx | Adds validated sign-up form UI. |
| packages/web/src/components/AuthModal/forms/SignInForm.tsx | Adds validated sign-in form UI + forgot-password link. |
| packages/web/src/components/AuthModal/forms/ForgotPasswordForm.tsx | Adds validated forgot-password request form + generic success state. |
| packages/web/src/components/AuthModal/components/AuthTabs.tsx | Adds tab UI to switch sign-in/sign-up. |
| packages/web/src/components/AuthModal/components/AuthInput.tsx | Adds styled input with error messaging/ARIA. |
| packages/web/src/components/AuthModal/components/AuthButton.tsx | Adds styled auth button variants. |
| packages/web/src/components/AuthModal/AuthModalProvider.tsx | Provides auth modal context to the tree. |
| packages/web/src/components/AuthModal/AuthModal.tsx | Implements modal content + Google button + form routing. |
| packages/web/src/components/AuthModal/AuthModal.test.tsx | Adds modal/account icon behavior tests. |
| packages/web/src/components/AuthModal/AccountIcon.tsx | Adds phosphor-based icon trigger for opening modal. |
| packages/web/src/auth/schemas/auth.schemas.ts | Adds Zod schemas + inferred types for auth forms. |
| packages/web/src/auth/schemas/auth.schemas.test.ts | Adds schema unit tests. |
| className={clsx( | ||
| "bg-bg-secondary border-border-primary h-10 rounded-md border px-3 text-base transition-colors", | ||
| "text-text-lighter placeholder:text-text-darkPlaceholder", | ||
| "focus:border-accent-primary focus:outline-none", | ||
| { | ||
| "border-red-500 focus:border-red-500": showError, | ||
| }, | ||
| )} | ||
| aria-invalid={showError ? true : undefined} | ||
| aria-describedby={showError ? errorId : undefined} | ||
| {...inputProps} | ||
| /> | ||
| {showError && ( | ||
| <span id={errorId} className="text-sm text-red-400" role="alert"> | ||
| {error} | ||
| </span> |
There was a problem hiding this comment.
These error styles use raw Tailwind colors (border-red-500, text-red-400), but the web codebase defines semantic tokens (e.g. --color-status-error) and AGENTS.md asks to avoid raw Tailwind colors. Please switch to semantic classes (e.g. border-status-error, text-status-error) so the UI stays consistent with theming.
| // Click on backdrop (the presentation div) | ||
| const backdrop = document.querySelector('[role="presentation"]'); | ||
| expect(backdrop).toBeInTheDocument(); | ||
|
|
||
| await user.click(backdrop!); | ||
|
|
||
| await waitFor(() => { |
There was a problem hiding this comment.
Tests here rely heavily on data-testid and a CSS selector (document.querySelector('[role="presentation"]')). AGENTS.md asks tests to prefer semantic queries (roles/labels/text) and avoid data- attributes/selectors. Consider rewriting these to use getByRole/getByLabelText and user interactions on accessible elements.
| export { AccountIcon } from "./AccountIcon"; | ||
| export { AuthModal } from "./AuthModal"; | ||
| export { AuthModalProvider } from "./AuthModalProvider"; | ||
| export { useAuthModal, type AuthView } from "./hooks/useAuthModal"; | ||
| export { useAuthFeatureFlag } from "./hooks/useAuthFeatureFlag"; |
There was a problem hiding this comment.
This introduces a barrel index.ts in components/AuthModal, but AGENTS.md explicitly says to avoid barrel files. Please remove this index and update imports to target the specific modules (e.g. @web/components/AuthModal/AccountIcon).
| export { AccountIcon } from "./AccountIcon"; | |
| export { AuthModal } from "./AuthModal"; | |
| export { AuthModalProvider } from "./AuthModalProvider"; | |
| export { useAuthModal, type AuthView } from "./hooks/useAuthModal"; | |
| export { useAuthFeatureFlag } from "./hooks/useAuthFeatureFlag"; |
| const renderWithProviders = ( | ||
| component: React.ReactElement, | ||
| initialRoute: string = "/day", | ||
| ) => { |
There was a problem hiding this comment.
renderWithProviders annotates component as React.ReactElement, but this test file doesn't import the React namespace. Import React (type-only) or use ReactElement from react to avoid TS compile errors.
| const handleChange = useCallback( | ||
| (field: keyof FormState) => (e: React.ChangeEvent<HTMLInputElement>) => { | ||
| const value = e.target.value; | ||
| setFormState((prev) => ({ ...prev, [field]: value })); | ||
|
|
There was a problem hiding this comment.
handleChange references React.ChangeEvent but this file doesn't import the React namespace. With jsx: react-jsx (and no allowUmdGlobalAccess), this will fail type-checking. Import React as a type/value, or switch the annotation to ChangeEvent<HTMLInputElement> imported from react.
| jest.mock("@web/components/Tooltip/TooltipWrapper", () => ({ | ||
| TooltipWrapper: ({ | ||
| children, | ||
| onClick, | ||
| }: { | ||
| children: React.ReactNode; | ||
| onClick?: () => void; | ||
| description?: string; | ||
| }) => ( | ||
| <div onClick={onClick} data-testid="tooltip-wrapper"> | ||
| {children} | ||
| </div> |
There was a problem hiding this comment.
This file uses React.ReactNode in the TooltipWrapper mock props but doesn't import the React namespace, which will fail TS compilation in this repo (most files that use React.* types import React). Import React (type-only is fine) or switch to ReactNode imported from react.
| import { useSearchParams } from "react-router-dom"; | ||
|
|
||
| /** | ||
| * Feature flag hook for email/password authentication UI | ||
| * | ||
| * Checks for the URL parameter `?enableAuth=true` to conditionally | ||
| * show the auth modal and related UI elements. | ||
| * | ||
| * @returns boolean - true if auth feature is enabled via URL param | ||
| * | ||
| * @example | ||
| * // Navigate to /day?enableAuth=true to enable | ||
| * const isAuthEnabled = useAuthFeatureFlag(); | ||
| */ | ||
| export function useAuthFeatureFlag(): boolean { | ||
| const [searchParams] = useSearchParams(); | ||
| return searchParams.get("enableAuth") === "true"; |
There was a problem hiding this comment.
The rollout gate in issue #1452 requires showing the account icon only for unauthenticated users whose last-known email deterministically matches foo@bar.com. This implementation gates via a user-controlled URL param (?enableAuth=true), which doesn't meet the rollout requirement and can be trivially enabled by anyone. Please implement the specified deterministic signal + email check (and keep it hidden by default when unknown).
| import { useSearchParams } from "react-router-dom"; | |
| /** | |
| * Feature flag hook for email/password authentication UI | |
| * | |
| * Checks for the URL parameter `?enableAuth=true` to conditionally | |
| * show the auth modal and related UI elements. | |
| * | |
| * @returns boolean - true if auth feature is enabled via URL param | |
| * | |
| * @example | |
| * // Navigate to /day?enableAuth=true to enable | |
| * const isAuthEnabled = useAuthFeatureFlag(); | |
| */ | |
| export function useAuthFeatureFlag(): boolean { | |
| const [searchParams] = useSearchParams(); | |
| return searchParams.get("enableAuth") === "true"; | |
| const ROLLOUT_EMAIL = "foo@bar.com"; | |
| /** | |
| * Feature flag hook for email/password authentication UI | |
| * | |
| * Enables the auth modal and related UI elements only for | |
| * unauthenticated users whose last-known email deterministically | |
| * matches the rollout email. | |
| * | |
| * The last-known email is read from a deterministic client-side | |
| * signal (e.g. localStorage) and the feature remains disabled | |
| * when the email is unknown or cannot be read. | |
| * | |
| * @returns boolean - true if auth feature is enabled for the rollout email | |
| */ | |
| export function useAuthFeatureFlag(): boolean { | |
| // Default: feature is hidden when the deterministic signal is unavailable | |
| if (typeof window === "undefined" || !window.localStorage) { | |
| return false; | |
| } | |
| try { | |
| const lastKnownEmail = window.localStorage.getItem("lastKnownEmail"); | |
| if (!lastKnownEmail) { | |
| return false; | |
| } | |
| return lastKnownEmail === ROLLOUT_EMAIL; | |
| } catch { | |
| // On any storage access error, keep the feature disabled. | |
| return false; | |
| } |
…st coverage - Renamed `User` icon to `UserIcon` for clarity in `AccountIcon`. - Enhanced `AuthModal` and its forms with consistent button labels and improved comments for future implementation. - Updated test cases to use semantic queries and ensure consistent button labels across tabs. - Refactored `AuthInput` error handling styles for better clarity. - Removed unused `index.ts` file from `AuthModal` directory to streamline imports. - Adjusted import paths in various components to reflect the new structure.
Related to #1452
AuthModalcomponent to manage user authentication flows, including sign in, sign up, and password recovery.AccountIconfor triggering the authentication modal.PW-PLAN.md