Skip to content

feat:started authentication#1

Merged
sreenandpk merged 2 commits into
developfrom
feature/FRO-2-folder
Jul 9, 2026
Merged

feat:started authentication#1
sreenandpk merged 2 commits into
developfrom
feature/FRO-2-folder

Conversation

@midhun123758

@midhun123758 midhun123758 commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

authentication

Summary by CodeRabbit

  • New Features

    • Added a new landing experience with an interactive globe and entry point into the app.
    • Introduced sign in, sign up, forgot/reset password, success, and dashboard screens.
    • Added session handling so users can stay signed in and log out smoothly.
    • Added a reusable error screen for unexpected issues.
  • Documentation

    • Added environment setup guidance and a sample config file.
    • Updated the README with setup and project usage notes.
  • Chores

    • Added project tooling for formatting, linting, testing, and automated checks.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9aaaaa61-6f3d-4c37-80eb-cc99a046698d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/FRO-2-folder

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (18)
src/features/globe/useGlobe.ts (4)

82-84: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

.fill([0, 0, 0]) shares one array reference across all initial slots.

Array.from({ length: N }).fill([0, 0, 0]) fills every index with the same array object reference, not independent tuples. This happens to be harmless today only because animate() runs synchronously before the first pathsData() call and immediately overwrites every slot (Line 105) — but it's a well-known JS footgun that will silently break if the initial-render logic changes.

🛡️ Safer pre-allocation
-    const pathCoordinateBuffers: Coordinate[][] = NATIONS.map(() =>
-      Array.from<Coordinate>({ length: GLOBE_CONFIG.PATH_POINTS }).fill([0, 0, 0]),
-    );
+    const pathCoordinateBuffers: Coordinate[][] = NATIONS.map(() =>
+      Array.from<Coordinate>({ length: GLOBE_CONFIG.PATH_POINTS }, () => [0, 0, 0]),
+    );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/globe/useGlobe.ts` around lines 82 - 84, The pre-allocation in
useGlobe currently uses Array.from(...).fill([0, 0, 0]), which reuses the same
coordinate array reference for every slot. Update the pathCoordinateBuffers
initialization so each position gets its own independent Coordinate tuple/array,
keeping the logic in useGlobe and animate() unchanged but avoiding shared
mutable state.

81-111: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Animation loop still allocates a new array per point per frame, defeating the stated pre-allocation goal.

The comment at Line 81 says buffers are pre-allocated "to avoid 60fps garbage collection pressure," but Line 105 does coords[i] = [nation.lat + ..., nation.lng + ..., alt] — a new array literal replacing the slot every frame, for every nation × PATH_POINTS (14 × 15 = 210 allocations/frame at 60fps). Mutate the existing tuple in place instead.

⚡ Proposed fix
           const alt = Math.max(
             0.01,
             GLOBE_CONFIG.PATH_BASE_ALTITUDE +
               Math.sin(i * 0.5 + t * 4) * GLOBE_CONFIG.PATH_WAVE_AMPLITUDE +
               Math.cos(i * 1.2 - t * 2) * 0.012,
           );
-          coords[i] = [nation.lat + Math.sin(i) * 0.5, nation.lng + lngOffset, alt];
+          const point = coords[i];
+          point[0] = nation.lat + Math.sin(i) * 0.5;
+          point[1] = nation.lng + lngOffset;
+          point[2] = alt;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/globe/useGlobe.ts` around lines 81 - 111, The animation loop in
useGlobe still creates a new Coordinate array for every point on each frame,
which defeats the pre-allocation in pathCoordinateBuffers. Update animate() so
it reuses the existing per-point buffer entries instead of assigning a fresh
tuple to coords[i], mutating the stored Coordinate values in place while keeping
world.pathsData(chartBuffer) fed from the same preallocated arrays.

50-59: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Globe textures hotlinked from unpkg without fallback.

globeImageUrl/bumpImageUrl/backgroundImageUrl point at unpkg.com/three-globe/example/... assets. unpkg is a CDN meant for package distribution, not guaranteed for high-traffic production hotlinking, and there's no onload/error handling if these fail — the globe would silently render untextured. Consider self-hosting these assets (same as the GeoJSON TODO at Line 132) or adding a fallback.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/globe/useGlobe.ts` around lines 50 - 59, The Globe setup in
useGlobe currently hotlinks textures via globeImageUrl, bumpImageUrl, and
backgroundImageUrl from unpkg without any fallback or error handling. Update the
Globe initialization to use self-hosted/local asset URLs (consistent with the
existing GeoJSON self-hosting approach) or add a fallback path if the remote
textures fail to load, so the texture setup remains reliable in production.

133-135: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

No res.ok check before parsing the GeoJSON response.

If the unpkg fetch returns a non-2xx status (e.g. 404/503), res.json() will still be attempted and either throw an opaque parse error or resolve unexpectedly, rather than surfacing a clear "failed to fetch boundaries" error via the existing .catch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/globe/useGlobe.ts` around lines 133 - 135, The GeoJSON load path
in useGlobe should validate the fetch response before calling res.json(), since
fetch can resolve on non-2xx statuses and produce unclear parse failures. Update
the promise chain around the unpkg request in useGlobe to check the response
status first, then either parse the body or throw a clear "failed to fetch
boundaries" error so the existing catch handles it consistently.
src/types/globe.types.ts (1)

11-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Unused, name-colliding GlobeInstance/GlobeControls duplicate the real library types.

useGlobe.ts imports GlobeInstance directly from 'globe.gl' (via the real bundled declarations re-exported in src/types.d.ts), not from here — this hand-rolled GlobeInstance/GlobeControls pair (lines 11-54) is unused dead code. Having two differently-shaped types both named GlobeInstance across the codebase is confusing for future maintainers. Recommend dropping these two interfaces and keeping only the app-specific types (Coordinate, PathData, LabelData, GeoFeature, PointOfView, GeoJSON).

♻️ Proposed cleanup
-export interface GlobeControls {
-  autoRotate: boolean;
-  autoRotateSpeed: number;
-  enableZoom: boolean;
-}
-
-export interface GlobeInstance {
-  // Image layers
-  globeImageUrl(url: string): GlobeInstance;
-  bumpImageUrl(url: string): GlobeInstance;
-  backgroundImageUrl(url: string): GlobeInstance;
-
-  // Paths (stock chart lines)
-  pathsData(data: PathData[]): GlobeInstance;
-  pathPoints(key: string): GlobeInstance;
-  pathPointLat(fn: (p: Coordinate) => number): GlobeInstance;
-  pathPointLng(fn: (p: Coordinate) => number): GlobeInstance;
-  pathPointAlt(fn: (p: Coordinate) => number): GlobeInstance;
-  pathColor(fn: (d: PathData) => string): GlobeInstance;
-  pathStroke(value: number): GlobeInstance;
-
-  // Labels (country name overlays)
-  labelsData(data: LabelData[]): GlobeInstance;
-  labelLat(fn: (d: LabelData) => number): GlobeInstance;
-  labelLng(fn: (d: LabelData) => number): GlobeInstance;
-  labelText(fn: (d: LabelData) => string): GlobeInstance;
-  labelSize(size: number): GlobeInstance;
-  labelColor(fn: (d: LabelData) => string): GlobeInstance;
-  labelDotRadius(r: number): GlobeInstance;
-
-  // Polygons (country boundary fills)
-  polygonsData(data: GeoFeature[]): GlobeInstance;
-  polygonCapColor(fn: (d: GeoFeature) => string): GlobeInstance;
-  polygonSideColor(fn: (d: GeoFeature) => string): GlobeInstance;
-  polygonStrokeColor(fn: (d: GeoFeature) => string): GlobeInstance;
-  polygonLabel(fn: (d: GeoFeature) => string): GlobeInstance;
-  onPolygonHover(fn: (hoverD: GeoFeature | null) => void): GlobeInstance;
-
-  // Camera
-  pointOfView(pov: Partial<PointOfView>, durationMs?: number): GlobeInstance;
-  width(w: number): GlobeInstance;
-  height(h: number): GlobeInstance;
-  controls(): GlobeControls;
-}
-
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/types/globe.types.ts` around lines 11 - 54, Remove the unused hand-rolled
GlobeInstance and GlobeControls interfaces from globe.types.ts, since
useGlobe.ts relies on the real GlobeInstance type re-exported from globe.gl.
Keep only the app-specific type definitions (Coordinate, PathData, LabelData,
GeoFeature, PointOfView, GeoJSON) and update any local references so there is no
duplicate name-colliding GlobeInstance in the codebase.
src/styles/tokens.css (1)

