-
Notifications
You must be signed in to change notification settings - Fork 776
feat(route): redesign /resume-builder with Forge AI-inspired landing #4400
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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> | ||
| ); | ||
| } |
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Merge custom When a consumer passes a custom 🐛 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| } | ||||||||||||||||||||
| 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'; | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 Severity Level: Major
|
||||||||||||||||||||||||||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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.(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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 🛠️ 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| 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>} | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: The sign-in action uses Severity Level: Major
|
||||||||||||||||||||||||||
| {!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.
| 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> | ||
| ); | ||
| } |
| 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> | ||
| ); | ||
| } |
| 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'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: This relative stylesheet path is incorrect from Severity Level: Major
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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 }}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 Convert the grid container to a 🪄 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 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 ( 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
Suggested change
🧰 Tools🪛 ast-grep (0.44.1)[warning] 63-63: A list component should have a key to prevent re-rendering 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 (list-component-needs-key) [warning] 50-52: Do not use array indexes for a list component's key (list-component-no-index) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Safeguard action config and bind Currently, Pass both destination props with optional chaining. As per Context snippet 1, 🔗 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
Suggested change
🧰 Tools🪛 ast-grep (0.44.1)[warning] 70-70: A list component should have a key to prevent re-rendering (list-component-needs-key) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </motion.div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </section> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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" />; | ||
| } |
| 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" />; | ||
| } |
| 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" />; | ||
| } |
| 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" />; | ||
| } |
| 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" />; | ||
| } |
There was a problem hiding this comment.
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
ForgeFeaturePageimports that do not resolve (./forge/...and the wrong CSS relative path), so navigating to/resume-builderwill 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 🚨
Steps of Reproduction ✅
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