Skip to content

Auth layout & UI update#2

Merged
Dali11 merged 1 commit into
mainfrom
ui-update-and-auth-layout
Apr 27, 2026
Merged

Auth layout & UI update#2
Dali11 merged 1 commit into
mainfrom
ui-update-and-auth-layout

Conversation

@Dali11

@Dali11 Dali11 commented Apr 27, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Implemented sign-in and sign-up forms with email validation and password requirements.
    • Added About section showcasing key features and benefits.
    • Added Contact section with call-to-action.
    • Integrated testimonials display with user ratings and reviews.
    • Enabled smooth scroll navigation for improved page traversal.
    • Added country selector field for user registration.
  • Style

    • Enhanced dark mode background color.
    • Introduced new form input and button styling utilities.
    • Added card hover effects with visual feedback.

@coderabbitai

coderabbitai Bot commented Apr 27, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The pull request implements a comprehensive UI overhaul featuring form-based authentication pages with validation, new Home page sections (About, ContactUs, Testimonial), responsive layout adjustments, and adds numerous UI component primitives (dialog, popover, select, command, input-group) styled with Tailwind. It updates navigation to use hash-based anchors, refreshes styling globals, and introduces form-related type definitions and dependencies.

Changes

Cohort / File(s) Summary
Authentication Pages
app/(auth)/layout.tsx, app/(auth)/sign-in/page.tsx, app/(auth)/sign-up/page.tsx
Auth layout now displays a responsive two-column structure with Testimonial component on the right. Sign-in/sign-up pages converted to client components with full react-hook-form integration, field validation (email pattern, password length, country selection), error display, and form submission wiring.
Home and Root Pages
app/(root)/page.tsx, components/Home.tsx
Root page now renders About and ContactUs sections alongside Home, each wrapped in semantic sections with anchor IDs. Home component simplified with revised spacing/alignment, new background image, and updated CTA button styling.
New Form Components
components/forms/InputField.tsx, components/forms/FooterLink.tsx, components/forms/CountrySelectField.tsx, components/forms/Testimonial.tsx
New form field components: InputField for text inputs with validation, FooterLink for navigation, CountrySelectField with searchable popover country picker and flag emojis, and Testimonial component displaying review ratings and author details.
New UI Primitives
components/ui/command.tsx, components/ui/dialog.tsx, components/ui/popover.tsx, components/ui/select.tsx, components/ui/input-group.tsx, components/ui/input.tsx, components/ui/label.tsx, components/ui/textarea.tsx
Comprehensive UI component library built on Radix primitives and Tailwind styling, providing command palette, dialog, popover, select dropdown, input groups with addons, text inputs, labels, and textareas with consistent data-slot attributes and styling variants.
Styling and Configuration
app/globals.css, app/layout.tsx
Global CSS updated with smooth scroll behavior, new dark-mode background color, and utility classes (.card-hover, .form-input, .yellow-btn). Root layout explicitly includes font-sans in className.
Navigation and Constants
components/NavItems.tsx, lib/constants.ts
NavItems now implements smooth scroll handling via handleScroll function triggered on link clicks. Constants updated to use hash anchors (#home, #about, #contact), removed auth nav items, and added HOME_CARDS and TESTIMONIALS data arrays.
Type Definitions and Dependencies
types/global.d.ts, types/react-select-country-list.d.ts, package.json
Global type declarations added for form payloads, component props, and domain models (Stock, Alert, User, etc.). New ambient module for react-select-country-list. Dependencies updated: lucide-react ^1.9.0→^1.11.0, shadcn ^4.4.0→^4.5.0, plus new packages cmdk and react-hook-form.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Dali11/JournalFX#1: Modifies and expands the same auth layout, sign-in/sign-up pages, root/home page, and Home component, replacing placeholder implementations with full form and layout components.

Poem

🐰 Hops of joy through forms so neat,
With inputs, dialogs, oh what a treat!
Popovers pop and countries dance,
Auth flows smooth in smooth scroll's trance,
A UI garden, fresh and bright,
Your app now hops with pure delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Auth layout & UI update' accurately reflects the main changes, which include a restructured authentication layout with testimonial component integration and comprehensive UI updates across auth pages and form components.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: 13

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/NavItems.tsx (1)

14-18: ⚠️ Potential issue | 🟡 Minor

isActive no longer matches hash-based hrefs.

After the move to in-page anchors (#home, #about, #contact per lib/constants.ts), pathname.startsWith('#about') will never be true — pathname from usePathname() does not include the fragment. As a result every nav item except #home-when-on-/ is rendered as inactive. Consider tracking the current section via IntersectionObserver or window.location.hash if active styling is intended.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/NavItems.tsx` around lines 14 - 18, The isActive function
currently uses pathname from usePathname() and thus never matches
fragment/hash-based hrefs (e.g., '#about'); update isActive (or the nav active
logic that uses isActive) to consider window.location.hash or an
IntersectionObserver-driven currentSection state: check if pathname === '/' and
href === '#home' still matches, otherwise treat the link as active when either
pathname.startsWith(path) OR window.location.hash === path (or when
currentSection === path if you implement IntersectionObserver to set
currentSection); reference the isActive function, pathname variable, and the nav
item hrefs to locate where to add this hash/observer-based check.
🟡 Minor comments (10)
app/globals.css-147-150 (1)

147-150: ⚠️ Potential issue | 🟡 Minor

Stylelint: empty line expected before declaration.

Stylelint flags scroll-behavior: smooth; for declaration-empty-line-before. Also there's a trailing space after font-sans;.

♻️ Proposed fix
   html {
-    `@apply` font-sans; 
+    `@apply` font-sans;
+
     scroll-behavior: smooth;
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/globals.css` around lines 147 - 150, Fix the CSS formatting in the html
rule: remove the trailing space after the font-sans declaration and insert a
blank line (empty line) before the scroll-behavior declaration so the block
satisfies Stylelint's declaration-empty-line-before rule; update the html { ...
} block accordingly (the two identifiers to locate are the html selector and the
declarations font-sans and scroll-behavior).
components/ContactUs.tsx-10-10 (1)

10-10: ⚠️ Potential issue | 🟡 Minor

Fix typos in copy.

Three text errors in this paragraph: "trades" → "traders", "controlof" → "control of", "tradiing" → "trading".

✏️ Proposed fix
-            <p className="text-center mt-3 text-gray-500 sm:w-150">Join thousands of trades who are already improving their results with JournalFX. Create your free account and take controlof your tradiing journey.</p>
+            <p className="text-center mt-3 text-gray-500 sm:w-150">Join thousands of traders who are already improving their results with JournalFX. Create your free account and take control of your trading journey.</p>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ContactUs.tsx` at line 10, The paragraph string in the ContactUs
component contains typos: change "trades" to "traders", "controlof" to "control
of", and "tradiing" to "trading" in the JSX paragraph (the element with
className "text-center mt-3 text-gray-500 sm:w-150") so the copy reads correctly
about traders improving results with JournalFX and creating a free account to
take control of their trading journey.
components/ContactUs.tsx-14-15 (1)

14-15: ⚠️ Potential issue | 🟡 Minor

Change CTA link destination to /sign-up.

The button labeled "Create Free Account" currently links to /sign-in, which is the sign-in page for existing users. Account creation happens at /sign-up. Update href="/sign-in" to href="/sign-up" to match the button's intent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ContactUs.tsx` around lines 14 - 15, In the ContactUs component
update the Link for the "Create Free Account" CTA: change href="/sign-in" to
href="/sign-up" on the Link element (the JSX element in ContactUs.tsx that
contains the text "Create Free Account") so the button routes to the account
creation page.
components/Home.tsx-25-27 (1)

25-27: ⚠️ Potential issue | 🟡 Minor

Fix Image aspect ratio to prevent distortion.

The current dimensions (900×150) specify a 6:1 aspect ratio, but home-holder.png is actually 1536×1024 (1.5:1 ratio). This mismatch will severely distort the image. Use dimensions that maintain the intrinsic aspect ratio—either 900×600 or 225×150—or use the fill prop with a sized container.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Home.tsx` around lines 25 - 27, The Image in components/Home.tsx
currently hardcodes width={900} height={150}, which distorts the 1536×1024
asset; update the <Image ... /> usage to maintain the intrinsic aspect ratio by
using either width={900} height={600} (or width={225} height={150}) to match the
3:2 ratio, or switch to the Next/Image fill mode by wrapping the Image in a
positioned container and using the fill prop so the image scales responsively
without distortion. Locate the Image element in Home.tsx and apply one of these
fixes to preserve aspect ratio.
components/Home.tsx-16-20 (1)

16-20: ⚠️ Potential issue | 🟡 Minor

Fix CTA link destinations to match routing patterns and acquisition flow.

  • "Get Started" (line 16) and "Create Free Account" in components/ContactUs.tsx link to /sign-in, but acquisition CTAs should direct users to /sign-up. The sign-in page is for existing users.
  • "Learn More" (line 20) links to /about, but no /about route exists. The app uses hash-based in-page navigation (see lib/constants.ts where "About" maps to #about). Change to href="/#about" to align with the established navigation pattern.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Home.tsx` around lines 16 - 20, Update the CTA link targets:
change the "Get Started" Link in components/Home.tsx (the Link that wraps the
ArrowRight component) from href="/sign-in" to href="/sign-up", and likewise
update the "Create Free Account" CTA in components/ContactUs.tsx from "/sign-in"
to "/sign-up"; also fix the "Learn More" Link in components/Home.tsx (the Link
with border-2/... classes) to use the in-page hash route href="/#about" instead
of "/about" so it matches the hash-based navigation defined in lib/constants.ts.
components/About.tsx-17-17 (1)

17-17: ⚠️ Potential issue | 🟡 Minor

Typo: cursor-poitercursor-pointer.

Tailwind silently drops the unknown class, so the hover affordance never renders.

🐛 Suggested fix
-     <li key={title} className="card-hover flex flex-col gap-3 bg-gray-700 border border-gray-600 text-white p-4 lg:p-6 rounded-xl cursor-poiter shadow-lg shadow-black/50 flex-1">
+     <li key={title} className="card-hover flex flex-col gap-3 bg-gray-700 border border-gray-600 text-white p-4 lg:p-6 rounded-xl cursor-pointer shadow-lg shadow-black/50 flex-1">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/About.tsx` at line 17, Update the className on the list item in
the About component: fix the typo "cursor-poiter" to "cursor-pointer" in the li
element's className (the element with key={title}) so Tailwind applies the
cursor affordance correctly.
lib/constants.ts-16-37 (1)

16-37: ⚠️ Potential issue | 🟡 Minor

TESTIMONIALS ids are out of sequence (1, 3, 2).

Likely unintentional — should be 1, 2, 3. Not functionally broken (only used as React keys), but worth fixing for clarity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/constants.ts` around lines 16 - 37, The TESTIMONIALS array has
non-sequential id values (1, 3, 2); update the id fields inside the TESTIMONIALS
constant so they are ordered sequentially (1, 2, 3) to avoid confusion and keep
keys predictable when used in React (modify the id values in the objects inside
the exported TESTIMONIALS array).
app/(auth)/sign-up/page.tsx-45-45 (1)

45-45: ⚠️ Potential issue | 🟡 Minor

minLength validators need a message to display.

InputField renders error.message; minLength: 2 / minLength: 8 as bare numbers produce a FieldError without a message, so users will see an empty error line.

-            validation={{ required: 'Full name is required', minLength: 2 }}
+            validation={{
+              required: 'Full name is required',
+              minLength: { value: 2, message: 'Full name must be at least 2 characters' },
+            }}
...
-            validation={{ required: 'Password is required', minLength: 8 }}
+            validation={{
+              required: 'Password is required',
+              minLength: { value: 8, message: 'Password must be at least 8 characters' },
+            }}

Also applies to: 73-73

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(auth)/sign-up/page.tsx at line 45, The minLength validators passed to
the InputField validation prop are plain numbers (e.g., in sign-up page.tsx
where validation={{ required: 'Full name is required', minLength: 2 }} and
similarly for the password field with minLength: 8), which produces FieldErrors
with no message; change those minLength entries to objects with both value and
message (e.g., minLength: { value: 2, message: 'Full name must be at least 2
characters' } and minLength: { value: 8, message: 'Password must be at least 8
characters' }) so InputField can render error.message correctly.
lib/constants.ts-11-11 (1)

11-11: ⚠️ Potential issue | 🟡 Minor

Typo in HOME_CARDS[2].text: "maintainemotional" → "maintain emotional".

-    { image: '/asset/icons/dashboard.svg', title: 'Build Discipline', text: 'Follow your trading plan consistently. Use journaling to maintainemotional control and stick to your strategy' },
+    { image: '/asset/icons/dashboard.svg', title: 'Build Discipline', text: 'Follow your trading plan consistently. Use journaling to maintain emotional control and stick to your strategy' },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/constants.ts` at line 11, Fix the typo in the HOME_CARDS array: update
HOME_CARDS[2].text (the card with title "Build Discipline") to change
"maintainemotional" to "maintain emotional" so the string reads "...Use
journaling to maintain emotional control and stick to your strategy".
components/forms/CountrySelectField.tsx-132-144 (1)

132-144: ⚠️ Potential issue | 🟡 Minor

Label/control association is broken for accessibility.

<Label htmlFor={name}> references an id that doesn't exist on the rendered popover trigger button — Button has no id={name}. Screen readers won't associate the label with the combobox. Either pass an id down to the trigger, or wrap the control in the <Label> element.

♿ Suggested change
-                render={({ field }) => (
-                    <CountrySelect value={field.value} onChange={field.onChange} />
-                )}
+                render={({ field }) => (
+                    <CountrySelect id={name} value={field.value} onChange={field.onChange} />
+                )}

…and forward id to the <Button> inside CountrySelect.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/forms/CountrySelectField.tsx` around lines 132 - 144, The Label's
htmlFor points to an id that isn't present on the popover trigger; update the
Controller render call to pass id={name} into the CountrySelect component, then
modify CountrySelect to accept an id prop and forward it onto the popover
trigger Button (the Button inside CountrySelect) so the Label htmlFor={name}
correctly associates with the combobox trigger; ensure the prop name matches
(id) and is applied to the actual DOM Button element.
🧹 Nitpick comments (11)
components/forms/InputField.tsx (1)

4-4: Unused Image import.

next/image is imported but never used in this file.

♻️ Proposed fix
-import Image from "next/image"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/forms/InputField.tsx` at line 4, Remove the unused import of Image
from "next/image" in the InputField component; locate the import statement at
the top of components/forms/InputField.tsx (where Image is imported) and delete
that import line so the InputField component and file no longer include an
unused dependency.
app/layout.tsx (1)

14-14: Duplicate font-sans class.

"font-sans dark" already includes font-sans; passing it again is redundant (cn/twMerge will dedupe, but it adds noise).

♻️ Proposed fix
-    <html lang="en" className={cn("font-sans dark", "font-sans", geist.variable)}>
+    <html lang="en" className={cn("font-sans dark", geist.variable)}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/layout.tsx` at line 14, Remove the duplicate "font-sans" argument passed
to the className utility: update the html element's className call that uses
cn("font-sans dark", "font-sans", geist.variable) to only include unique classes
(e.g., cn("font-sans dark", geist.variable)), keeping the cn call and
geist.variable reference intact.
components/ui/select.tsx (2)

78-86: Dead expression in cn(...) for the viewport.

position === "popper" && "" always evaluates to either false or "", both of which are no-ops inside cn. Drop it (or fill in the intended classes if the popper case needs extra styles).

♻️ Suggested fix
-        <SelectPrimitive.Viewport
-          data-position={position}
-          className={cn(
-            "data-[position=popper]:h-(--radix-select-trigger-height) data-[position=popper]:w-full data-[position=popper]:min-w-(--radix-select-trigger-width)",
-            position === "popper" && ""
-          )}
-        >
+        <SelectPrimitive.Viewport
+          data-position={position}
+          className={cn(
+            "data-[position=popper]:h-(--radix-select-trigger-height) data-[position=popper]:w-full data-[position=popper]:min-w-(--radix-select-trigger-width)",
+          )}
+        >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/select.tsx` around lines 78 - 86, The cn call inside
SelectPrimitive.Viewport contains a dead expression `position === "popper" &&
""`; remove this no-op from the className array (or replace it with the actual
classes for the popper case if special styling is required) so the cn invocation
only receives meaningful class strings; update the SelectPrimitive.Viewport JSX
where className is built (using cn(...)) to drop the `position === "popper" &&
""` fragment.

72-72: Compress-on-one-line className hurts readability.

The combined cn(...) argument list squeezes position === "popper" && "..." onto the same line as the base class string with missing whitespace around operators (position ==="popper"&&"..."). Split it across lines like the other UI files for consistency and so prettier/eslint formatting doesn’t churn on every save.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/select.tsx` at line 72, The className call in
components/ui/select.tsx is compressed onto one line (the cn(...) argument list)
and includes `position ==="popper"&&"..."` with missing spaces; split the
cn(...) arguments across multiple lines matching other UI components, move the
`position === "popper" && "data-[side=bottom]:translate-y-1 ..."` expression
onto its own line as a separate argument, and ensure proper spacing around the
`===` and `&&` operators and a trailing comma/space so Prettier/ESLint no longer
churn on every save.
components/About.tsx (1)

6-6: Avoid nested <main> elements.

This About component is rendered inside app/(root)/page.tsx which most likely already has a <main> wrapper. Per HTML semantics there should be only one <main> per document; use <section> here.

♻️ Suggested fix
-    <main className="sm:mt-24 mt-12 mx-2 lg:mx-0">
+    <section className="sm:mt-24 mt-12 mx-2 lg:mx-0">
...
-    </main>
+    </section>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/About.tsx` at line 6, The About component currently renders a
<main> element which causes nested <main> semantics when About is embedded in
app/(root)/page.tsx; replace the <main> in the About component with a <section>
(keeping the existing className "sm:mt-24 mt-12 mx-2 lg:mx-0" and any
children/props) so the component uses a semantic section element instead of a
second main.
components/forms/CountrySelectField.tsx (3)

48-48: Memoize the country list — it's recomputed on every render.

countryList().getData() allocates the full ~250-entry array on every render of CountrySelect. Move it to module scope or wrap in useMemo.

♻️ Suggested change
-const countries: CountryOption[] = countryList().getData();
+const countries: CountryOption[] = useMemo(() => countryList().getData(), []);

(Add useMemo to the React import.) Module-scope is also fine since the data is static.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/forms/CountrySelectField.tsx` at line 48, The countries array is
being rebuilt every render because CountrySelect calls countryList().getData()
inline; move that heavy allocation to module scope (declare a top-level const
countries: CountryOption[] = countryList().getData()) or wrap it in useMemo
inside the CountrySelect component (import useMemo and compute countries =
useMemo(() => countryList().getData(), [])) so the array is computed once;
update references to the existing countries variable in the CountrySelect
component accordingly.

1-1: Avoid Control<any> — use the proper generic.

Control<any> defeats type-safety for the rest of the form. Make the component generic over the form values (or use FieldValues), which also lets you delete the eslint-disable line.

-/* eslint-disable `@typescript-eslint/no-explicit-any` */
...
-type CountrySelectProps = {
-    name: string;
-    label: string;
-    control: Control<any>;
-    error?: FieldError;
-    required?: boolean;
-};
+type CountrySelectProps<T extends FieldValues = FieldValues> = {
+    name: Path<T>;
+    label: string;
+    control: Control<T>;
+    error?: FieldError;
+    required?: boolean;
+};

Also applies to: 28-28

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/forms/CountrySelectField.tsx` at line 1, The component currently
uses Control<any>, which breaks type-safety; make CountrySelectField generic
over form values (e.g., <TFieldValues extends FieldValues>) and replace
Control<any> with Control<TFieldValues> in the component props and any related
types (also update any usages or prop forwarding to accept the generic), then
remove the top-line "eslint-disable `@typescript-eslint/no-explicit-any`" because
the explicit any will be gone; check the other occurrence referenced (line 28)
and change it to the same generic Control<TFieldValues> so the whole component
is strongly typed.

25-31: Duplicate CountrySelectProps and inline CountryOption definitions.

  • CountrySelectProps is also declared globally in types/global.d.ts (Lines 17-23). Either rely on the global one or remove the global to keep a single source of truth.
  • CountryOption is already exported by the ambient declaration in types/react-select-country-list.d.ts; the inline duplicate at Lines 43-46 is redundant.

Also applies to: 43-46

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/forms/CountrySelectField.tsx` around lines 25 - 31, Remove the
duplicate type declarations in CountrySelectField by deleting the inline
CountrySelectProps and CountryOption types and instead import or reference the
existing ambient/global types (CountrySelectProps and CountryOption) already
declared in the project; update the component to rely on the ambient
declarations (or add an explicit import if you prefer explicitness) so only one
source of truth remains for CountrySelectProps and CountryOption and ensure any
usages in the component (e.g., the component signature and option typing) use
those ambient names.
app/(auth)/sign-in/page.tsx (1)

7-10: Unused router / useRouter import.

router is initialized but never referenced. Either wire it up to navigate after successful sign-in or remove the import to avoid lint noise.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(auth)/sign-in/page.tsx around lines 7 - 10, The SignIn component
currently imports and initializes useRouter into the router variable but never
uses it, causing unused-import/lint noise; either remove the useRouter import
and the const router = useRouter() line from the SignIn component if navigation
isn’t needed, or wire router into the sign-in flow (e.g., call router.push(...)
after successful sign-in inside the SignIn handler) so the router variable is
referenced; locate the SignIn component and update the import/const usage
accordingly.
components/forms/Testimonial.tsx (1)

30-32: Minor: prefer Array.from({ length: n }) over spreading a holey Array(n).

[...Array(testimonial.rating)] materializes a holey array before spreading; Array.from({ length: testimonial.rating }, (_, i) => ...) is more idiomatic and avoids the intermediate iterable. Also consider guarding against non-integer / negative rating if the data ever comes from an external source.

♻️ Suggested change
-              {[...Array(testimonial.rating)].map((_, i) => (
-                <Star key={i} className="w-4 h-4 fill-yellow-500 text-yellow-500" />
-              ))}
+              {Array.from({ length: testimonial.rating }, (_, i) => (
+                <Star key={i} className="w-4 h-4 fill-yellow-500 text-yellow-500" />
+              ))}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/forms/Testimonial.tsx` around lines 30 - 32, Replace the
holey-array spread used to render stars with Array.from and validate the rating:
instead of [...Array(testimonial.rating)].map(...), use Array.from({ length:
Math.max(0, Math.floor(Number(testimonial.rating) || 0)) }, (_, i) => ...) so
the Star rendering in Testimonial.tsx (where testimonial.rating and the Star
component are used) handles non-integer, negative, or non-numeric input robustly
and avoids creating a holey array.
app/(root)/page.tsx (1)

8-8: Nit: stray trailing whitespace in className string.

-    <div className='text-white sm:mx-16 sm:text-start text-center  '>
+    <div className='text-white sm:mx-16 sm:text-start text-center'>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(root)/page.tsx at line 8, Remove the stray trailing whitespace inside
the className string on the div in page.tsx (the element with
className='text-white sm:mx-16 sm:text-start text-center  '); edit the JSX to
trim the ending space so the className becomes 'text-white sm:mx-16
sm:text-start text-center' to avoid unintended class parsing and lint warnings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/`(auth)/layout.tsx:
- Line 11: The Image tag in app/(auth)/layout.tsx uses a relative path
("asset/images/logo.svg") which breaks on nested routes; update the Image src to
a root-relative path (e.g. "/asset/images/logo.svg") in the JSX where Image is
rendered so next/image will load the static public asset correctly while keeping
the existing props (alt, width, height, className).

In `@app/`(auth)/sign-in/page.tsx:
- Line 36: The InputField validation is passing bare pattern and minLength
values which produce FieldError objects without messages, so the error UI shows
empty text; update the validation props for the email and password InputField
usages to use the object form (e.g., pattern: { value: /.../, message: '...' }
and minLength: { value: 8, message: '...' }) and replace the overly-restrictive
email regex (/^\w+@\w+\.\w+$/) with the same regex used on the sign-up page to
allow +, -, . in the local part and broader TLDs; ensure the error messages are
clear (e.g., "Enter a valid email" and "Password must be at least 8 characters")
and modify the InputField validation props accordingly.
- Line 29: The form currently passes a lowercase handler name "onsubmit" which
is undefined and falls back to window.onsubmit; create a proper onSubmit handler
(e.g., async function onSubmit(data) { ... }) in this component and wire it into
the form as handleSubmit(onSubmit) — mirror the implementation pattern used in
app/(auth)/sign-up/page.tsx (define onSubmit, perform validation/submit logic,
call your auth/signIn function and handle errors) and ensure the function name
is exactly "onSubmit" (camelCase) when referenced in the JSX.

In `@app/`(auth)/sign-up/page.tsx:
- Around line 16-25: The form type SignUpFormData currently declares
investmentGoals, riskTolerance, and preferredIndustry but the form (useForm
defaultValues in your sign-up page) neither renders inputs for them nor supplies
defaults, causing undefined values on submit; fix this by either (A) narrowing
the form type to only the fields actually collected (e.g., create a
SignUpBasicData type and pass that to useForm) and update any handlers that
expect SignUpFormData, or (B) add the missing inputs to the sign-up page and
include matching keys in useForm defaultValues (investmentGoals, riskTolerance,
preferredIndustry) and ensure the submit handler (the component using useForm)
and any downstream consumers accept the full SignUpFormData shape.
- Around line 26-31: The onSubmit handler currently logs the entire
SignUpFormData (console.log(data)) which exposes the plaintext password; update
the onSubmit function to stop logging full payloads—either remove the
console.log entirely or sanitize the object before logging by omitting or
redacting the password field (e.g., create a copy without data.password) and
then log non-sensitive fields only, and replace the stub with the actual
authentication call (invoke your auth/signup method and handle success/error) in
the onSubmit function to ensure no sensitive values are written to logs.

In `@app/globals.css`:
- Around line 165-168: The Tailwind v4 change requires the important modifier at
the end of the utility, so update the .form-input class string to move the "!"
after the utility; replace the current focus:!border-yellow-500 with
focus:border-yellow-500! inside the `@apply` list (in the .form-input rule) so the
focus border utility is applied with importance under the focus variant.

In `@components/forms/InputField.tsx`:
- Around line 14-23: The Input component is being rendered with a direct value
prop while also spreading register(name, validation), causing a
controlled/uncontrolled conflict with react-hook-form; remove the value prop and
pass the initial value via defaultValue (or switch to using Controller if you
need a controlled input) so update the JSX where Input is used (the Input
element that spreads {...register(name, validation)}) to replace value={value}
with defaultValue={value} and ensure no other controlled handlers conflict with
register.

In `@components/NavItems.tsx`:
- Line 4: Remove the private Next.js internal import taintObjectReference from
NavItems.tsx (it's unused and pulls a server-only path into a client module);
delete the import line and any references to taintObjectReference in this file,
and if you actually need React's taint API move that logic into a Server
Component and import experimental_taintObjectReference from 'react' instead of
from 'next/dist/server/...'.
- Around line 20-31: The click handler is typed for an anchor but currently
attached to the <li>, and keyboard activation on the Link bypasses it; move the
onClick from the <li> to the Link rendered for each NAV_ITEMS entry, keep
handleScroll signature as (e: React.MouseEvent<HTMLAnchorElement>, href: string)
=> void, call e.preventDefault() there, and use const target =
document.querySelector(href) followed by target?.scrollIntoView({ behavior:
'smooth' }); update any JSX to pass (e) => handleScroll(e, href) into the Link
(not the li) so both mouse and keyboard activations use smooth-scroll.

In `@components/ui/command.tsx`:
- Around line 49-65: Move the DialogHeader (which contains DialogTitle and
DialogDescription) to be rendered as children inside DialogContent instead of as
a sibling of DialogContent so Radix can wire aria-labelledby/aria-describedby
and portalize the header correctly; specifically, update the JSX so
DialogContent contains a sr-only DialogHeader with DialogTitle and
DialogDescription, keeping existing props (className, showCloseButton) and
preserving children rendering and styling in the Command component so
accessibility and Radix warnings are resolved.

In `@components/ui/popover.tsx`:
- Around line 58-66: PopoverTitle is typed as React.ComponentProps<"h2"> but
renders a div, breaking the prop contract and heading semantics; update
PopoverTitle to render an actual <h2> element (keeping
data-slot="popover-title", className={cn("font-medium", className)} and
spreading {...props}) so the runtime element matches the type and restores
accessibility, or alternatively change the props type to
React.ComponentProps<"div"> if you intentionally want a div—pick one and make
both the JSX tag and the generic type consistent in the PopoverTitle function
signature and body.

In `@types/global.d.ts`:
- Around line 58-62: SearchCommandProps is declared twice with conflicting
shapes; remove the duplicate and consolidate into a single accurate type (or
rename one of them) so TypeScript no longer reports duplicate identifier errors.
Locate both declarations named SearchCommandProps, decide whether the intended
contract includes props like open, setOpen, renderAs,
buttonLabel/buttonVariant/className, label, and initialStocks, then merge them
into one coherent type (or rename the second to e.g. SearchCommandControlProps)
and update all references/usages to the new unified name; ensure exported types
and any components/functions that consume SearchCommandProps are updated to the
consolidated property set.
- Around line 1-50: Add the missing react-hook-form type imports at the top of
this global declaration: import Control, FieldError, UseFormRegister,
RegisterOptions and FieldValues from "react-hook-form", then update the usages
in CountrySelectProps, FormInputProps and SelectFieldProps to use the generic
forms Control<FieldValues> and UseFormRegister<FieldValues> (keep FieldError and
RegisterOptions as imported types) so the types SignInFormData, SignUpFormData,
CountrySelectProps, FormInputProps and SelectFieldProps compile without "Cannot
find name" errors.

---

Outside diff comments:
In `@components/NavItems.tsx`:
- Around line 14-18: The isActive function currently uses pathname from
usePathname() and thus never matches fragment/hash-based hrefs (e.g., '#about');
update isActive (or the nav active logic that uses isActive) to consider
window.location.hash or an IntersectionObserver-driven currentSection state:
check if pathname === '/' and href === '#home' still matches, otherwise treat
the link as active when either pathname.startsWith(path) OR window.location.hash
=== path (or when currentSection === path if you implement IntersectionObserver
to set currentSection); reference the isActive function, pathname variable, and
the nav item hrefs to locate where to add this hash/observer-based check.

---

Minor comments:
In `@app/`(auth)/sign-up/page.tsx:
- Line 45: The minLength validators passed to the InputField validation prop are
plain numbers (e.g., in sign-up page.tsx where validation={{ required: 'Full
name is required', minLength: 2 }} and similarly for the password field with
minLength: 8), which produces FieldErrors with no message; change those
minLength entries to objects with both value and message (e.g., minLength: {
value: 2, message: 'Full name must be at least 2 characters' } and minLength: {
value: 8, message: 'Password must be at least 8 characters' }) so InputField can
render error.message correctly.

In `@app/globals.css`:
- Around line 147-150: Fix the CSS formatting in the html rule: remove the
trailing space after the font-sans declaration and insert a blank line (empty
line) before the scroll-behavior declaration so the block satisfies Stylelint's
declaration-empty-line-before rule; update the html { ... } block accordingly
(the two identifiers to locate are the html selector and the declarations
font-sans and scroll-behavior).

In `@components/About.tsx`:
- Line 17: Update the className on the list item in the About component: fix the
typo "cursor-poiter" to "cursor-pointer" in the li element's className (the
element with key={title}) so Tailwind applies the cursor affordance correctly.

In `@components/ContactUs.tsx`:
- Line 10: The paragraph string in the ContactUs component contains typos:
change "trades" to "traders", "controlof" to "control of", and "tradiing" to
"trading" in the JSX paragraph (the element with className "text-center mt-3
text-gray-500 sm:w-150") so the copy reads correctly about traders improving
results with JournalFX and creating a free account to take control of their
trading journey.
- Around line 14-15: In the ContactUs component update the Link for the "Create
Free Account" CTA: change href="/sign-in" to href="/sign-up" on the Link element
(the JSX element in ContactUs.tsx that contains the text "Create Free Account")
so the button routes to the account creation page.

In `@components/forms/CountrySelectField.tsx`:
- Around line 132-144: The Label's htmlFor points to an id that isn't present on
the popover trigger; update the Controller render call to pass id={name} into
the CountrySelect component, then modify CountrySelect to accept an id prop and
forward it onto the popover trigger Button (the Button inside CountrySelect) so
the Label htmlFor={name} correctly associates with the combobox trigger; ensure
the prop name matches (id) and is applied to the actual DOM Button element.

In `@components/Home.tsx`:
- Around line 25-27: The Image in components/Home.tsx currently hardcodes
width={900} height={150}, which distorts the 1536×1024 asset; update the <Image
... /> usage to maintain the intrinsic aspect ratio by using either width={900}
height={600} (or width={225} height={150}) to match the 3:2 ratio, or switch to
the Next/Image fill mode by wrapping the Image in a positioned container and
using the fill prop so the image scales responsively without distortion. Locate
the Image element in Home.tsx and apply one of these fixes to preserve aspect
ratio.
- Around line 16-20: Update the CTA link targets: change the "Get Started" Link
in components/Home.tsx (the Link that wraps the ArrowRight component) from
href="/sign-in" to href="/sign-up", and likewise update the "Create Free
Account" CTA in components/ContactUs.tsx from "/sign-in" to "/sign-up"; also fix
the "Learn More" Link in components/Home.tsx (the Link with border-2/...
classes) to use the in-page hash route href="/#about" instead of "/about" so it
matches the hash-based navigation defined in lib/constants.ts.

In `@lib/constants.ts`:
- Around line 16-37: The TESTIMONIALS array has non-sequential id values (1, 3,
2); update the id fields inside the TESTIMONIALS constant so they are ordered
sequentially (1, 2, 3) to avoid confusion and keep keys predictable when used in
React (modify the id values in the objects inside the exported TESTIMONIALS
array).
- Line 11: Fix the typo in the HOME_CARDS array: update HOME_CARDS[2].text (the
card with title "Build Discipline") to change "maintainemotional" to "maintain
emotional" so the string reads "...Use journaling to maintain emotional control
and stick to your strategy".

---

Nitpick comments:
In `@app/`(auth)/sign-in/page.tsx:
- Around line 7-10: The SignIn component currently imports and initializes
useRouter into the router variable but never uses it, causing unused-import/lint
noise; either remove the useRouter import and the const router = useRouter()
line from the SignIn component if navigation isn’t needed, or wire router into
the sign-in flow (e.g., call router.push(...) after successful sign-in inside
the SignIn handler) so the router variable is referenced; locate the SignIn
component and update the import/const usage accordingly.

In `@app/`(root)/page.tsx:
- Line 8: Remove the stray trailing whitespace inside the className string on
the div in page.tsx (the element with className='text-white sm:mx-16
sm:text-start text-center  '); edit the JSX to trim the ending space so the
className becomes 'text-white sm:mx-16 sm:text-start text-center' to avoid
unintended class parsing and lint warnings.

In `@app/layout.tsx`:
- Line 14: Remove the duplicate "font-sans" argument passed to the className
utility: update the html element's className call that uses cn("font-sans dark",
"font-sans", geist.variable) to only include unique classes (e.g., cn("font-sans
dark", geist.variable)), keeping the cn call and geist.variable reference
intact.

In `@components/About.tsx`:
- Line 6: The About component currently renders a <main> element which causes
nested <main> semantics when About is embedded in app/(root)/page.tsx; replace
the <main> in the About component with a <section> (keeping the existing
className "sm:mt-24 mt-12 mx-2 lg:mx-0" and any children/props) so the component
uses a semantic section element instead of a second main.

In `@components/forms/CountrySelectField.tsx`:
- Line 48: The countries array is being rebuilt every render because
CountrySelect calls countryList().getData() inline; move that heavy allocation
to module scope (declare a top-level const countries: CountryOption[] =
countryList().getData()) or wrap it in useMemo inside the CountrySelect
component (import useMemo and compute countries = useMemo(() =>
countryList().getData(), [])) so the array is computed once; update references
to the existing countries variable in the CountrySelect component accordingly.
- Line 1: The component currently uses Control<any>, which breaks type-safety;
make CountrySelectField generic over form values (e.g., <TFieldValues extends
FieldValues>) and replace Control<any> with Control<TFieldValues> in the
component props and any related types (also update any usages or prop forwarding
to accept the generic), then remove the top-line "eslint-disable
`@typescript-eslint/no-explicit-any`" because the explicit any will be gone; check
the other occurrence referenced (line 28) and change it to the same generic
Control<TFieldValues> so the whole component is strongly typed.
- Around line 25-31: Remove the duplicate type declarations in
CountrySelectField by deleting the inline CountrySelectProps and CountryOption
types and instead import or reference the existing ambient/global types
(CountrySelectProps and CountryOption) already declared in the project; update
the component to rely on the ambient declarations (or add an explicit import if
you prefer explicitness) so only one source of truth remains for
CountrySelectProps and CountryOption and ensure any usages in the component
(e.g., the component signature and option typing) use those ambient names.

In `@components/forms/InputField.tsx`:
- Line 4: Remove the unused import of Image from "next/image" in the InputField
component; locate the import statement at the top of
components/forms/InputField.tsx (where Image is imported) and delete that import
line so the InputField component and file no longer include an unused
dependency.

In `@components/forms/Testimonial.tsx`:
- Around line 30-32: Replace the holey-array spread used to render stars with
Array.from and validate the rating: instead of
[...Array(testimonial.rating)].map(...), use Array.from({ length: Math.max(0,
Math.floor(Number(testimonial.rating) || 0)) }, (_, i) => ...) so the Star
rendering in Testimonial.tsx (where testimonial.rating and the Star component
are used) handles non-integer, negative, or non-numeric input robustly and
avoids creating a holey array.

In `@components/ui/select.tsx`:
- Around line 78-86: The cn call inside SelectPrimitive.Viewport contains a dead
expression `position === "popper" && ""`; remove this no-op from the className
array (or replace it with the actual classes for the popper case if special
styling is required) so the cn invocation only receives meaningful class
strings; update the SelectPrimitive.Viewport JSX where className is built (using
cn(...)) to drop the `position === "popper" && ""` fragment.
- Line 72: The className call in components/ui/select.tsx is compressed onto one
line (the cn(...) argument list) and includes `position ==="popper"&&"..."` with
missing spaces; split the cn(...) arguments across multiple lines matching other
UI components, move the `position === "popper" &&
"data-[side=bottom]:translate-y-1 ..."` expression onto its own line as a
separate argument, and ensure proper spacing around the `===` and `&&` operators
and a trailing comma/space so Prettier/ESLint no longer churn on every save.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c6da01c8-e8fd-43da-bdf5-b1e028184b64

📥 Commits

Reviewing files that changed from the base of the PR and between 38ede80 and 7e2f797.

⛔ Files ignored due to path filters (16)
  • package-lock.json is excluded by !**/package-lock.json
  • public/asset/icons/Logo-main.png is excluded by !**/*.png
  • public/asset/icons/analysze.svg is excluded by !**/*.svg
  • public/asset/icons/analytics.svg is excluded by !**/*.svg
  • public/asset/icons/arrow-down.svg is excluded by !**/*.svg
  • public/asset/icons/audience.svg is excluded by !**/*.svg
  • public/asset/icons/calendar.svg is excluded by !**/*.svg
  • public/asset/icons/clock.svg is excluded by !**/*.svg
  • public/asset/icons/dashboard.svg is excluded by !**/*.svg
  • public/asset/icons/logo.png is excluded by !**/*.png
  • public/asset/icons/mode.svg is excluded by !**/*.svg
  • public/asset/icons/pin.svg is excluded by !**/*.svg
  • public/asset/icons/star.svg is excluded by !**/*.svg
  • public/asset/icons/target.svg is excluded by !**/*.svg
  • public/asset/images/home-bg.png is excluded by !**/*.png
  • public/asset/images/home-holder.png is excluded by !**/*.png
📒 Files selected for processing (26)
  • app/(auth)/layout.tsx
  • app/(auth)/sign-in/page.tsx
  • app/(auth)/sign-up/page.tsx
  • app/(root)/page.tsx
  • app/globals.css
  • app/layout.tsx
  • components/About.tsx
  • components/ContactUs.tsx
  • components/Home.tsx
  • components/NavItems.tsx
  • components/forms/CountrySelectField.tsx
  • components/forms/FooterLink.tsx
  • components/forms/InputField.tsx
  • components/forms/Testimonial.tsx
  • components/ui/command.tsx
  • components/ui/dialog.tsx
  • components/ui/input-group.tsx
  • components/ui/input.tsx
  • components/ui/label.tsx
  • components/ui/popover.tsx
  • components/ui/select.tsx
  • components/ui/textarea.tsx
  • lib/constants.ts
  • package.json
  • types/global.d.ts
  • types/react-select-country-list.d.ts

Comment thread app/(auth)/layout.tsx
<section className="scrollbar-hide-default">
<Link href="/" className="text-2xl font-bold">
<Image src="/logo.svg" alt="Logo" width={140} height={32} w-auto h-8/>
<Image src="asset/images/logo.svg" alt="Logo" width={140} height={32} className="w-auto h-8"/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the actual location of logo.svg in the public folder
fd -t f 'logo.svg'
fd -t d 'asset|assets' public 2>/dev/null || fd -t d 'asset|assets'

Repository: Dali11/JournalFX

Length of output: 103


Logo src is missing the leading /next/image requires a root-relative path for static public assets.

"asset/images/logo.svg" is interpreted as relative to the current route, causing it to resolve to /sign-in/asset/images/logo.svg on the /sign-in page and fail with a 404. The next/image component enforces absolute paths for static public assets.

🐛 Suggested fix
-                <Image src="asset/images/logo.svg" alt="Logo" width={140} height={32} className="w-auto h-8"/>
+                <Image src="/asset/images/logo.svg" alt="Logo" width={140} height={32} className="w-auto h-8"/>
📝 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
<Image src="asset/images/logo.svg" alt="Logo" width={140} height={32} className="w-auto h-8"/>
<Image src="/asset/images/logo.svg" alt="Logo" width={140} height={32} className="w-auto h-8"/>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(auth)/layout.tsx at line 11, The Image tag in app/(auth)/layout.tsx
uses a relative path ("asset/images/logo.svg") which breaks on nested routes;
update the Image src to a root-relative path (e.g. "/asset/images/logo.svg") in
the JSX where Image is rendered so next/image will load the static public asset
correctly while keeping the existing props (alt, width, height, className).

<>
<h1 className="mt-12 lg:mt-24 mb-5 lg:mb-10 font-bold text-2xl">Welcome back</h1>

<form onSubmit={handleSubmit(onsubmit)} className="space-y-5">

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: onsubmit is undefined — references the global window.onsubmit instead of a real handler.

There is no onSubmit function defined in this file (note the lowercase onsubmit). When the form validates successfully, handleSubmit will invoke window.onsubmit (or undefined), so submitted form data is silently dropped. Define an onSubmit handler (mirroring app/(auth)/sign-up/page.tsx) and pass it here.

🐛 Suggested fix
+    const onSubmit = async (data: SignInFormData) => {
+        try {
+            console.log(data)
+            // TODO: integrate sign-in (e.g., next-auth)
+        } catch (error) {
+            console.error('Error during sign in:', error)
+        }
+    };
+
     return (
         <>
             <h1 className="mt-12 lg:mt-24 mb-5 lg:mb-10 font-bold text-2xl">Welcome back</h1>

-            <form onSubmit={handleSubmit(onsubmit)} className="space-y-5">
+            <form onSubmit={handleSubmit(onSubmit)} className="space-y-5">
📝 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
<form onSubmit={handleSubmit(onsubmit)} className="space-y-5">
const onSubmit = async (data: SignInFormData) => {
try {
console.log(data)
// TODO: integrate sign-in (e.g., next-auth)
} catch (error) {
console.error('Error during sign in:', error)
}
};
return (
<>
<h1 className="mt-12 lg:mt-24 mb-5 lg:mb-10 font-bold text-2xl">Welcome back</h1>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-5">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(auth)/sign-in/page.tsx at line 29, The form currently passes a
lowercase handler name "onsubmit" which is undefined and falls back to
window.onsubmit; create a proper onSubmit handler (e.g., async function
onSubmit(data) { ... }) in this component and wire it into the form as
handleSubmit(onSubmit) — mirror the implementation pattern used in
app/(auth)/sign-up/page.tsx (define onSubmit, perform validation/submit logic,
call your auth/signIn function and handle errors) and ensure the function name
is exactly "onSubmit" (camelCase) when referenced in the JSX.

placeholder="contact@jsmastery.com"
register={register}
error={errors.email}
validation={{ required: 'Email is required', pattern: /^\w+@\w+\.\w+$/ }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validation rules are missing message — error UI will render empty text.

InputField renders error.message, but pattern: /regex/ and minLength: 8 set as bare values produce a FieldError with no message. Use the object form so users see meaningful feedback. Also the email regex /^\w+@\w+\.\w+$/ is overly restrictive (rejects +, -, . in local part and many TLDs); consider aligning with the sign-up page's pattern.

🛠️ Suggested fix
-                    validation={{ required: 'Email is required', pattern: /^\w+@\w+\.\w+$/ }}
+                    validation={{
+                        required: 'Email is required',
+                        pattern: { value: /^\S+@\S+\.\S+$/i, message: 'Invalid email address' },
+                    }}
...
-                    validation={{ required: 'Password is required', minLength: 8 }}
+                    validation={{
+                        required: 'Password is required',
+                        minLength: { value: 8, message: 'Password must be at least 8 characters' },
+                    }}

Also applies to: 46-46

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(auth)/sign-in/page.tsx at line 36, The InputField validation is passing
bare pattern and minLength values which produce FieldError objects without
messages, so the error UI shows empty text; update the validation props for the
email and password InputField usages to use the object form (e.g., pattern: {
value: /.../, message: '...' } and minLength: { value: 8, message: '...' }) and
replace the overly-restrictive email regex (/^\w+@\w+\.\w+$/) with the same
regex used on the sign-up page to allow +, -, . in the local part and broader
TLDs; ensure the error messages are clear (e.g., "Enter a valid email" and
"Password must be at least 8 characters") and modify the InputField validation
props accordingly.

Comment on lines +16 to +25
} = useForm<SignUpFormData>({
defaultValues: {
fullName: '',
email: '',
password: '',
country: 'MW',
},
mode: 'onBlur'

})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

SignUpFormData declares fields the form never collects.

SignUpFormData includes investmentGoals, riskTolerance, and preferredIndustry, but no inputs are rendered for them and they are missing from defaultValues. Submitted data will have these as undefined, which will likely break any downstream consumer that expects a full SignUpFormData. Either narrow the form's type to the fields actually collected, or add the missing inputs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(auth)/sign-up/page.tsx around lines 16 - 25, The form type
SignUpFormData currently declares investmentGoals, riskTolerance, and
preferredIndustry but the form (useForm defaultValues in your sign-up page)
neither renders inputs for them nor supplies defaults, causing undefined values
on submit; fix this by either (A) narrowing the form type to only the fields
actually collected (e.g., create a SignUpBasicData type and pass that to
useForm) and update any handlers that expect SignUpFormData, or (B) add the
missing inputs to the sign-up page and include matching keys in useForm
defaultValues (investmentGoals, riskTolerance, preferredIndustry) and ensure the
submit handler (the component using useForm) and any downstream consumers accept
the full SignUpFormData shape.

Comment on lines +26 to +31
const onSubmit = async (data: SignUpFormData) => {
try {
console.log(data)
} catch (error) {
console.error('Error during sign up:', 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.

⚠️ Potential issue | 🟠 Major

Avoid logging the full submitted payload (it contains the user's password).

console.log(data) includes the plaintext password from the form. Even in development this can leak into shared logs/screenshots. At minimum, omit password before logging, and replace this stub with the real auth integration before merging.

🛡️ Suggested fix
-  const onSubmit = async (data: SignUpFormData) => {
-    try { 
-      console.log(data)
-  } catch (error) {
-    console.error('Error during sign up:', error)
-  }}
+  const onSubmit = async (data: SignUpFormData) => {
+    try {
+      const { password: _pw, ...safe } = data;
+      console.log('sign-up submission', safe);
+      // TODO: integrate sign-up call
+    } catch (error) {
+      console.error('Error during sign up:', error);
+    }
+  };
📝 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 onSubmit = async (data: SignUpFormData) => {
try {
console.log(data)
} catch (error) {
console.error('Error during sign up:', error)
}}
const onSubmit = async (data: SignUpFormData) => {
try {
const { password: _pw, ...safe } = data;
console.log('sign-up submission', safe);
// TODO: integrate sign-up call
} catch (error) {
console.error('Error during sign up:', error);
}
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(auth)/sign-up/page.tsx around lines 26 - 31, The onSubmit handler
currently logs the entire SignUpFormData (console.log(data)) which exposes the
plaintext password; update the onSubmit function to stop logging full
payloads—either remove the console.log entirely or sanitize the object before
logging by omitting or redacting the password field (e.g., create a copy without
data.password) and then log non-sensitive fields only, and replace the stub with
the actual authentication call (invoke your auth/signup method and handle
success/error) in the onSubmit function to ensure no sensitive values are
written to logs.

Comment thread components/NavItems.tsx
Comment on lines +20 to +31
const handleScroll = (e: React.MouseEvent<HTMLAnchorElement>, href: string) => {
e.preventDefault()
const target = document.querySelector(href)
if(target) {
target.scrollIntoView({behavior: 'smooth'})
}
}
return (

<ul className='flex flex-col sm:flex-row p-2 sm:gap-10 font-medium gap-2'>
{NAV_ITEMS.map(({href, label}) => (
<li key={href}>
<li key={href} onClick={(e) => handleScroll(e, href)}>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Wrong event type, and the click handler should live on the link.

  • The handler is attached to <li> (line 31) but typed as React.MouseEvent<HTMLAnchorElement> — the correct generic is HTMLLIElement.
  • Putting onClick on the <li> means keyboard activation of the Link (Enter) bypasses preventDefault/scrollIntoView and falls back to a hard hash navigation. Move the handler to the Link itself so mouse and keyboard activation both smooth-scroll.
  • Consider target?.scrollIntoView(...) for safety (you already null-check, so optional).
♻️ Suggested fix
-    const handleScroll = (e: React.MouseEvent<HTMLAnchorElement>, href: string) => {
+    const handleScroll = (e: React.MouseEvent<HTMLAnchorElement>, href: string) => {
         e.preventDefault()
         const target = document.querySelector(href)
         if(target) {
             target.scrollIntoView({behavior: 'smooth'})
         }
     }
   return (
         <ul className='flex flex-col sm:flex-row p-2 sm:gap-10 font-medium gap-2'>
         {NAV_ITEMS.map(({href, label}) => (
-            <li key={href} onClick={(e) => handleScroll(e, href)}>
-                <Link href={href} className={`hover:text-yellow-500 transition-colors ${
+            <li key={href}>
+                <Link href={href} onClick={(e) => handleScroll(e, href)} className={`hover:text-yellow-500 transition-colors ${
                     isActive(href) ? 'text-yellow-500' : 'text-gray-100'
                 }`}>
                     {label}
                 </Link>
             </li>
         ))}
     </ul>
📝 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 handleScroll = (e: React.MouseEvent<HTMLAnchorElement>, href: string) => {
e.preventDefault()
const target = document.querySelector(href)
if(target) {
target.scrollIntoView({behavior: 'smooth'})
}
}
return (
<ul className='flex flex-col sm:flex-row p-2 sm:gap-10 font-medium gap-2'>
{NAV_ITEMS.map(({href, label}) => (
<li key={href}>
<li key={href} onClick={(e) => handleScroll(e, href)}>
const handleScroll = (e: React.MouseEvent<HTMLAnchorElement>, href: string) => {
e.preventDefault()
const target = document.querySelector(href)
if(target) {
target.scrollIntoView({behavior: 'smooth'})
}
}
return (
<ul className='flex flex-col sm:flex-row p-2 sm:gap-10 font-medium gap-2'>
{NAV_ITEMS.map(({href, label}) => (
<li key={href}>
<Link href={href} onClick={(e) => handleScroll(e, href)} className={`hover:text-yellow-500 transition-colors ${
isActive(href) ? 'text-yellow-500' : 'text-gray-100'
}`}>
{label}
</Link>
</li>
))}
</ul>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/NavItems.tsx` around lines 20 - 31, The click handler is typed for
an anchor but currently attached to the <li>, and keyboard activation on the
Link bypasses it; move the onClick from the <li> to the Link rendered for each
NAV_ITEMS entry, keep handleScroll signature as (e:
React.MouseEvent<HTMLAnchorElement>, href: string) => void, call
e.preventDefault() there, and use const target = document.querySelector(href)
followed by target?.scrollIntoView({ behavior: 'smooth' }); update any JSX to
pass (e) => handleScroll(e, href) into the Link (not the li) so both mouse and
keyboard activations use smooth-scroll.

Comment thread components/ui/command.tsx
Comment on lines +49 to +65
return (
<Dialog {...props}>
<DialogHeader className="sr-only">
<DialogTitle>{title}</DialogTitle>
<DialogDescription>{description}</DialogDescription>
</DialogHeader>
<DialogContent
className={cn(
"top-1/3 translate-y-0 overflow-hidden rounded-xl! p-0",
className
)}
showCloseButton={showCloseButton}
>
{children}
</DialogContent>
</Dialog>
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

DialogHeader must be rendered inside DialogContent (a11y + Radix requirement).

DialogTitle and DialogDescription are placed as siblings of DialogContent rather than children. This breaks the Radix Dialog accessibility contract:

  • Radix wires aria-labelledby/aria-describedby on DialogContent only when DialogTitle/DialogDescription are descendants of it. As written, the dialog has no accessible name/description for screen readers.
  • DialogContent is the only piece rendered through DialogPortal based on open state. The header here renders unconditionally in the consumer's DOM, not inside the portalized dialog.
  • Radix will emit the "DialogContent requires a DialogTitle for accessibility" warning at runtime.

This also diverges from the shadcn upstream command recipe where the sr-only header sits inside DialogContent.

🔧 Proposed fix
   return (
     <Dialog {...props}>
-      <DialogHeader className="sr-only">
-        <DialogTitle>{title}</DialogTitle>
-        <DialogDescription>{description}</DialogDescription>
-      </DialogHeader>
       <DialogContent
         className={cn(
           "top-1/3 translate-y-0 overflow-hidden rounded-xl! p-0",
           className
         )}
         showCloseButton={showCloseButton}
       >
+        <DialogHeader className="sr-only">
+          <DialogTitle>{title}</DialogTitle>
+          <DialogDescription>{description}</DialogDescription>
+        </DialogHeader>
         {children}
       </DialogContent>
     </Dialog>
   )
📝 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
return (
<Dialog {...props}>
<DialogHeader className="sr-only">
<DialogTitle>{title}</DialogTitle>
<DialogDescription>{description}</DialogDescription>
</DialogHeader>
<DialogContent
className={cn(
"top-1/3 translate-y-0 overflow-hidden rounded-xl! p-0",
className
)}
showCloseButton={showCloseButton}
>
{children}
</DialogContent>
</Dialog>
)
return (
<Dialog {...props}>
<DialogContent
className={cn(
"top-1/3 translate-y-0 overflow-hidden rounded-xl! p-0",
className
)}
showCloseButton={showCloseButton}
>
<DialogHeader className="sr-only">
<DialogTitle>{title}</DialogTitle>
<DialogDescription>{description}</DialogDescription>
</DialogHeader>
{children}
</DialogContent>
</Dialog>
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/command.tsx` around lines 49 - 65, Move the DialogHeader (which
contains DialogTitle and DialogDescription) to be rendered as children inside
DialogContent instead of as a sibling of DialogContent so Radix can wire
aria-labelledby/aria-describedby and portalize the header correctly;
specifically, update the JSX so DialogContent contains a sr-only DialogHeader
with DialogTitle and DialogDescription, keeping existing props (className,
showCloseButton) and preserving children rendering and styling in the Command
component so accessibility and Radix warnings are resolved.

Comment thread components/ui/popover.tsx
Comment on lines +58 to +66
function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) {
return (
<div
data-slot="popover-title"
className={cn("font-medium", className)}
{...props}
/>
)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

PopoverTitle types props as h2 but renders a div.

The typing claims <h2> props (e.g., consumer could pass heading-only attributes), yet a <div> is rendered. This is both a type contract bug and an a11y regression — popovers should expose a heading. Either render the matching tag or fix the type.

♻️ Suggested fix
-function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) {
-  return (
-    <div
-      data-slot="popover-title"
-      className={cn("font-medium", className)}
-      {...props}
-    />
-  )
-}
+function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) {
+  return (
+    <h2
+      data-slot="popover-title"
+      className={cn("font-medium", className)}
+      {...props}
+    />
+  )
+}
📝 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
function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) {
return (
<div
data-slot="popover-title"
className={cn("font-medium", className)}
{...props}
/>
)
}
function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) {
return (
<h2
data-slot="popover-title"
className={cn("font-medium", className)}
{...props}
/>
)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/popover.tsx` around lines 58 - 66, PopoverTitle is typed as
React.ComponentProps<"h2"> but renders a div, breaking the prop contract and
heading semantics; update PopoverTitle to render an actual <h2> element (keeping
data-slot="popover-title", className={cn("font-medium", className)} and
spreading {...props}) so the runtime element matches the type and restores
accessibility, or alternatively change the props type to
React.ComponentProps<"div"> if you intentionally want a div—pick one and make
both the JSX tag and the generic type consistent in the PopoverTitle function
signature and body.

Comment thread types/global.d.ts
Comment on lines +1 to +50
declare global {
type SignInFormData = {
email: string;
password: string;
};

type SignUpFormData = {
fullName: string;
email: string;
password: string;
country: string;
investmentGoals: string;
riskTolerance: string;
preferredIndustry: string;
};

type CountrySelectProps = {
name: string;
label: string;
control: Control;
error?: FieldError;
required?: boolean;
};

type FormInputProps = {
name: string;
label: string;
placeholder: string;
type?: string;
register: UseFormRegister;
error?: FieldError;
validation?: RegisterOptions;
disabled?: boolean;
value?: string;
};

type Option = {
value: string;
label: string;
};

type SelectFieldProps = {
name: string;
label: string;
placeholder: string;
options: readonly Option[];
control: Control;
error?: FieldError;
required?: boolean;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm whether Control/UseFormRegister are exported and require a generic in the installed react-hook-form
fd -t f 'package.json' -d 2 | xargs -I{} grep -l 'react-hook-form' {} 2>/dev/null
rg -nP --type=ts -C2 '\b(Control|UseFormRegister|RegisterOptions|FieldError)\b' types/ components/forms/

Repository: Dali11/JournalFX

Length of output: 1684


🏁 Script executed:

cat -n types/global.d.ts

Repository: Dali11/JournalFX

Length of output: 6275


🏁 Script executed:

rg 'react-hook-form' package.json

Repository: Dali11/JournalFX

Length of output: 94


🏁 Script executed:

fd -t f 'node_modules/react-hook-form' -d 1 | head -5

Repository: Dali11/JournalFX

Length of output: 42


🏁 Script executed:

find node_modules/react-hook-form -name '*.d.ts' -type f | head -10

Repository: Dali11/JournalFX

Length of output: 124


🏁 Script executed:

rg -A 5 'export.*Control.*=' node_modules/react-hook-form --type=ts 2>/dev/null | head -30

Repository: Dali11/JournalFX

Length of output: 42


🏁 Script executed:

rg 'type Control' node_modules/react-hook-form --type=ts -A 3 2>/dev/null | head -40

Repository: Dali11/JournalFX

Length of output: 42


🌐 Web query:

react-hook-form 7.74.0 Control generic type parameter

💡 Result:

In react-hook-form version 7.74.0 (consistent with recent v7.x releases up to 7.73.x as of 2026-04-27), the Control type is a generic with three type parameters: Control. TFieldValues: The shape of your form values (typically inferred from useForm). TContext: Context object passed to resolver (often any or object). TTransformedValues: Optional transformed values from resolver output (defaults to TFieldValues). Usage example from source types: control?: Control<TFieldValues, any, TTransformedValues> From TypeScript docs: export type UseFormReturn<TFieldValues extends FieldValues = FieldValues, TContext = any, TTransformedValues extends FieldValues | undefined = undefined> = { ... control: Control<TFieldValues, TContext> ... } For generic components: interface Props { control: Control; ... } or more precisely Control<T, any> or Control<T, any, any>. control: Control<TFieldValues, any, TTransformedValues> appears in UseControllerProps and ControllerProps.

Citations:


🌐 Web query:

react-hook-form Control FieldValues type parameter required

💡 Result:

No, the FieldValues type parameter is not required for the react-hook-form Control type. Control is a generic type with default parameters that defaults to FieldValues. The Control type is defined as: export type Control< TFieldValues extends FieldValues = FieldValues, TContext = any, TTransformedValues = TFieldValues, > = { ... } This means Control can be used without explicit type parameters (defaults to Control), or with specific form values like Control. The same applies to ControllerProps and UseControllerProps, which accept control?: Control where TFieldValues defaults to FieldValues. In practice: - For non-generic/reusable components: control: Control or just Control (uses defaults) - For type-safe form-specific usage: control: Control Type errors like "Control not assignable to Control" occur in reusable components expecting the default generic instead of a specific one, requiring proper generic constraints like UseControllerProps or Control.

Citations:


Add missing imports for react-hook-form types in the global declaration file.

Control, FieldError, UseFormRegister, and RegisterOptions are referenced in types/global.d.ts (lines 20, 21, 30-32, 47-48) but never imported. This causes TypeScript to fail with "Cannot find name" errors, preventing the file from compiling.

Add imports at the top of the file:

Suggested fix
+import type { Control, FieldError, UseFormRegister, RegisterOptions, FieldValues } from 'react-hook-form';
+
 declare global {
     type SignInFormData = {

Additionally, Control and UseFormRegister are generic types. For reusable global component types, use Control<FieldValues> and UseFormRegister<FieldValues> instead of bare variants to ensure proper type safety across different form implementations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@types/global.d.ts` around lines 1 - 50, Add the missing react-hook-form type
imports at the top of this global declaration: import Control, FieldError,
UseFormRegister, RegisterOptions and FieldValues from "react-hook-form", then
update the usages in CountrySelectProps, FormInputProps and SelectFieldProps to
use the generic forms Control<FieldValues> and UseFormRegister<FieldValues>
(keep FieldError and RegisterOptions as imported types) so the types
SignInFormData, SignUpFormData, CountrySelectProps, FormInputProps and
SelectFieldProps compile without "Cannot find name" errors.

Comment thread types/global.d.ts
Comment on lines +58 to +62
type SearchCommandProps = {
renderAs?: 'button' | 'text';
label?: string;
initialStocks: StockWithWatchlistStatus[];
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: SearchCommandProps is declared twice with conflicting shapes.

SearchCommandProps is defined at Lines 58–62 (renderAs, label, initialStocks) and again at Lines 171–178 (open, setOpen, renderAs, buttonLabel, buttonVariant, className). TypeScript will raise a duplicate-identifier error, and consumers can't tell which contract is intended. Consolidate into a single definition (or rename one).

Also applies to: 171-178

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@types/global.d.ts` around lines 58 - 62, SearchCommandProps is declared twice
with conflicting shapes; remove the duplicate and consolidate into a single
accurate type (or rename one of them) so TypeScript no longer reports duplicate
identifier errors. Locate both declarations named SearchCommandProps, decide
whether the intended contract includes props like open, setOpen, renderAs,
buttonLabel/buttonVariant/className, label, and initialStocks, then merge them
into one coherent type (or rename the second to e.g. SearchCommandControlProps)
and update all references/usages to the new unified name; ensure exported types
and any components/functions that consume SearchCommandProps are updated to the
consolidated property set.

@Dali11 Dali11 merged commit 5933d70 into main Apr 27, 2026
1 check passed
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.

1 participant