33-35: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stylelint casing hints on font names — safe to ignore.

Font family proper nouns (BlinkMacSystemFont, Roboto, Helvetica, Arial, Consolas) are conventionally kept in their canonical case; lowercasing per the value-keyword-case rule would hurt readability with no functional benefit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/styles/tokens.css` around lines 33 - 35, No code change is needed here:
the font-family tokens in tokens.css use canonical proper-noun casing (for
example BlinkMacSystemFont, Roboto, Helvetica, Arial, and Consolas), so keep the
existing values in the --font-family-base and --font-family-mono declarations
as-is and do not apply value-keyword-case lowercasing.

Source: Linters/SAST tools

src/components/ui/ErrorBoundary/ErrorBoundary.tsx (1)

35-39: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

TODO: wire up real error tracking.

componentDidCatch only logs to console per the TODO. Want me to help integrate Sentry (or another tracker) here, or open a tracking issue for this?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ui/ErrorBoundary/ErrorBoundary.tsx` around lines 35 - 39,
componentDidCatch in ErrorBoundary is still only writing to console, so replace
the placeholder logging with real error tracking integration. Update
ErrorBoundary.componentDidCatch to send the caught error and info.componentStack
to the chosen service (for example Sentry) with the existing context preserved,
and keep console logging only as a fallback if needed.
src/index.css (1)

2-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Hardcoded colors/fonts bypass the design-token system.

tokens.css documents that "All design values are defined here as CSS custom properties. Use these variables throughout all component CSS files. DO NOT hard-code raw color values in component styles." Lines 5, 6, and 8 hardcode #000, a font stack, and #fff instead of --color-bg-primary, --font-family-base, and --color-text-primary.

