Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ const RepoAnalyzerWorkspace = lazy(() => import('./pages/RepoAnalyzer/Workspace'
const LegacyProjectVisualizerLanding = lazy(() => import('./pages/ProjectVisualizer/Landing'));
const ProjectVisualizerDashboard = lazy(() => import('./pages/ProjectVisualizer/Dashboard'));

const ResumeBuilderLanding = lazy(() => import('./pages/features/ResumeBuilderLanding'));
const ResumeBuilderLanding = lazy(() => import('./pages/features/ResumeBuilderForge'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: This route switch now lazy-loads the Forge resume page, but that page currently depends on ForgeFeaturePage imports that do not resolve (./forge/... and the wrong CSS relative path), so navigating to /resume-builder will fail to load the module at runtime. Fix the broken Forge imports first (or keep the previous landing component until they are corrected). [import error]

Severity Level: Critical 🚨
- ❌ /resume-builder landing route fails to render.
- ❌ Build or dev server fails resolving Forge imports.
- ⚠️ Resume hub links into landing break for users.
Steps of Reproduction ✅
1. In `frontend/src/App.jsx` lines 51-66, `AppRoutes` defines the public route
`"/resume-builder"` with element `ResumeBuilderLanding` wrapped in `Suspense` (verified
via Read of App.jsx lines 52-66).

2. At the top of the same file, line 105 defines `const ResumeBuilderLanding = lazy(() =>
import('./pages/features/ResumeBuilderForge'));` (Read of App.jsx lines 20-33), so
navigating to `/resume-builder` triggers a dynamic import of
`frontend/src/pages/features/ResumeBuilderForge.jsx`.

3. `frontend/src/pages/features/ResumeBuilderForge.jsx` imports `ForgeFeaturePage`
(`import ForgeFeaturePage from '../../components/forge/ForgeFeaturePage';` at line 2) and
renders it (`return ForgeFeaturePage slug="resume-builder"` at line 4) as seen in the Read
of that file.

4. `frontend/src/components/forge/ForgeFeaturePage.jsx` (Read lines 1-10) imports
`../styles/forge.css` and `StatusBadge`, `MetricChip`, `ForgeButton`, `BentoCard` from
`./forge/...`, but LS shows `/frontend/src/components/forge` only contains
`BentoCard.jsx`, `ForgeButton.jsx`, `ForgeFeaturePage.jsx`, `MetricChip.jsx`,
`StatusBadge.jsx` and no `forge` subdirectory, and LS of `/frontend/src/styles` shows
`forge.css` under `src/styles`, not `components/styles`. These relative paths therefore
resolve to non-existent `components/styles/forge.css` and `components/forge/forge/*`,
causing module resolution errors when `ForgeFeaturePage` is bundled, so the lazy-loaded
`/resume-builder` landing fails to load.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** frontend/src/App.jsx
**Line:** 105:105
**Comment:**
	*Import Error: This route switch now lazy-loads the Forge resume page, but that page currently depends on `ForgeFeaturePage` imports that do not resolve (`./forge/...` and the wrong CSS relative path), so navigating to `/resume-builder` will fail to load the module at runtime. Fix the broken Forge imports first (or keep the previous landing component until they are corrected).

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

const PortfolioBuilderLanding = lazy(() => import('./pages/features/PortfolioBuilderLanding'));
const ResumeRoastLanding = lazy(() => import('./pages/features/ResumeRoastLanding'));
const GithubPortfolioLanding = lazy(() => import('./pages/features/GithubPortfolioLanding'));
Expand Down
23 changes: 23 additions & 0 deletions frontend/src/components/forge/BentoCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';

/**
* Hover-glow bento card. Tracks cursor for the radial glow (forge signature).
*/
export default function BentoCard({ icon: Icon, title, description, className = '' }) {
const onMove = (e) => {
const r = e.currentTarget.getBoundingClientRect();
e.currentTarget.style.setProperty('--mx', `${e.clientX - r.left}px`);
e.currentTarget.style.setProperty('--my', `${e.clientY - r.top}px`);
};
return (
<div className={`forge-card ${className}`} onMouseMove={onMove}>
{Icon && (
<div className="ico">
<Icon size={22} strokeWidth={1.8} />
</div>
)}
<h3>{title}</h3>
<p>{description}</p>
</div>
);
}
9 changes: 9 additions & 0 deletions frontend/src/components/forge/ForgeButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import { Link } from 'react-router-dom';

/** Forge button — renders as router Link or anchor. */
export default function ForgeButton({ to, href, children, variant = 'primary', ...rest }) {
const cls = `forge-btn ${variant === 'primary' ? 'forge-btn-primary' : 'forge-btn-ghost'}`;
if (to) return <Link to={to} className={cls} {...rest}>{children}</Link>;
return <a href={href || '#'} className={cls} {...rest}>{children}</a>;
Comment on lines +5 to +8

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Merge custom className properly.

When a consumer passes a custom className to this component, it will completely overwrite the base forge-btn classes because {...rest} is spread after className={cls}. Extract className from the props to combine it correctly.

🐛 Proposed fix
-export default function ForgeButton({ to, href, children, variant = 'primary', ...rest }) {
-  const cls = `forge-btn ${variant === 'primary' ? 'forge-btn-primary' : 'forge-btn-ghost'}`;
+export default function ForgeButton({ to, href, children, variant = 'primary', className = '', ...rest }) {
+  const cls = `forge-btn ${variant === 'primary' ? 'forge-btn-primary' : 'forge-btn-ghost'} ${className}`.trim();
   if (to) return <Link to={to} className={cls} {...rest}>{children}</Link>;
   return <a href={href || '#'} className={cls} {...rest}>{children}</a>;
 }
📝 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
export default function ForgeButton({ to, href, children, variant = 'primary', ...rest }) {
const cls = `forge-btn ${variant === 'primary' ? 'forge-btn-primary' : 'forge-btn-ghost'}`;
if (to) return <Link to={to} className={cls} {...rest}>{children}</Link>;
return <a href={href || '#'} className={cls} {...rest}>{children}</a>;
export default function ForgeButton({ to, href, children, variant = 'primary', className = '', ...rest }) {
const cls = `forge-btn ${variant === 'primary' ? 'forge-btn-primary' : 'forge-btn-ghost'} ${className}`.trim();
if (to) return <Link to={to} className={cls} {...rest}>{children}</Link>;
return <a href={href || '#'} className={cls} {...rest}>{children}</a>;
}
🤖 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 `@frontend/src/components/forge/ForgeButton.jsx` around lines 5 - 8, Update
ForgeButton to destructure the incoming className prop separately from ...rest,
then combine it with the generated forge-btn and variant classes. Use the merged
className on both the Link and anchor branches, while ensuring ...rest no longer
overwrites it.

}
166 changes: 166 additions & 0 deletions frontend/src/components/forge/ForgeFeaturePage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { useAuth } from '../../hooks/useAuth';
import { FEATURES_BY_SLUG } from '../../data/featuresConfig';
import Seo from '../../components/Seo';
import '../styles/forge.css';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The stylesheet import path is off by one directory level, so it resolves to components/styles/forge.css instead of src/styles/forge.css. This will either fail the build or load no styles for the new page; update the relative path to the actual styles directory. [import error]

Severity Level: Major ⚠️
⚠️ Forge landing renders unstyled; Forge design not applied.
⚠️ Visual consistency of resume-builder marketing experience degraded.
Steps of Reproduction ✅
1. `ForgeFeaturePage` is loaded for `/resume-builder` via `ResumeBuilderForge`
(`frontend/src/pages/features/ResumeBuilderForge.jsx:1-4`) and the route configuration in
`frontend/src/App.jsx:26,81`.

2. When `ForgeFeaturePage.jsx` is evaluated, it executes the stylesheet import at
`frontend/src/components/forge/ForgeFeaturePage.jsx:6`, which uses the relative path
`../styles/forge.css` from the `components/forge` directory.

3. From `frontend/src/components/forge`, the path `../styles/forge.css` resolves to
`frontend/src/components/styles/forge.css`, but a glob over the repository shows the
actual stylesheet at `frontend/src/styles/forge.css` (`functions.Glob` found
`/frontend/src/styles/forge.css`).

4. Because `frontend/src/components/styles/forge.css` does not exist, the build either
throws a "module not found" error or silently omits the stylesheet, resulting in the Forge
landing rendering without the intended Forge design-system styles.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** frontend/src/components/forge/ForgeFeaturePage.jsx
**Line:** 6:6
**Comment:**
	*Import Error: The stylesheet import path is off by one directory level, so it resolves to `components/styles/forge.css` instead of `src/styles/forge.css`. This will either fail the build or load no styles for the new page; update the relative path to the actual styles directory.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

import StatusBadge from './forge/StatusBadge';
import MetricChip from './forge/MetricChip';
import ForgeButton from './forge/ForgeButton';
import BentoCard from './forge/BentoCard';
Comment on lines +7 to +10

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The relative imports for local forge components point to a non-existent nested forge/ subdirectory. Because this file is already inside components/forge, these paths will fail module resolution and break the page at build/runtime; import the sibling files directly from the current directory instead. [import error]

Severity Level: Critical 🚨
❌ /resume-builder Forge landing fails due to import error.
⚠️ Future Forge feature pages would also fail to load.
Steps of Reproduction ✅
1. The route `/resume-builder` is wired in `frontend/src/App.jsx:81` to
`ResumeBuilderLanding`, which is lazily imported from
`./pages/features/ResumeBuilderForge` at `frontend/src/App.jsx:26`.

2. `frontend/src/pages/features/ResumeBuilderForge.jsx:1-4` renders `<ForgeFeaturePage
slug="resume-builder" />`, pulling in
`frontend/src/components/forge/ForgeFeaturePage.jsx`.

3. `ForgeFeaturePage.jsx:7-10` imports `StatusBadge`, `MetricChip`, `ForgeButton`, and
`BentoCard` from `./forge/...`, implying a nested `forge` subdirectory under
`frontend/src/components/forge`.

4. A directory listing of `frontend/src/components/forge` (`LS` output) shows only sibling
files `StatusBadge.jsx`, `MetricChip.jsx`, `ForgeButton.jsx`, and `BentoCard.jsx` directly
in that folder with no `forge/` subdirectory, so the bundler cannot resolve
`./forge/StatusBadge` etc., causing a module resolution error and preventing the Forge
feature page from loading.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** frontend/src/components/forge/ForgeFeaturePage.jsx
**Line:** 7:10
**Comment:**
	*Import Error: The relative imports for local forge components point to a non-existent nested `forge/` subdirectory. Because this file is already inside `components/forge`, these paths will fail module resolution and break the page at build/runtime; import the sibling files directly from the current directory instead.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +5 to +10

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Fix broken relative import paths.

The current paths for local components and the forge.css stylesheet are incorrect based on this file's location (src/components/forge/ForgeFeaturePage.jsx). These will trigger compilation failures because they evaluate to non-existent directories like src/components/styles/ and src/components/forge/forge/.

🛠️ Proposed fix
-import Seo from '../../components/Seo';
-import '../styles/forge.css';
-import StatusBadge from './forge/StatusBadge';
-import MetricChip from './forge/MetricChip';
-import ForgeButton from './forge/ForgeButton';
-import BentoCard from './forge/BentoCard';
+import Seo from '../Seo';
+import '../../styles/forge.css';
+import StatusBadge from './StatusBadge';
+import MetricChip from './MetricChip';
+import ForgeButton from './ForgeButton';
+import BentoCard from './BentoCard';
📝 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
import Seo from '../../components/Seo';
import '../styles/forge.css';
import StatusBadge from './forge/StatusBadge';
import MetricChip from './forge/MetricChip';
import ForgeButton from './forge/ForgeButton';
import BentoCard from './forge/BentoCard';
import Seo from '../Seo';
import '../../styles/forge.css';
import StatusBadge from './StatusBadge';
import MetricChip from './MetricChip';
import ForgeButton from './ForgeButton';
import BentoCard from './BentoCard';
🤖 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 `@frontend/src/components/forge/ForgeFeaturePage.jsx` around lines 5 - 10,
Correct the relative imports in ForgeFeaturePage so they resolve from
src/components/forge: point forge.css to the styles directory at the appropriate
parent level, and import StatusBadge, MetricChip, ForgeButton, and BentoCard
from the local forge component directory without duplicating forge in the path.


const ICONS = {
FileText: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6',
Type: 'M4 7V4h16v3 M9 20h6 M12 4v16',
Github: 'M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22',
Sparkles: 'M12 3v3 M12 18v3 M3 12h3 M18 12h3 M5.6 5.6l2.1 2.1 M16.3 16.3l2.1 2.1 M18.4 5.6l-2.1 2.1 M7.7 16.3l-2.1 2.1',
BarChart: 'M3 3v18h18 M8 17V9 M13 17V5 M18 17v-6',
Layout: 'M3 3h18v18H3z M3 9h18 M9 21V9',
Linkedin: 'M4 4a2 2 0 1 0 0 4 2 2 0 0 0 0-4z M4 9h4v11H4z M10 9h4v3a4 4 0 0 1 8 0v8h-4v-7a2 2 0 0 0-4 0v7h-4z',
Download: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4 M7 10l5 5 5-5 M12 15V3',
Mic: 'M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z M19 10v2a7 7 0 0 1-14 0v-2 M12 19v4 M8 23h8',
Briefcase: 'M2 7h20v13H2z M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16',
Flame: 'M12 2s4 4 4 8a4 4 0 0 1-8 0c0-1 .5-2 1-3-2 1-4 3-4 6a7 7 0 0 0 14 0c0-5-7-11-7-11z',
Network: 'M12 2a3 3 0 1 0 0 6 3 3 0 0 0 0-6z M5 16a3 3 0 1 0 0 6 3 3 0 0 0 0-6z M19 16a3 3 0 1 0 0 6 3 3 0 0 0 0-6z M7 18l3-7 M17 18l-3-7 M8 19h8',
Users: 'M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2 M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z M23 21v-2a4 4 0 0 0-3-3.87 M16 3.13a4 4 0 0 1 0 7.75',
Palette: 'M12 2a9 9 0 0 0 0 18c1.1 0 2-.9 2-2 0-.5-.2-1-.5-1.3-.3-.4-.5-.8-.5-1.2 0-1 .8-1.8 1.8-1.8H17a4 4 0 0 0 4-4c0-4.4-4-8-9-8z M7.5 11.5A1 1 0 1 0 7 13a1 1 0 0 0 .5-1.5z M12 7.5A1 1 0 1 0 12.5 9 1 1 0 0 0 12 7.5z',
Zap: 'M13 2L3 14h9l-1 8 10-12h-9z',
Smartphone: 'M7 2h10a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2z M12 18h.01',
Globe: 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20z M2 12h20 M12 2a15 15 0 0 1 0 20 M12 2a15 15 0 0 0 0 20',
};

function Icon({ name, size = 22, strokeWidth = 1.8 }) {
const d = ICONS[name];
if (!d) return null;
return (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
{d.split(' M').map((seg, i) => (
<path key={i} d={(i === 0 ? seg : 'M' + seg)} />
))}
</svg>
);
}

/**
* ForgeFeaturePage — data-driven landing for a single feature route.
* Reads the existing FEATURES_BY_SLUG[slug] config and renders a cohesive,
* forge-ai-inspired page (status chips, monospace metrics, bento showcase, steps).
* Each route landing imports this with its slug.
*/
export default function ForgeFeaturePage({ slug }) {
const { user } = useAuth();
const config = FEATURES_BY_SLUG[slug];
if (!config) return <div className="forge-page">Unknown feature: {slug}</div>;

const heroCtaText = user ? config.primaryAction.label : config.hero.primaryCta.text;
const heroCtaLink = user ? config.primaryAction.to : config.hero.primaryCta.to;
const ctaText = user ? config.primaryAction.label : config.cta.ctaText;
const ctaLink = user ? config.primaryAction.to : config.cta.ctaTo;

return (
<div className="forge-page">
<Seo {...config.seo} />

<nav className="forge-nav">
<Link to="/" className="forge-brand">CareerPilot<span className="dot">.</span></Link>
<div className="forge-nav-links">
<Link to="/resume-builder" className="forge-nav-link">Resume Builder</Link>
<Link to="/portfolio-builder" className="forge-nav-link">Portfolio</Link>
<Link to="/job-finder" className="forge-nav-link">Job Finder</Link>
<Link to="/mock-interview" className="forge-nav-link">Mock Interview</Link>
</div>
<div className="forge-nav-actions">
{!user && <ForgeButton href="/login" variant="ghost">Sign in</ForgeButton>}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The sign-in action uses href for an internal route, which makes ForgeButton render a plain anchor and triggers a full page reload instead of client-side router navigation. This resets in-memory app state and creates inconsistent navigation behavior compared with other internal links; use router navigation (to) for internal paths. [api mismatch]

Severity Level: Major ⚠️
⚠️ Sign-in from Forge landing triggers full SPA reload.
⚠️ Navigation behavior inconsistent with other internal login links.
Steps of Reproduction ✅
1. For unauthenticated visitors, the `/resume-builder` route renders `ForgeFeaturePage`
via `ResumeBuilderForge` (`frontend/src/pages/features/ResumeBuilderForge.jsx:1-4` and
`frontend/src/App.jsx:26,81`), with `useAuth` returning `user = null`.

2. In this state, the navbar actions at
`frontend/src/components/forge/ForgeFeaturePage.jsx:72-75` render the sign-in button as
`{!user && <ForgeButton href="/login" variant="ghost">Sign in</ForgeButton>}`, passing
`href` but no `to`.

3. The `ForgeButton` implementation at `frontend/src/components/forge/ForgeButton.jsx:4-8`
renders a `<Link>` only when `to` is provided, and otherwise falls back to `<a href={href
|| '#'} ...>`, so the Forge sign-in button becomes a plain anchor navigating to `/login`.

4. Clicking this anchor from the Forge landing triggers a full page reload to `/login`,
unlike other login entry points which use `react-router-dom`'s `<Link to="/login">` (e.g.
`frontend/src/components/ui/LandingNavbar.jsx:29`,
`frontend/src/components/Navbar.jsx:356-360`), leading to inconsistent navigation behavior
and resetting any in-memory state on the Forge page.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** frontend/src/components/forge/ForgeFeaturePage.jsx
**Line:** 73:73
**Comment:**
	*Api Mismatch: The sign-in action uses `href` for an internal route, which makes `ForgeButton` render a plain anchor and triggers a full page reload instead of client-side router navigation. This resets in-memory app state and creates inconsistent navigation behavior compared with other internal links; use router navigation (`to`) for internal paths.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Use client-side routing for the login link.

Using href here bypasses React Router and causes a full browser page reload. Switch to to="/login" to keep the SPA navigation fast and seamless.

🚀 Proposed fix
-          {!user && <ForgeButton href="/login" variant="ghost">Sign in</ForgeButton>}
+          {!user && <ForgeButton to="/login" variant="ghost">Sign in</ForgeButton>}
📝 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
{!user && <ForgeButton href="/login" variant="ghost">Sign in</ForgeButton>}
{!user && <ForgeButton to="/login" variant="ghost">Sign in</ForgeButton>}
🤖 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 `@frontend/src/components/forge/ForgeFeaturePage.jsx` at line 73, Update the
unauthenticated login link in ForgeFeaturePage’s ForgeButton to use the React
Router `to` prop with `/login` instead of `href`, preserving the existing ghost
variant and sign-in label.

<ForgeButton to={heroCtaLink} variant="primary">{user ? 'Dashboard' : 'Get started for free'}</ForgeButton>
</div>
</nav>

{/* HERO */}
<section className="forge-section forge-container forge-hero">
<StatusBadge label="OPERATIONAL" />
<h1>
{config.hero.title} <span className="accent">{config.hero.accentText}</span>
</h1>
<p>{config.hero.description}</p>
<div className="forge-hero-actions">
<ForgeButton to={heroCtaLink} variant="primary">{heroCtaText}</ForgeButton>
{config.hero.secondaryCta && (
<ForgeButton href={config.hero.secondaryCta.href} variant="ghost">{config.hero.secondaryCta.text}</ForgeButton>
)}
</div>
<div className="forge-metrics">
{config.hero.stats.map((s, i) => (
<MetricChip key={i} value={s.value} label={s.label} accent={i === 0} />
))}
</div>
</section>

{/* SHOWCASE */}
{config.showcase && (
<section className="forge-section forge-container" id="demo">
<div style={{ textAlign: 'center' }}>
<span className="forge-label">Capabilities</span>
<h2 className="forge-h2">{config.showcase.heading}</h2>
</div>
<div className="forge-bento">
{config.showcase.features.map((f, i) => (
<BentoCard key={i} icon={(p) => <Icon name={f.icon} {...p} />} title={f.title} description={f.description} />
))}
</div>
</section>
)}

{/* HOW IT WORKS */}
{config.howItWorks && (
<section className="forge-section forge-container">
<div style={{ textAlign: 'center' }}>
<span className="forge-label">Workflow</span>
<h2 className="forge-h2">{config.howItWorks.title}</h2>
</div>
<div className="forge-steps">
{config.howItWorks.steps.map((s, i) => (
<div key={i} className="forge-step">
<span className="n">STEP {s.number}</span>
<h4>{s.title}</h4>
<p>{s.description}</p>
</div>
))}
</div>
</section>
)}

{/* TESTIMONIALS */}
{config.testimonials && config.testimonials.items?.length > 0 && (
<section className="forge-section forge-container">
<div style={{ textAlign: 'center' }}>
<span className="forge-label">Signal</span>
<h2 className="forge-h2">{config.testimonials.heading}</h2>
</div>
{config.testimonials.items.map((t, i) => (
<div key={i} className="forge-quote">
<p className="q">“{t.quote}”</p>
<div className="who">
{t.avatar && <img src={t.avatar} alt="" width="36" height="36" style={{ borderRadius: '50%' }} />}
<span><strong style={{ color: 'var(--forge-text)' }}>{t.name}</strong> · {t.role}{t.company ? ` @ ${t.company}` : ''}</span>
<span className="metric" style={{ marginLeft: 'auto' }}>{t.metric}</span>
</div>
</div>
))}
</section>
)}

{/* CTA */}
<section className="forge-container">
<div className="forge-cta">
<h2>{config.cta.headline}</h2>
<p>{config.cta.subtext}</p>
<ForgeButton to={ctaLink} variant="primary">{ctaText}</ForgeButton>
</div>
</section>

<footer className="forge-foot">
CareerPilot · {config.name} · built with the Forge design system
</footer>
</div>
);
}
11 changes: 11 additions & 0 deletions frontend/src/components/forge/MetricChip.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';

/** Monospace metric chip used in hero stat rows. */
export default function MetricChip({ value, label, accent = false }) {
return (
<div className="forge-metric">
<div className={`v ${accent ? 'accent' : ''}`}>{value}</div>
<div className="l">{label}</div>
</div>
);
}
15 changes: 15 additions & 0 deletions frontend/src/components/forge/StatusBadge.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';

/**
* Forge signature status chip — operational/SLA style pill.
* variant: 'ok' | 'warn' | 'danger'
*/
export default function StatusBadge({ label = 'OPERATIONAL', variant = 'ok', className = '' }) {
const v = variant === 'danger' ? 'danger' : variant === 'warn' ? 'warn' : 'ok';
return (
<span className={`forge-status ${v} ${className}`}>
<span className="pulse" />
{label}
</span>
);
}
80 changes: 80 additions & 0 deletions frontend/src/components/landing/ForgeToolsSection.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from 'react';
import { motion } from 'framer-motion';
import { FEATURES } from '../../data/featuresConfig';
import '../styles/forge.css';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: This relative stylesheet path is incorrect from components/landing and points to a non-existent components/styles/forge.css, which will cause a module resolution failure. Update it to the correct path under src/styles. [import error]

Severity Level: Major ⚠️
- ⚠️ Future Forge tools grid component fails bundling.
- ⚠️ CSS import mismatch complicates Forge components reuse.
Steps of Reproduction ✅
1. `frontend/src/components/landing/ForgeToolsSection.jsx` imports the Forge stylesheet
with `import '../styles/forge.css';` at line 4 (verified via Read of that file).

2. LS of `/frontend/src/styles` shows `forge.css` located at
`frontend/src/styles/forge.css`, and LS of `/frontend/src/components` plus
`/frontend/src/components/styles` confirms there is no `components/styles/forge.css`, so
from `components/landing` the relative path `../styles/forge.css` actually targets a
non-existent `frontend/src/components/styles/forge.css`.

3. JavaScript module resolution for relative paths means any page that imports
`ForgeToolsSection` will cause the bundler to resolve `../styles/forge.css` from the
`components/landing` directory, which fails because that file does not exist, resulting in
a "module not found" error for the CSS import.

4. Currently `ForgeToolsSection` has no external callers (Grep for `ForgeToolsSection`
only finds its own file), so the issue is dormant today but will reliably surface as soon
as this component is wired into a landing page, breaking the page’s build or runtime load.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** frontend/src/components/landing/ForgeToolsSection.jsx
**Line:** 4:4
**Comment:**
	*Import Error: This relative stylesheet path is incorrect from `components/landing` and points to a non-existent `components/styles/forge.css`, which will cause a module resolution failure. Update it to the correct path under `src/styles`.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

import StatusBadge from '../forge/StatusBadge';
import ForgeButton from '../forge/ForgeButton';

const ICON_PATHS = {
FileText: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6',
Palette: 'M12 2a9 9 0 0 0 0 18c1.1 0 2-.9 2-2 0-.5-.2-1-.5-1.3-.3-.4-.5-.8-.5-1.2 0-1 .8-1.8 1.8-1.8H17a4 4 0 0 0 4-4c0-4.4-4-8-9-8z M7.5 11.5A1 1 0 1 0 7 13a1 1 0 0 0 .5-1.5z M12 7.5A1 1 0 1 0 12.5 9 1 1 0 0 0 12 7.5z',
Flame: 'M12 2s4 4 4 8a4 4 0 0 1-8 0c0-1 .5-2 1-3-2 1-4 3-4 6a7 7 0 0 0 14 0c0-5-7-11-7-11z',
Github: 'M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22',
Network: 'M12 2a3 3 0 1 0 0 6 3 3 0 0 0 0-6z M5 16a3 3 0 1 0 0 6 3 3 0 0 0 0-6z M19 16a3 3 0 1 0 0 6 3 3 0 0 0 0-6z M7 18l3-7 M17 18l-3-7 M8 19h8',
Mic: 'M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z M19 10v2a7 7 0 0 1-14 0v-2 M12 19v4 M8 23h8',
Users: 'M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2 M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z M23 21v-2a4 4 0 0 0-3-3.87 M16 3.13a4 4 0 0 1 0 7.75',
Briefcase: 'M2 7h20v13H2z M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16',
};

const header = { hidden: {}, show: { transition: { staggerChildren: 0.12 } } };
const item = {
hidden: { opacity: 0, y: 24 },
show: { opacity: 1, y: 0, transition: { duration: 0.6, ease: [0.16, 1, 0.3, 1] } },
};

export default function ForgeToolsSection() {
return (
<section className="forge-page" style={{ padding: '84px 0' }}>
<div className="forge-container">
<motion.div
variants={header}
initial="hidden"
whileInView="show"
viewport={{ once: true, margin: '-100px' }}
style={{ textAlign: 'center' }}
>
<motion.div variants={item} style={{ display: 'flex', justifyContent: 'center', marginBottom: 18 }}>
<StatusBadge label="ALL SYSTEMS OPERATIONAL" />
</motion.div>
<motion.h2 variants={item} className="forge-h2">
Powerful AI tools for <span style={{ color: 'var(--forge-accent)' }}>every step</span>
</motion.h2>
<motion.p variants={item} className="forge-sub">
From your first resume draft to your final interview, CareerPilot gives you the unfair advantage you need.
</motion.p>
</motion.div>

<div className="forge-tools-grid" style={{ marginTop: 44 }}>

Copy link
Copy Markdown
Contributor

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

Fix broken Framer Motion variant propagation for tool cards.

The tool cards are wrapped in a standard <div> sibling to the header motion.div. Because they are not descendants of an active motion parent, the variants={item} prop on the tool cards will not be triggered when scrolled into view, causing the grid animation to fail entirely.

Convert the grid container to a motion.div and apply the same trigger props to restore the stagger animation. Remember to also change the closing </div> to </motion.div> on line 76.

🪄 Proposed fix
-        <div className="forge-tools-grid" style={{ marginTop: 44 }}>
+        <motion.div
+          className="forge-tools-grid"
+          style={{ marginTop: 44 }}
+          variants={header}
+          initial="hidden"
+          whileInView="show"
+          viewport={{ once: true, margin: '-100px' }}
+        >
🤖 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 `@frontend/src/components/landing/ForgeToolsSection.jsx` at line 47, Convert
the tool-card grid container in ForgeToolsSection from div to motion.div,
preserving its className and inline style, and apply the same scroll/animation
trigger props used by the surrounding motion section so its child variants and
stagger animation activate. Update the matching closing tag while leaving the
tool card markup unchanged.

{FEATURES.map((f, i) => {
const Icon = (p) => (
<svg width={24} height={24} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round" {...p}>
{(ICON_PATHS[f.icon?.name] || ICON_PATHS.FileText).split(' M').map((seg, k) => (
<path key={k} d={k === 0 ? seg : 'M' + seg} />
))}
</svg>
);
const large = f.size === 'large';
return (
<motion.div
key={f.slug}
variants={item}
className={`forge-tool ${large ? 'lg' : ''}`}
style={large ? { display: 'flex', flexDirection: 'column', justifyContent: 'space-between' } : {}}
>
<div className="ico"><Icon /></div>
Comment on lines +49 to +64

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Inline the SVG to prevent unmounting/remounting on every render.

Defining a React component (Icon) inside a .map loop creates a new component type on every render. This forces React to destroy and recreate the DOM nodes instead of cleanly updating them, degrading performance.

Inline the SVG directly to avoid this overhead.

⚡ Proposed fix
-            const Icon = (p) => (
-              <svg width={24} height={24} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round" {...p}>
-                {(ICON_PATHS[f.icon?.name] || ICON_PATHS.FileText).split(' M').map((seg, k) => (
-                  <path key={k} d={k === 0 ? seg : 'M' + seg} />
-                ))}
-              </svg>
-            );
             const large = f.size === 'large';
             return (
               <motion.div
                 key={f.slug}
                 variants={item}
                 className={`forge-tool ${large ? 'lg' : ''}`}
                 style={large ? { display: 'flex', flexDirection: 'column', justifyContent: 'space-between' } : {}}
               >
-                <div className="ico"><Icon /></div>
+                <div className="ico">
+                  <svg width={24} height={24} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round">
+                    {(ICON_PATHS[f.icon?.name] || ICON_PATHS.FileText).split(' M').map((seg, k) => (
+                      <path key={k} d={k === 0 ? seg : 'M' + seg} />
+                    ))}
+                  </svg>
+                </div>
📝 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 Icon = (p) => (
<svg width={24} height={24} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round" {...p}>
{(ICON_PATHS[f.icon?.name] || ICON_PATHS.FileText).split(' M').map((seg, k) => (
<path key={k} d={k === 0 ? seg : 'M' + seg} />
))}
</svg>
);
const large = f.size === 'large';
return (
<motion.div
key={f.slug}
variants={item}
className={`forge-tool ${large ? 'lg' : ''}`}
style={large ? { display: 'flex', flexDirection: 'column', justifyContent: 'space-between' } : {}}
>
<div className="ico"><Icon /></div>
const large = f.size === 'large';
return (
<motion.div
key={f.slug}
variants={item}
className={`forge-tool ${large ? 'lg' : ''}`}
style={large ? { display: 'flex', flexDirection: 'column', justifyContent: 'space-between' } : {}}
>
<div className="ico">
<svg width={24} height={24} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round">
{(ICON_PATHS[f.icon?.name] || ICON_PATHS.FileText).split(' M').map((seg, k) => (
<path key={k} d={k === 0 ? seg : 'M' + seg} />
))}
</svg>
</div>
🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 63-63: A list component should have a key to prevent re-rendering
Context:


Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(list-component-needs-key)


[warning] 63-63: A list component should have a key to prevent re-rendering
Context:
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(list-component-needs-key)


[warning] 50-52: Do not use array indexes for a list component's key
Context: (ICON_PATHS[f.icon?.name] || ICON_PATHS.FileText).split(' M').map((seg, k) => (
<path key={k} d={k === 0 ? seg : 'M' + seg} />
))
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(list-component-no-index)

🤖 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 `@frontend/src/components/landing/ForgeToolsSection.jsx` around lines 49 - 64,
Inline the SVG markup within the forge-tools map render instead of defining the
per-item Icon component. Preserve the existing ICON_PATHS fallback,
path-splitting logic, SVG attributes, and prop spreading while removing the
locally recreated Icon component and rendering the SVG directly inside the ico
container.

<div>
<span className="badge-tag">{f.badge || 'Tool'}</span>
<h3>{f.name}</h3>
<p>{f.tagline}</p>
</div>
<div style={{ marginTop: 18 }}>
<ForgeButton to={f.primaryAction.to} variant="ghost">{f.primaryAction.label} →</ForgeButton>
</div>
Comment on lines +70 to +72

Copy link
Copy Markdown
Contributor

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

Safeguard action config and bind href for external links.

Currently, ForgeButton only receives the to prop. If a feature configures an href instead of to, the button's internal link logic will break or render empty. Additionally, accessing f.primaryAction.to without optional chaining risks a runtime crash if the configuration is incomplete.

Pass both destination props with optional chaining. As per Context snippet 1, ForgeButton gracefully handles the fallback logic between to and href.

🔗 Proposed fix
-                <div style={{ marginTop: 18 }}>
-                  <ForgeButton to={f.primaryAction.to} variant="ghost">{f.primaryAction.label} →</ForgeButton>
-                </div>
+                <div style={{ marginTop: 18 }}>
+                  <ForgeButton to={f.primaryAction?.to} href={f.primaryAction?.href} variant="ghost">
+                    {f.primaryAction?.label} →
+                  </ForgeButton>
+                </div>
📝 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
<div style={{ marginTop: 18 }}>
<ForgeButton to={f.primaryAction.to} variant="ghost">{f.primaryAction.label}</ForgeButton>
</div>
<div style={{ marginTop: 18 }}>
<ForgeButton to={f.primaryAction?.to} href={f.primaryAction?.href} variant="ghost">
{f.primaryAction?.label}
</ForgeButton>
</div>
🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 70-70: A list component should have a key to prevent re-rendering
Context: {f.primaryAction.label} →
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(list-component-needs-key)

🤖 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 `@frontend/src/components/landing/ForgeToolsSection.jsx` around lines 70 - 72,
Update the ForgeButton usage in ForgeToolsSection to pass both destination
props, using optional chaining for f.primaryAction.to and f.primaryAction.href.
Preserve the existing label and variant while allowing ForgeButton’s fallback
logic to handle internal and external links safely.

</motion.div>
);
})}
</div>
</div>
</section>
);
}
5 changes: 5 additions & 0 deletions frontend/src/pages/features/GithubPortfolioForge.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';
import ForgeFeaturePage from '../../components/forge/ForgeFeaturePage';
export default function GithubPortfolioForge() {
return <ForgeFeaturePage slug="github-portfolio" />;
}
5 changes: 5 additions & 0 deletions frontend/src/pages/features/MockInterviewForge.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';
import ForgeFeaturePage from '../../components/forge/ForgeFeaturePage';
export default function MockInterviewForge() {
return <ForgeFeaturePage slug="mock-interview" />;
}
5 changes: 5 additions & 0 deletions frontend/src/pages/features/ProjectVisualizerForge.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';
import ForgeFeaturePage from '../../components/forge/ForgeFeaturePage';
export default function ProjectVisualizerForge() {
return <ForgeFeaturePage slug="project-visualizer" />;
}
5 changes: 5 additions & 0 deletions frontend/src/pages/features/ResumeBuilderForge.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';
import ForgeFeaturePage from '../../components/forge/ForgeFeaturePage';
export default function ResumeBuilderForge() {
return <ForgeFeaturePage slug="resume-builder" />;
}
5 changes: 5 additions & 0 deletions frontend/src/pages/features/ResumeRoastForge.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';
import ForgeFeaturePage from '../../components/forge/ForgeFeaturePage';
export default function ResumeRoastForge() {
return <ForgeFeaturePage slug="resume-roast" />;
}
Loading
Loading