♻️ Proposed fix — use design tokens
 body {
   margin: 0;
   padding: 0;
-  background-color: `#000`;
-  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+  background-color: var(--color-bg-primary);
+  font-family: var(--font-family-base);
   overflow: hidden;
-  color: `#fff`;
+  color: var(--color-text-primary);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/index.css` around lines 2 - 9, The global `body` styles in
`src/index.css` are hardcoding theme values instead of using the design-token
system. Update the `body` rule to use the existing CSS custom properties from
`tokens.css` for background, font family, and text color, specifically replacing
the raw color and font stack with the corresponding `--color-bg-primary`,
`--font-family-base`, and `--color-text-primary` tokens.
src/styles/reset.css (1)

17-23: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stylelint casing hint is a style-only nit; skip.

optimizeLegibility at line 22 is the canonical CSS value spelling; the stylelint value-keyword-case rule flags it purely for casing convention with no functional effect.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/styles/reset.css` around lines 17 - 23, No code change is needed here:
the `body` reset in `reset.css` already uses the canonical `text-rendering:
optimizeLegibility` value, and the `value-keyword-case` warning is only about
casing style. Leave the `body` rule as-is and do not alter `optimizeLegibility`.

Source: Linters/SAST tools

src/features/auth/auth.service.ts (1)

71-74: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Type the registration payload instead of any.

Every other method here has a typed request/response; register(data: any) loses type safety on a critical auth entry point. Define a RegisterRequest interface (mirroring the backend schema) and use it here.

♻️ Proposed fix
+export interface RegisterRequest {
+  username: string;
+  email: string;
+  password: string;
+}
+
-  async register(data: any): Promise<UserResponse> {
+  async register(data: RegisterRequest): Promise<UserResponse> {
     const response = await apiClient.post<UserResponse>('/auth/register', data);
     return response.data;
   },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/auth/auth.service.ts` around lines 71 - 74, The register method
in auth.service loses type safety by accepting any, unlike the other typed auth
methods. Define a RegisterRequest interface that matches the backend
registration payload and update AuthService.register to accept that type instead
of any, keeping the existing UserResponse return type and using the register
symbol to locate the change.
src/features/dashboard/DashboardPage.tsx (1)

22-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move profile-card inline styles into dashboard.css.

The file already imports dashboard.css for page-level classes; the profile-details block should follow the same pattern instead of large inline style objects, for consistency and easier theming.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/dashboard/DashboardPage.tsx` around lines 22 - 55, The profile
details block in DashboardPage should stop using large inline style objects and
be moved into dashboard.css to match the existing page-level styling pattern.
Update the JSX in DashboardPage to use class names for the profile card,
heading, and timestamp, and add the corresponding CSS rules in dashboard.css so
the visual appearance stays the same while making the styles easier to maintain
and theme.
src/App.tsx (2)

16-16: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Use selectors instead of subscribing to the whole store.

Destructuring the full store causes AppContent to re-render on unrelated state changes (e.g., accessToken updates during token refresh), even though only user, logout, and initAuth are used here.

♻️ Suggested selector-based subscription
-  const { logout, initAuth, user } = useUserStore();
+  const logout = useUserStore((s) => s.logout);
+  const initAuth = useUserStore((s) => s.initAuth);
+  const user = useUserStore((s) => s.user);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/App.tsx` at line 16, The AppContent subscription is pulling the whole
useUserStore state via destructuring, which causes unnecessary re-renders on
unrelated updates. Update the store access in App.tsx to use selector-based
subscriptions in AppContent so it only subscribes to logout, initAuth, and user,
instead of the entire store. Keep the change localized to the useUserStore call
and preserve the existing behavior of AppContent.

76-84: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Misleading prop wiring: onNavigateToDashboard navigates to login, not dashboard.

See related comment in src/components/SuccessPage.tsx (Lines 5-8, 49-56) where this prop is defined and bound to a "Go to Sign In" button — the naming here should match that it routes to ROUTES.LOGIN, not the dashboard.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/App.tsx` around lines 76 - 84, The `SuccessPage` prop wiring is
misleading because `onNavigateToDashboard` in `App` actually navigates to
`ROUTES.LOGIN`, not the dashboard. Rename the prop in `SuccessPage` and its
usage sites to match its real behavior, or change the handler to point to the
dashboard if that was intended; use the `SuccessPage` component and its
`onNavigateToDashboard` prop definition to locate the related wiring.
src/components/SuccessPage.tsx (2)

5-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Rename onNavigateToDashboard to reflect actual behavior.

The prop is always wired to navigate to the login screen (confirmed in App.tsx Line 80, with the comment "Needs to login first"), and the button itself reads "Go to Sign In." The name implies dashboard navigation, which is misleading for future maintainers wiring this component.

♻️ Suggested rename
 interface SuccessPageProps {
-  onNavigateToDashboard: () => void;
+  onNavigateToLogin: () => void;
   onBackToLogin: () => void;
 }

 export const SuccessPage: React.FC<SuccessPageProps> = ({
-  onNavigateToDashboard,
+  onNavigateToLogin,
   onBackToLogin,
 }) => {

Also applies to: 49-56

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/SuccessPage.tsx` around lines 5 - 8, The SuccessPage props and
usage are misleading because onNavigateToDashboard does not navigate to a
dashboard and is actually wired to the login/sign-in flow. Rename the prop in
SuccessPageProps and all call sites, including the handler passed from App.tsx
and any button wiring in SuccessPage, to a name that reflects the real action
(such as sign-in/login navigation). Keep the naming consistent across the
component interface, destructuring, and any references so future maintainers can
understand the behavior immediately.

16-105: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting inline styles into CSS classes.

This component leans heavily on inline style objects (icon circle, button stack, tip card) instead of the class-based system already established in auth-layout.css. Moving these into named classes would keep styling consistent and easier to theme alongside the rest of the auth flow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/SuccessPage.tsx` around lines 16 - 105, SuccessPage.tsx relies
on several inline style objects, which should be moved to the existing CSS
class-based styling approach. Extract the styles used in the main card, icon
circle, button stack, and onboarding tip card into named classes in
auth-layout.css, then update SuccessPage to reference those classes instead of
inline style props. Keep the component structure and behavior the same while
aligning the styling with the rest of the auth flow.
src/styles/components/auth-layout.css (1)

5-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Scope auth-specific tokens instead of polluting :root.

These variables are redefined at the global :root level in a component-scoped stylesheet. Since CSS imports are global side-effects, this can silently override or be overridden by tokens with the same names defined elsewhere (e.g., the app-wide tokens.css), depending on import order.

♻️ Suggested scoping
-:root {
+.auth-screen-wrapper {
   --color-bg-left: `#0b1120`;
   --color-bg-right: `#f8fafc`;
   --color-text-dark: `#09090b`;
   --color-text-muted: `#64748b`;
   --color-text-grey: `#475569`;
   --color-brand-teal: `#0d9488`;
   --color-brand-teal-hover: `#0f766e`;
   --color-border: `#e2e8f0`;
   --color-border-hover: `#cbd5e1`;
   --color-input-focus: `#0d9488`;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/styles/components/auth-layout.css` around lines 5 - 16, The auth layout
color tokens are being declared on the global :root inside a component
stylesheet, which can leak into or collide with app-wide tokens. Move these
custom properties into the auth-specific scope used by this stylesheet (for
example the auth layout wrapper/class defined in auth-layout styles) and keep
the existing variable names so only the auth layout consumes them. Use the :root
token block in auth-layout.css and the auth layout container selector as the
main places to update.
src/components/ui/AuthLayout/AuthLayout.tsx (1)

18-26: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Keyboard activation only handles Enter, not Space.

Elements with role="button" are expected to activate on both Enter and Space per ARIA authoring practices. Consider using a native <button> element instead, which gets this for free and removes the need for manual role/tabIndex/onKeyDown wiring.

♻️ Suggested fix
-        <div
+        <button
+          type="button"
           className="brand-header"
           onClick={() => navigate(ROUTES.HOME)}
-          role="button"
-          tabIndex={0}
-          onKeyDown={(e) => e.key === 'Enter' && navigate(ROUTES.HOME)}
-          style={{ cursor: 'pointer' }}
           aria-label="Go back to home"
         >
           <div className="brand-logo-icon">
             <TrendingUp size={22} strokeWidth={2.5} />
           </div>
           <span className="brand-logo-text">Copilot AI</span>
-        </div>
+        </button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ui/AuthLayout/AuthLayout.tsx` around lines 18 - 26, The
clickable brand header in AuthLayout only supports Enter via its onKeyDown
handler, so update the `brand-header` interaction to also activate on Space, or
बेहतर replace the div with a native button in `AuthLayout` so `role`,
`tabIndex`, and keyboard handling are no longer manual. If keeping the current
element, adjust the `onKeyDown` logic alongside `navigate(ROUTES.HOME)` so both
Enter and Space trigger the same navigation behavior.
src/test/Login.test.tsx (1)

8-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

useUserStore isn't mocked, so setAccessToken/setUser hit the real Zustand store with persist (localStorage) during tests.

Since only authService is mocked, Login's successful-login test path exercises the real store, including its persist middleware writing to localStorage. This risks state leaking across test files/cases since the store singleton and localStorage aren't reset between tests, and isn't representative of a pure unit test for Login. Consider mocking useUserStore as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/Login.test.tsx` around lines 8 - 13, The Login test setup only mocks
authService, so the successful-login flow still uses the real useUserStore
singleton and its persist/localStorage side effects. Update the test mocks to
include useUserStore as well, and make its setAccessToken and setUser actions
jest/vi fns so Login can be tested without touching Zustand persistence. Keep
the mock aligned with the existing authService mock in Login.test.tsx so the
unit test stays isolated and does not leak state across cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 15-22: Disable persisted credentials in the Checkout code step by
updating the actions/checkout@v4 configuration to set persist-credentials to
false. This change belongs in the same workflow job that also uses Setup
Node.js, and the key symbol to locate is the Checkout code step under
actions/checkout@v4.

In `@src/components/Login.tsx`:
- Around line 37-54: The login flow in Login’s submit handler leaves a partial
authenticated session if authService.getMe() fails after setAccessToken()
succeeds. Update the catch path in the same handler to clear the auth state
(reset the access token and authenticated flag, and avoid leaving user state
set) before showing the error, so AuthGuard does not treat the failed login as
valid.

In `@src/features/globe/useGlobe.ts`:
- Around line 174-182: Cleanup in useGlobe currently clears the DOM but does not
fully release the globe.gl resources. Update the unmount cleanup in useGlobe to
dispose the globe instance first by calling world._destructor() before removing
event listeners and clearing the container, then null out
globeInstanceRef.current so renderer, scene, controls, and WebGL resources are
released properly.
- Around line 122-128: The wheel handler in useGlobe should preserve the current
camera state instead of resetting it on every scroll tick. Update handleWheel so
it reads the existing world point of view and only adjusts lng, or guard the
handler when a country is active, so flyTo and the current lat/altitude are not
immediately overwritten.

In `@src/index.css`:
- Around line 1-14: The global body styles in src/index.css are applying a
permanent scroll lock via body overflow hidden, which affects all routes loaded
from src/main.tsx. Remove the overflow hidden rule from the global stylesheet
and move the scroll-lock behavior into the specific fullscreen layout/component
that needs it, keeping Login/Register/Forgot Password/Reset Password/Dashboard
able to scroll when their content overflows. Use the body and `#root` styles in
src/index.css as the place to locate and adjust this change.

In `@src/lib/api.client.ts`:
- Around line 20-58: The response interceptor in apiClient is allowing multiple
401s to trigger separate /auth/refresh calls, which can race and break token
rotation handling. Update the refresh flow in the response interceptor to
serialize refresh attempts with a single shared in-flight promise/queue so
concurrent requests wait for the same refresh result, then retry using the new
token. Also add a timeout to the axios.post refresh call so a stalled backend
does not leave requests hanging indefinitely.

In `@src/stores/userStore.ts`:
- Around line 49-61: The initAuth catch block in userStore has an unused error
binding; use the caught error instead of ignoring it. Update the initAuth method
to log the failure from authService.getMe before clearing state, so silent
auth-init issues are debuggable, and keep the existing reset logic for user,
isAuthenticated, and accessToken in the catch path.

In `@src/styles/components/country-list.css`:
- Around line 85-91: The .country-indicator style uses the nonstandard casing
currentColor, which triggers the Stylelint warning. Update the box-shadow
declaration in the country list styles to use the lowercase currentcolor token
so the rule passes while preserving the same visual effect.

In `@src/test/Login.test.tsx`:
- Around line 18-23: The test helper in Login.test.tsx is passing an invalid
Login prop: replace onBackToHome with one of the supported LoginProps callbacks.
Update renderLogin to use the actual Login component API from Login.tsx (for
example, pass onNavigateToSignup or onNavigateToForgot if that is what the test
needs, or remove the extra prop entirely) so the JSX matches the component’s
defined props and TypeScript checks pass.

---

Nitpick comments:
In `@src/App.tsx`:
- Line 16: The AppContent subscription is pulling the whole useUserStore state
via destructuring, which causes unnecessary re-renders on unrelated updates.
Update the store access in App.tsx to use selector-based subscriptions in
AppContent so it only subscribes to logout, initAuth, and user, instead of the
entire store. Keep the change localized to the useUserStore call and preserve
the existing behavior of AppContent.
- Around line 76-84: The `SuccessPage` prop wiring is misleading because
`onNavigateToDashboard` in `App` actually navigates to `ROUTES.LOGIN`, not the
dashboard. Rename the prop in `SuccessPage` and its usage sites to match its
real behavior, or change the handler to point to the dashboard if that was
intended; use the `SuccessPage` component and its `onNavigateToDashboard` prop
definition to locate the related wiring.

In `@src/components/SuccessPage.tsx`:
- Around line 5-8: The SuccessPage props and usage are misleading because
onNavigateToDashboard does not navigate to a dashboard and is actually wired to
the login/sign-in flow. Rename the prop in SuccessPageProps and all call sites,
including the handler passed from App.tsx and any button wiring in SuccessPage,
to a name that reflects the real action (such as sign-in/login navigation). Keep
the naming consistent across the component interface, destructuring, and any
references so future maintainers can understand the behavior immediately.
- Around line 16-105: SuccessPage.tsx relies on several inline style objects,
which should be moved to the existing CSS class-based styling approach. Extract
the styles used in the main card, icon circle, button stack, and onboarding tip
card into named classes in auth-layout.css, then update SuccessPage to reference
those classes instead of inline style props. Keep the component structure and
behavior the same while aligning the styling with the rest of the auth flow.

In `@src/components/ui/AuthLayout/AuthLayout.tsx`:
- Around line 18-26: The clickable brand header in AuthLayout only supports
Enter via its onKeyDown handler, so update the `brand-header` interaction to
also activate on Space, or बेहतर replace the div with a native button in
`AuthLayout` so `role`, `tabIndex`, and keyboard handling are no longer manual.
If keeping the current element, adjust the `onKeyDown` logic alongside
`navigate(ROUTES.HOME)` so both Enter and Space trigger the same navigation
behavior.

In `@src/components/ui/ErrorBoundary/ErrorBoundary.tsx`:
- Around line 35-39: componentDidCatch in ErrorBoundary is still only writing to
console, so replace the placeholder logging with real error tracking
integration. Update ErrorBoundary.componentDidCatch to send the caught error and
info.componentStack to the chosen service (for example Sentry) with the existing
context preserved, and keep console logging only as a fallback if needed.

In `@src/features/auth/auth.service.ts`:
- Around line 71-74: The register method in auth.service loses type safety by
accepting any, unlike the other typed auth methods. Define a RegisterRequest
interface that matches the backend registration payload and update
AuthService.register to accept that type instead of any, keeping the existing
UserResponse return type and using the register symbol to locate the change.

In `@src/features/dashboard/DashboardPage.tsx`:
- Around line 22-55: The profile details block in DashboardPage should stop
using large inline style objects and be moved into dashboard.css to match the
existing page-level styling pattern. Update the JSX in DashboardPage to use
class names for the profile card, heading, and timestamp, and add the
corresponding CSS rules in dashboard.css so the visual appearance stays the same
while making the styles easier to maintain and theme.

In `@src/features/globe/useGlobe.ts`:
- Around line 82-84: The pre-allocation in useGlobe currently uses
Array.from(...).fill([0, 0, 0]), which reuses the same coordinate array
reference for every slot. Update the pathCoordinateBuffers initialization so
each position gets its own independent Coordinate tuple/array, keeping the logic
in useGlobe and animate() unchanged but avoiding shared mutable state.
- Around line 81-111: The animation loop in useGlobe still creates a new
Coordinate array for every point on each frame, which defeats the pre-allocation
in pathCoordinateBuffers. Update animate() so it reuses the existing per-point
buffer entries instead of assigning a fresh tuple to coords[i], mutating the
stored Coordinate values in place while keeping world.pathsData(chartBuffer) fed
from the same preallocated arrays.
- Around line 50-59: The Globe setup in useGlobe currently hotlinks textures via
globeImageUrl, bumpImageUrl, and backgroundImageUrl from unpkg without any
fallback or error handling. Update the Globe initialization to use
self-hosted/local asset URLs (consistent with the existing GeoJSON self-hosting
approach) or add a fallback path if the remote textures fail to load, so the
texture setup remains reliable in production.
- Around line 133-135: The GeoJSON load path in useGlobe should validate the
fetch response before calling res.json(), since fetch can resolve on non-2xx
statuses and produce unclear parse failures. Update the promise chain around the
unpkg request in useGlobe to check the response status first, then either parse
the body or throw a clear "failed to fetch boundaries" error so the existing
catch handles it consistently.

In `@src/index.css`:
- Around line 2-9: The global `body` styles in `src/index.css` are hardcoding
theme values instead of using the design-token system. Update the `body` rule to
use the existing CSS custom properties from `tokens.css` for background, font
family, and text color, specifically replacing the raw color and font stack with
the corresponding `--color-bg-primary`, `--font-family-base`, and
`--color-text-primary` tokens.

In `@src/styles/components/auth-layout.css`:
- Around line 5-16: The auth layout color tokens are being declared on the
global :root inside a component stylesheet, which can leak into or collide with
app-wide tokens. Move these custom properties into the auth-specific scope used
by this stylesheet (for example the auth layout wrapper/class defined in
auth-layout styles) and keep the existing variable names so only the auth layout
consumes them. Use the :root token block in auth-layout.css and the auth layout
container selector as the main places to update.

In `@src/styles/reset.css`:
- Around line 17-23: No code change is needed here: the `body` reset in
`reset.css` already uses the canonical `text-rendering: optimizeLegibility`
value, and the `value-keyword-case` warning is only about casing style. Leave
the `body` rule as-is and do not alter `optimizeLegibility`.

In `@src/styles/tokens.css`:
- Around line 33-35: No code change is needed here: the font-family tokens in
tokens.css use canonical proper-noun casing (for example BlinkMacSystemFont,
Roboto, Helvetica, Arial, and Consolas), so keep the existing values in the
--font-family-base and --font-family-mono declarations as-is and do not apply
value-keyword-case lowercasing.

In `@src/test/Login.test.tsx`:
- Around line 8-13: The Login test setup only mocks authService, so the
successful-login flow still uses the real useUserStore singleton and its
persist/localStorage side effects. Update the test mocks to include useUserStore
as well, and make its setAccessToken and setUser actions jest/vi fns so Login
can be tested without touching Zustand persistence. Keep the mock aligned with
the existing authService mock in Login.test.tsx so the unit test stays isolated
and does not leak state across cases.

In `@src/types/globe.types.ts`:
- Around line 11-54: Remove the unused hand-rolled GlobeInstance and
GlobeControls interfaces from globe.types.ts, since useGlobe.ts relies on the
real GlobeInstance type re-exported from globe.gl. Keep only the app-specific
type definitions (Coordinate, PathData, LabelData, GeoFeature, PointOfView,
GeoJSON) and update any local references so there is no duplicate name-colliding
GlobeInstance in the codebase.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a6f6d6bb-2efa-4874-8776-53004f216f0c

📥 Commits

Reviewing files that changed from the base of the PR and between 427d4d3 and f87c377.

⛔ Files ignored due to path filters (4)
  • package-lock.json is excluded by !**/package-lock.json
  • public/favicon.svg is excluded by !**/*.svg
  • public/icons.svg is excluded by !**/*.svg
  • public/trading_banner.png is excluded by !**/*.png
📒 Files selected for processing (51)
  • .env.example
  • .github/workflows/ci.yml
  • .gitignore
  • .node-version
  • .nvmrc
  • .oxlintrc.json
  • .prettierignore
  • .prettierrc.json
  • .vscode/extensions.json
  • .vscode/settings.json
  • README.md
  • index.html
  • package.json
  • src/App.tsx
  • src/components/ForgotPassword.tsx
  • src/components/Login.tsx
  • src/components/Register.tsx
  • src/components/ResetPassword.tsx
  • src/components/SuccessPage.tsx
  • src/components/ui/AuthLayout/AuthLayout.tsx
  • src/components/ui/ErrorBoundary/ErrorBoundary.tsx
  • src/components/ui/ErrorBoundary/index.ts
  • src/constants/routes.constants.ts
  • src/features/auth/AuthGuard.tsx
  • src/features/auth/auth.service.ts
  • src/features/auth/auth.types.ts
  • src/features/dashboard/DashboardPage.tsx
  • src/features/globe/GlobeView.tsx
  • src/features/globe/globe.constants.ts
  • src/features/globe/nations.constants.ts
  • src/features/globe/useGlobe.ts
  • src/index.css
  • src/lib/api.client.ts
  • src/main.tsx
  • src/stores/userStore.ts
  • src/styles/components/auth-layout.css
  • src/styles/components/country-list.css
  • src/styles/components/dashboard.css
  • src/styles/components/overlay.css
  • src/styles/reset.css
  • src/styles/tokens.css
  • src/test/Login.test.tsx
  • src/test/setup.ts
  • src/types.d.ts
  • src/types/env.d.ts
  • src/types/globe.types.ts
  • tsconfig.app.json
  • tsconfig.json
  • tsconfig.node.json
  • vite.config.ts
  • vitest.config.ts

Comment thread .github/workflows/ci.yml
Comment on lines +15 to +22
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '1,120p' .github/workflows/ci.yml

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 1353


🏁 Script executed:

sed -n '1,160p' .github/workflows/ci.yml

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 1353


🏁 Script executed:

printf 'Workflow file:\n'
sed -n '1,220p' .github/workflows/ci.yml
printf '\nGit usage in workflow:\n'
rg -n '\bgit\b' .github/workflows/ci.yml

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 1392


Disable persisted credentials on checkout.

actions/checkout writes the token into git config by default. This job only builds and tests, so persist-credentials: false removes an unnecessary auth path.

🔒 Proposed fix
      - name: Checkout code
        uses: actions/checkout@v4
+       with:
+         persist-credentials: false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 15-16: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 15 - 22, Disable persisted credentials
in the Checkout code step by updating the actions/checkout@v4 configuration to
set persist-credentials to false. This change belongs in the same workflow job
that also uses Setup Node.js, and the key symbol to locate is the Checkout code
step under actions/checkout@v4.

Source: Linters/SAST tools

Comment thread src/components/Login.tsx
Comment on lines +37 to +54
try {
setIsLoading(true);
const tokenResponse = await authService.login(email, password);
setAccessToken(tokenResponse.access_token);
const userProfile = await authService.getMe();
setUser(userProfile);
onLoginSuccess();
} catch (err: any) {
console.error('Login failed', err);
if (err.response?.status === 401 || err.response?.status === 400) {
setErrorMsg('Invalid email or password.');
} else {
setErrorMsg('An unexpected error occurred. Please try again.');
}
} finally {
setIsLoading(false);
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

git ls-files | rg '^(src/components/Login\.tsx|src/store/userStore\.ts|src/.+AuthGuard.+|src/.+guard.+|src/.+auth.+)'

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 355


🏁 Script executed:

sed -n '1,220p' src/components/Login.tsx && printf '\n---\n' && sed -n '1,260p' src/store/userStore.ts && printf '\n---\n' && rg -n "AuthGuard|isAuthenticated|accessToken|setAccessToken|getMe\\(" src

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 7067


🏁 Script executed:

#!/bin/sh
set -eu

for f in src/components/Login.tsx src/store/userStore.ts; do
  echo "===== $f ====="
  nl -ba "$f" | sed -n '1,240p'
  echo
done

echo "===== matches ====="
rg -n "AuthGuard|isAuthenticated|accessToken|setAccessToken|getMe\\(" src

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 2594


🏁 Script executed:

sed -n '1,220p' src/stores/userStore.ts && printf '\n---\n' && sed -n '1,220p' src/features/auth/AuthGuard.tsx && printf '\n---\n' && sed -n '1,220p' src/lib/api.client.ts && printf '\n---\n' && sed -n '1,220p' src/features/auth/auth.service.ts && printf '\n---\n' && sed -n '1,180p' src/App.tsx

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 12256


🏁 Script executed:

cat -n src/stores/userStore.ts && printf '\n---\n' && cat -n src/features/auth/AuthGuard.tsx && printf '\n---\n' && cat -n src/lib/api.client.ts && printf '\n---\n' && cat -n src/features/auth/auth.service.ts

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 10953


🏁 Script executed:

cat -n src/App.tsx | sed -n '1,180p'

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 4201


Reset auth state if getMe() fails
setAccessToken() marks the store authenticated before the profile load finishes. If getMe() throws, the login form stays on screen but the store still has accessToken/isAuthenticated = true, and AuthGuard will treat that session as valid. Clear the token/auth flag in the catch path instead of leaving a partial session behind.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Login.tsx` around lines 37 - 54, The login flow in Login’s
submit handler leaves a partial authenticated session if authService.getMe()
fails after setAccessToken() succeeds. Update the catch path in the same handler
to clear the auth state (reset the access token and authenticated flag, and
avoid leaving user state set) before showing the error, so AuthGuard does not
treat the failed login as valid.

Comment on lines +122 to +128
// ── 4. Scroll-to-rotate ───────────────────────────────────────────────
let scrollAngle = 0;
const handleWheel = (e: WheelEvent) => {
scrollAngle += e.deltaY * GLOBE_CONFIG.SCROLL_SENSITIVITY;
world.pointOfView({ lat: 0, lng: scrollAngle, altitude: 2 });
};
window.addEventListener('wheel', handleWheel, { passive: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== useGlobe file outline ==\n'
ast-grep outline src/features/globe/useGlobe.ts --view expanded || true

printf '\n== relevant occurrences ==\n'
rg -n "pointOfView|flyTo|controls\\(|SCROLL_SENSITIVITY|handleWheel|wheel" src/features/globe/useGlobe.ts src -g '!**/node_modules/**' || true

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 3439


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== useGlobe relevant section ==\n'
sed -n '1,220p' src/features/globe/useGlobe.ts | cat -n

printf '\n== globe types ==\n'
sed -n '1,220p' src/types/globe.types.ts | cat -n

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 12172


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== globe package references ==\n'
rg -n "3d-globe|globe\\.gl? |pointOfView\\(|controls\\(" package.json pnpm-lock.yaml package-lock.json yarn.lock src -g '!**/node_modules/**' || true

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 1929


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== package dependencies mentioning globe ==\n'
sed -n '1,220p' package.json | cat -n

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 1955


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,220p' src/features/globe/useGlobe.ts | cat -n

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 8801


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== package manifests and lockfile refs for globe ==\n'
rg -n '"globe|three-globe|worldwind|pointOfView|controls"' package.json pnpm-lock.yaml package-lock.json yarn.lock src/types/globe.types.ts -g '!**/node_modules/**' || true

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 773


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== package.json ==\n'
sed -n '1,220p' package.json | cat -n

printf '\n== globe-related lockfile matches ==\n'
for f in pnpm-lock.yaml package-lock.json yarn.lock; do
  [ -f "$f" ] && { echo "--- $f ---"; rg -n "globe|three-globe|controls|pointOfView" "$f" || true; }
done

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 2519


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== useGlobe.ts with line numbers ==\n'
sed -n '1,220p' src/features/globe/useGlobe.ts | cat -n

printf '\n== globe.types.ts with line numbers ==\n'
sed -n '1,220p' src/types/globe.types.ts | cat -n

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 12197


🌐 Web query:

globe.gl pointOfView getter current camera position api

💡 Result:

In globe.gl, the pointOfView method serves as both a getter and a setter for the camera's position [1][2]. To retrieve the current camera position, call the method without any arguments [3][4]: const currentPov = globe.pointOfView; // Returns { lat: number, lng: number, altitude: number } To set a new camera position, pass an object containing the target coordinates and an optional transition duration (in milliseconds) [1][2]: globe.pointOfView({ lat, lng, altitude }, [ms]); Additionally, you can use the onZoom callback to listen for changes to the camera position triggered by user interaction [2][5]: globe.onZoom(pov => { console.log('New camera position:', pov); });

Citations:


Preserve the current camera state in the wheel handler
world.pointOfView({ lat: 0, lng: scrollAngle, altitude: 2 }) resets the focused view on every wheel tick, so flyTo() loses its lat/altitude immediately and the wheel zoom gets overwritten. Read the current POV and update only lng, or skip this handler while a country is active.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/globe/useGlobe.ts` around lines 122 - 128, The wheel handler in
useGlobe should preserve the current camera state instead of resetting it on
every scroll tick. Update handleWheel so it reads the existing world point of
view and only adjusts lng, or guard the handler when a country is active, so
flyTo and the current lat/altitude are not immediately overwritten.

Comment on lines +174 to +182
// ── Cleanup ───────────────────────────────────────────────────────────
return () => {
isMounted = false;
cancelAnimationFrame(animationFrameId);
window.removeEventListener('resize', handleResize);
window.removeEventListener('wheel', handleWheel);
container.innerHTML = '';
globeInstanceRef.current = null;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and locate the cleanup/instance creation code.
ast-grep outline src/features/globe/useGlobe.ts --view expanded || true
printf '\n--- useGlobe.ts (relevant lines) ---\n'
sed -n '1,260p' src/features/globe/useGlobe.ts

printf '\n--- search for _destructor / resetProps / globe instance usage ---\n'
rg -n "_destructor|resetProps|new Globe|globeInstanceRef|container\.innerHTML" src || true

printf '\n--- search for globe.gl typings in repo ---\n'
rg -n "interface GlobeGenericInstance|_destructor\(\): void|GlobeGenericInstance" . || true

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 8568


🌐 Web query:

globe.gl _destructor GlobeGenericInstance dispose renderer scene controls documentation

💡 Result:

The globe.gl library provides an internal _destructor method, which is defined in the GlobeGenericInstance interface [1][2]. This method is designed to perform cleanup of the component's internal state [3][4]. When you need to clean up a globe.gl instance, particularly in environments where components are frequently created and destroyed (like React), you should invoke this method: globeInstance._destructor; In addition to calling _destructor, manual DOM cleanup is often recommended to ensure the browser garbage collector (GC) can fully release the memory associated with the Three.js renderer and scene [5]. A common approach is to clear the container element's innerHTML after calling the destructor: // Example cleanup flow globeInstance._destructor; containerElement.innerHTML = ''; // Removes the canvas and associated DOM elements Regarding the specific components mentioned: - Renderer, Scene, and Controls: globe.gl provides accessors to these Three.js objects (e.g.,.renderer,.scene,.controls) [6], but their lifecycle is primarily managed by the library. Calling _destructor signals the internal processes to stop animation loops (requestAnimationFrame) and clean up event listeners [3][4]. - Memory Leaks: While the library has built-in mechanisms to dispose of resources [7][8], some users have historically encountered issues with incremental data updates (such as textures or geometries) not being disposed of properly [9]. If you experience memory growth during runtime, ensure your data is memoized (especially in React) [7] or consider verifying the state of renderer.info.memory [9]. For complex applications, particularly those using react-globe.gl, be aware that specialized methods like globeTileEngineClearCache may also be available depending on your version if you are dealing with persistent tile caching issues [10].

Citations:


Dispose the globe.gl instance in cleanup
container.innerHTML = '' only removes the canvas; call world._destructor() first so the renderer, scene, controls, and WebGL resources are released before unmount/remount cycles.

🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 179-179: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: container.innerHTML = ''
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation

(dom-content-modification)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/globe/useGlobe.ts` around lines 174 - 182, Cleanup in useGlobe
currently clears the DOM but does not fully release the globe.gl resources.
Update the unmount cleanup in useGlobe to dispose the globe instance first by
calling world._destructor() before removing event listeners and clearing the
container, then null out globeInstanceRef.current so renderer, scene, controls,
and WebGL resources are released properly.

Comment thread src/index.css
Comment on lines +1 to +14
/* Reset margins and set core black backgrounds */
body {
margin: 0;
padding: 0;
background-color: #000;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
overflow: hidden;
color: #fff;
}

#root {
width: 100vw;
height: 100vh;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Files of interest ==\n'
git ls-files 'src/*' 'src/**/*' | sed -n '1,200p'

printf '\n== main entry and routing references ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' "index\.css|BrowserRouter|Routes|Route|createBrowserRouter|RouterProvider|react-router" src

printf '\n== index.css and related layout styles ==\n'
for f in src/index.css src/main.tsx; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    cat -n "$f"
  fi
done

printf '\n== Search for body/overflow/page root classes ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' "overflow\s*:\s*hidden|body\s*\\{|globe-page-root|scroll|overflow" src

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 10611


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== App routes and page shells ==\n'
cat -n src/App.tsx

printf '\n== Reset and token globals ==\n'
cat -n src/styles/reset.css
printf '\n--- tokens.css ---\n'
cat -n src/styles/tokens.css

printf '\n== Auth and dashboard layout styles/components ==\n'
for f in \
  src/components/Login.tsx \
  src/components/Register.tsx \
  src/components/ForgotPassword.tsx \
  src/components/ResetPassword.tsx \
  src/features/dashboard/DashboardPage.tsx \
  src/components/ui/AuthLayout/AuthLayout.tsx \
  src/styles/components/auth-layout.css \
  src/styles/components/dashboard.css; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    cat -n "$f"
    echo
  fi
done

printf '\n== Body/root/full-height usage ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' "height:\s*100vh|min-height:\s*100vh|overflow:\s*hidden|overflow-y:\s*auto|position:\s*fixed|document\.body|body\s*\\{" src

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 50412


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== auth-layout.css: layout and overflow rules ==\n'
sed -n '1,260p' src/styles/components/auth-layout.css | cat -n

printf '\n== dashboard.css ==\n'
sed -n '1,220p' src/styles/components/dashboard.css | cat -n

printf '\n== GlobeView inline styles that intentionally lock overflow ==\n'
sed -n '1,180p' src/features/globe/GlobeView.tsx | cat -n

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 12375


Move the body scroll lock out of the global stylesheet
src/index.css:2-8 is loaded app-wide from src/main.tsx, so body { overflow: hidden; } affects Login/Register/Forgot Password/Reset Password/Dashboard too. Those routes don’t create their own scroll container, so any overflow on smaller viewports will be clipped with no way to scroll.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/index.css` around lines 1 - 14, The global body styles in src/index.css
are applying a permanent scroll lock via body overflow hidden, which affects all
routes loaded from src/main.tsx. Remove the overflow hidden rule from the global
stylesheet and move the scroll-lock behavior into the specific fullscreen
layout/component that needs it, keeping Login/Register/Forgot Password/Reset
Password/Dashboard able to scroll when their content overflows. Use the body and
`#root` styles in src/index.css as the place to locate and adjust this change.

Comment thread src/lib/api.client.ts
Comment on lines +20 to +58
// Response Interceptor: Handle 401s and automatic token refresh
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;

// If it's a 401 and we haven't already retried this request
if (error.response?.status === 401 && !originalRequest._retry) {
// Prevent infinite loops on the refresh endpoint itself
if (originalRequest.url === '/auth/refresh') {
// Refresh failed (cookie expired/invalid) -> force logout
useUserStore.getState().logout();
return Promise.reject(error);
}

originalRequest._retry = true;

try {
// Attempt to get a new access token using the HttpOnly cookie
const { data } = await axios.post(`${API_URL}/auth/refresh`, {}, { withCredentials: true });

const newAccessToken = data.access_token;

// Update the store with the new token
useUserStore.getState().setAccessToken(newAccessToken);

// Update the original request's header and retry
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
return apiClient(originalRequest);
} catch (refreshError) {
// Refresh failed (e.g., cookie expired) -> logout
useUserStore.getState().logout();
return Promise.reject(refreshError);
}
}

return Promise.reject(error);
},
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Files in scope:\n'
git ls-files src/lib/api.client.ts src | sed -n '1,120p'

printf '\nOutline of src/lib/api.client.ts:\n'
ast-grep outline src/lib/api.client.ts --view expanded || true

printf '\nRelevant auth-related symbols across src:\n'
rg -n --hidden -S "refreshPromise|_retry|interceptors.response.use|auth/refresh|setAccessToken|logout\\(" src

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 2664


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Read src/lib/api.client.ts with line numbers:\n'
cat -n src/lib/api.client.ts | sed -n '1,220p'

printf '\nSearch for any refresh deduplication or queue logic:\n'
rg -n --hidden -S "queue|single-flight|in-flight|refreshPromise|isRefreshing|failedRequests|subscribers" src

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 2686


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant file sections if present.
if [ -f src/lib/api.client.ts ]; then
  sed -n '1,220p' src/lib/api.client.ts
fi

# Probe whether the refresh path is already deduplicated elsewhere.
rg -n --hidden -S "refresh.*Promise|Promise.*refresh|isRefreshing|queue.*401|401.*queue|single[- ]flight" src || true

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 2250


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== src/lib/api.client.ts ==="
if [ -f src/lib/api.client.ts ]; then
  cat -n src/lib/api.client.ts | sed -n '1,220p'
fi

echo
echo "=== related auth flow search ==="
rg -n --hidden -S "withCredentials: true|/auth/refresh|setAccessToken\\(|logout\\(|_retry|interceptors.response.use" src || true

Repository: TradeDisciplineAI/ai-trading-discipline-copilot-frontned

Length of output: 3790


Serialize the refresh request path src/lib/api.client.ts:27-49 — multiple 401s will each fire their own /auth/refresh call and retry independently. If refresh tokens are rotated server-side, those parallel calls can invalidate one another; the refresh call also has no timeout, so a hung backend leaves retries stuck. Add a single in-flight refresh promise/queue and a timeout on the refresh request.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/api.client.ts` around lines 20 - 58, The response interceptor in
apiClient is allowing multiple 401s to trigger separate /auth/refresh calls,
which can race and break token rotation handling. Update the refresh flow in the
response interceptor to serialize refresh attempts with a single shared
in-flight promise/queue so concurrent requests wait for the same refresh result,
then retry using the new token. Also add a timeout to the axios.post refresh
call so a stalled backend does not leave requests hanging indefinitely.

Comment thread src/stores/userStore.ts
Comment on lines +49 to +61
initAuth: async () => {
try {
// If we have a user in localStorage, we can optimistically say they might be authenticated.
// But to be sure, and to get the access token in memory, we fetch their profile.
// Since there is no access token in memory yet, the Axios interceptor will hit a 401
// when it tries to get /auth/me, which will trigger the /auth/refresh flow automatically!
const user = await authService.getMe();
set({ user, isAuthenticated: true });
} catch (error) {
// If refresh fails, clear the state
set({ user: null, isAuthenticated: false, accessToken: null });
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Unused catch parameter flagged by lint.

Static analysis flags the unused error binding; at minimum log it for debuggability of silent auth-init failures.

Proposed fix
-        } catch (error) {
+        } catch (error) {
+          console.error('initAuth failed', error);
           // If refresh fails, clear the state
           set({ user: null, isAuthenticated: false, accessToken: null });
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
initAuth: async () => {
try {
// If we have a user in localStorage, we can optimistically say they might be authenticated.
// But to be sure, and to get the access token in memory, we fetch their profile.
// Since there is no access token in memory yet, the Axios interceptor will hit a 401
// when it tries to get /auth/me, which will trigger the /auth/refresh flow automatically!
const user = await authService.getMe();
set({ user, isAuthenticated: true });
} catch (error) {
// If refresh fails, clear the state
set({ user: null, isAuthenticated: false, accessToken: null });
}
},
initAuth: async () => {
try {
// If we have a user in localStorage, we can optimistically say they might be authenticated.
// But to be sure, and to get the access token in memory, we fetch their profile.
// Since there is no access token in memory yet, the Axios interceptor will hit a 401
// when it tries to get /auth/me, which will trigger the /auth/refresh flow automatically!
const user = await authService.getMe();
set({ user, isAuthenticated: true });
} catch (error) {
console.error('initAuth failed', error);
// If refresh fails, clear the state
set({ user: null, isAuthenticated: false, accessToken: null });
}
},
🧰 Tools
🪛 GitHub Check: Type-check · Lint · Format · Test · Build

[warning] 57-57: eslint(no-unused-vars)
Catch parameter 'error' is caught but never used.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/stores/userStore.ts` around lines 49 - 61, The initAuth catch block in
userStore has an unused error binding; use the caught error instead of ignoring
it. Update the initAuth method to log the failure from authService.getMe before
clearing state, so silent auth-init issues are debuggable, and keep the existing
reset logic for user, isAuthenticated, and accessToken in the catch path.

Source: Linters/SAST tools

Comment on lines +85 to +91
.country-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
box-shadow: 0 0 6px currentColor;
flex-shrink: 0;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Stylelint: lowercase currentcolor.

🎨 Fix
 .country-indicator {
   width: 8px;
   height: 8px;
   border-radius: 50%;
-  box-shadow: 0 0 6px currentColor;
+  box-shadow: 0 0 6px currentcolor;
   flex-shrink: 0;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.country-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
box-shadow: 0 0 6px currentColor;
flex-shrink: 0;
}
.country-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
box-shadow: 0 0 6px currentcolor;
flex-shrink: 0;
}
🧰 Tools
🪛 Stylelint (17.14.0)

[error] 89-89: Expected "currentColor" to be "currentcolor" (value-keyword-case)

(value-keyword-case)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/styles/components/country-list.css` around lines 85 - 91, The
.country-indicator style uses the nonstandard casing currentColor, which
triggers the Stylelint warning. Update the box-shadow declaration in the country
list styles to use the lowercase currentcolor token so the rule passes while
preserving the same visual effect.

Source: Linters/SAST tools

Comment thread src/test/Login.test.tsx
Comment on lines +18 to +23
const renderLogin = (onSuccess = vi.fn(), onBack = vi.fn()) =>
render(
<MemoryRouter>
<Login onLoginSuccess={onSuccess} onBackToHome={onBack} />
</MemoryRouter>,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

onBackToHome is not a valid Login prop.

Login's props interface only defines onLoginSuccess, onNavigateToSignup, and onNavigateToForgot (see src/components/Login.tsx lines 7-11). This test passes onBackToHome={onBack}, which doesn't exist on LoginProps. TypeScript performs excess-property checking on JSX attributes, so this will fail type-checking / CI (tsc), and the prop is silently dropped at runtime regardless.

🐛 Suggested fix
-const renderLogin = (onSuccess = vi.fn(), onBack = vi.fn()) =>
+const renderLogin = (onSuccess = vi.fn()) =>
   render(
     <MemoryRouter>
-      <Login onLoginSuccess={onSuccess} onBackToHome={onBack} />
+      <Login onLoginSuccess={onSuccess} />
     </MemoryRouter>,
   );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const renderLogin = (onSuccess = vi.fn(), onBack = vi.fn()) =>
render(
<MemoryRouter>
<Login onLoginSuccess={onSuccess} onBackToHome={onBack} />
</MemoryRouter>,
);
const renderLogin = (onSuccess = vi.fn()) =>
render(
<MemoryRouter>
<Login onLoginSuccess={onSuccess} />
</MemoryRouter>,
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/Login.test.tsx` around lines 18 - 23, The test helper in
Login.test.tsx is passing an invalid Login prop: replace onBackToHome with one
of the supported LoginProps callbacks. Update renderLogin to use the actual
Login component API from Login.tsx (for example, pass onNavigateToSignup or
onNavigateToForgot if that is what the test needs, or remove the extra prop
entirely) so the JSX matches the component’s defined props and TypeScript checks
pass.

@sreenandpk
sreenandpk merged commit 2dce75f into develop Jul 9, 2026
1 of 2 checks passed
@sreenandpk
sreenandpk deleted the feature/FRO-2-folder branch July 16, 2026 13:21
@coderabbitai coderabbitai Bot mentioned this pull request Jul 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants