Feature import user cv#132
Conversation
|
This PR was not deployed automatically as @badarouzia does not have access to the Railway project. In order to get automatic PR deploys, please add @badarouzia to your workspace on Railway. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
This PR implements a new CV Compatibility Analysis flow in the web app, adding a 3-step UI (upload → “AI processing” overlay → results) and introducing supporting UI atoms/molecules/organisms plus tests.
Changes:
- Reworked
/cv-analysisroute into a stateful orchestration page for upload, processing overlay, and result display. - Added new CV-import UI components (UploaderCard, AIProcessingOverlay, AnalysisResultCard) plus supporting atoms/molecules and Vitest tests.
- Performed minor formatting/normalization updates in shared styles and a JSON data file.
Reviewed changes
Copilot reviewed 13 out of 15 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| web/src/styles/tailwind.css | Normalizes hex color casing for CSS variables. |
| web/src/styles/scrollbar.css | Re-indents scrollbar CSS (no functional change). |
| web/src/styles/calendar.css | Re-formats calendar override CSS (no functional change). |
| web/src/routes/cv-analysis.tsx | New CV analysis workflow page and orchestration logic. |
| web/src/components/organisms/cv-import/UploaderCard.tsx | Drag-and-drop CV upload UI + deadline input/urgency display. |
| web/src/components/organisms/cv-import/Uploadercard.spec.tsx | Adds tests for UploaderCard behavior (upload + deadline). |
| web/src/components/organisms/cv-import/AnalysisResultCard.tsx | Results/CTA card shown after analysis completion. |
| web/src/components/organisms/cv-import/Analysisresultcard.spec.tsx | Adds tests for AnalysisResultCard behavior. |
| web/src/components/organisms/cv-import/AIProcessingOverlay.tsx | Simulated analysis overlay with progress + rotating status messages. |
| web/src/components/molecules/cv-import/Stepper.tsx | Stepper molecule for the multi-step flow. |
| web/src/components/molecules/cv-import/AnalysisStatus.tsx | Spinner + status message molecule used by the overlay. |
| web/src/components/molecules/convincing-banner/reviews.json | Re-indents JSON content (no semantic change). |
| web/src/components/atoms/cv-import/StepIndicator.tsx | Atom for individual step display. |
| web/src/components/atoms/cv-import/ProgressBar.tsx | Progress bar atom used by the overlay. |
| web/src/components/atoms/cv-import/FileBadge.tsx | File extension badge atom used in the uploader UI. |
Comments suppressed due to low confidence (4)
web/src/routes/cv-analysis.tsx:16
/cv-analysisis marked asrequiresAuth: trueinroutes.config.ts, but this route no longer appliesbeforeLoad: createAuthGuard('/cv-analysis'). This makes the page accessible without the configured auth check. Re-add the auth guard (or update the route config if it’s intended to be public) so routing behavior matches the central config.
export const Route = createFileRoute('/cv-analysis')({
component: CVAnalysisPage,
});
web/src/routes/cv-analysis.tsx:45
- URL validation is currently
jobUrl.trim().startsWith('http'), which can treat invalid strings as valid (e.g.httpxyz) and is duplicated in multiple places. Consider centralizing this into a single boolean (e.g.isJobUrlValid) and validating vianew URL(jobUrl)with an allowed protocol list (http:/https:).
const handleStartAnalysis = () => {
if (cvFile && jobUrl.trim().startsWith('http')) {
setIsAnalyzing(true);
}
};
web/src/routes/cv-analysis.tsx:85
onStartCoursecurrently logs to the console. Since this is a user-facing CTA, it should trigger real navigation/action (e.g.router.navigateto the generated curriculum route) or be removed until implemented; leavingconsole.loghere can easily ship to production.
<AnalysisResultCard
onRetry={handleReset}
onStartCourse={() => console.log('Navigating to course path...')}
/>
web/src/components/organisms/cv-import/UploaderCard.tsx:168
- The UI states “Max size: 5 MB”, but there is no size/type validation before calling
onFileSelect(both for drop and input). Add client-side checks (size limit + allowed MIME/types) and provide user feedback when the file is rejected, otherwise users can select larger/unsupported files despite the UI hint.
<input
id="cv-input"
type="file"
hidden
accept=".pdf,.doc,.docx"
onChange={(e) =>
e.target.files?.[0] && onFileSelect(e.target.files[0])
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 15 changed files in this pull request and generated 11 comments.
Comments suppressed due to low confidence (8)
web/src/routes/cv-analysis.tsx:16
- The route definition no longer applies
beforeLoad: createAuthGuard(...). This makes/cv-analysisaccessible without authentication and could expose CV-related UI to unauthenticated users. Re-add the auth guard (or explicitly document/handle why this route must be public).
export const Route = createFileRoute('/cv-analysis')({
component: CVAnalysisPage,
});
web/src/routes/cv-analysis.tsx:85
onStartCoursecurrently just logs to the console. This leaves the primary CTA non-functional in production builds and makes it easy to miss navigation wiring. Consider using TanStack Router navigation (or pass a real callback from a parent) instead ofconsole.log.
<AnalysisResultCard
onRetry={handleReset}
onStartCourse={() => console.log('Navigating to course path...')}
/>
web/src/routes/cv-analysis.tsx:133
- The job-offer URL
<input>has no associated<label>oraria-label, so screen readers will only announce it generically (placeholder text is not a reliable accessible name). Add a visible label or at least anaria-label/aria-labelledby.
<input
type="url"
style={urlInputStyle}
placeholder="Paste LinkedIn, WTTJ link..."
value={jobUrl}
onChange={(e) => setJobUrl(e.target.value)}
/>
web/src/routes/cv-analysis.tsx:174
- This file uses many hardcoded colors and large inline style objects (e.g.
pageContainer,jobCard, button colors) rather than the existing Tailwind + design-token approach used across routes. This makes theming and future design updates harder. Consider translating these styles to Tailwind classes and/or CSS variables (e.g.var(--color-...)).
/** @type {React.CSSProperties} Layout for the main page container */
const pageContainer: React.CSSProperties = {
backgroundColor: '#F8FAFC',
minHeight: '100vh',
padding: '60px 20px',
fontFamily: 'Inter, system-ui, sans-serif',
};
web/src/routes/cv-analysis.tsx:170
- This file references
React.CSSPropertiesbut does not importReact(orCSSProperties) for the React type namespace. In this codebase, other files import React when usingReact.*types (e.g.web/src/types/events.ts:1). Importtype { CSSProperties }fromreact(orimport React from 'react') to avoid TS/lint issues.
/** @type {React.CSSProperties} Layout for the main page container */
const pageContainer: React.CSSProperties = {
backgroundColor: '#F8FAFC',
web/src/components/organisms/cv-import/AIProcessingOverlay.tsx:80
- User-facing title text contains spelling/grammar errors: “Analys TalkUp.AI in processe”. This will be visible during the analysis overlay; please correct it (e.g., “TalkUp.AI analysis in progress”).
<div style={overlayStyle}>
<div style={contentBox}>
<h2 style={{ marginBottom: '24px' }}>Analys TalkUp.AI in processe</h2>
<AnalysisStatus message={currentMessage} />
web/src/components/organisms/cv-import/AIProcessingOverlay.tsx:103
- This file uses
React.CSSPropertiesfor style objects but does not importReact(orCSSProperties). To match existing code patterns and avoid TS/lint issues, importtype { CSSProperties }fromreact(or import React) and use that type instead of relying on a global React namespace.
/** @type {React.CSSProperties} Styles for the full-screen background */
const overlayStyle: React.CSSProperties = {
position: 'fixed',
top: 0,
web/src/components/organisms/cv-import/UploaderCard.tsx:169
- The file input
onChangeforwards the selected file directly toonFileSelectwithout enforcing the advertised constraints (5MB max, allowed types). Add the same size/type validation here as for drag/drop to prevent unsupported uploads via the file picker.
<input
id="cv-input"
type="file"
hidden
accept=".pdf,.doc,.docx"
onChange={(e) =>
e.target.files?.[0] && onFileSelect(e.target.files[0])
}
/>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 15 changed files in this pull request and generated 8 comments.
Comments suppressed due to low confidence (1)
web/src/components/organisms/cv-import/UploaderCard.tsx:168
- The file input
onChangeaccepts and forwards any selected file without enforcing the advertised constraints (max size 5MB, PDF/DOC/DOCX). Implement the same validation here as for drag/drop and display an error message when the file is too large or unsupported.
<input
id="cv-input"
type="file"
hidden
accept=".pdf,.doc,.docx"
onChange={(e) =>
e.target.files?.[0] && onFileSelect(e.target.files[0])
}
There was a problem hiding this comment.
Review — request changes
CI is green but this isn't mergeable as-is. One security blocker, plus systemic design-system violations and a couple of non-functional UI dead-ends. Sorted by severity below; inline comments mark the specific spots.
🔴 Blockers
- Auth guard removed — route now public.
web/src/routes/cv-analysis.tsxdeletesbeforeLoad: createAuthGuard('/cv-analysis'). This page handles user CVs (PII) and was protected; every other authed route keeps the guard. Restore it. (No inline anchor — the line was deleted in this PR.) - Feature ignores the design system. Entire feature is inline
React.CSSPropertieswith hardcoded hex (#2B70C9,#64748B,#F8FAFC,#1D9E75…). The design we implemented since the beginning of the project require semantic tokens (bg-accent,text-text,border-border…) and Tailwind utilities (text-h*,text-body-*). Zero dark-mode support : fixed white/slate backgrounds break the.darkclass. Needs rework to tokens + Tailwind, not inline styles.
🟠 Major
- AI processing is simulated, not real.
AIProcessingOverlayruns asetInterval0→100% timer with no API call;AnalysisResultCardshows static "Analysis Complete!" / "Skills Validated". No CV parsing, no scraping, no backend, no server changes — so "import user CV" stores/sends nothing. If this is an intentional UI-first stub, label it as such in code + PR description. console.logas default behavior. DefaultonStartCourseand the route bothconsole.log(...). "Start My Training" CTA is a dead end. (oxlint no-consoleis set towarn, so it doesn't fail CI — that's why this slipped through.)- URL validation is
.startsWith('http'). If this URL ever reaches a server-side scraper it's an SSRF surface. Validate withURL()+ anhttps:protocol allowlist, and never let a raw user URL drive a server-side fetch without allowlisting.
🟡 Minor
- File naming breaks kebab-case convention (
Analysisresultcard.spec.tsx…); repo norm iskebab-case-dir/index.tsx. - File-type check accepts on MIME OR extension (
||); extension is trivially spoofed — real validation belongs server-side. - Hover via JS (
onMouseOver/Outhex swap) instead of CSS:hover/:focus; tests even assert the JS hex, which is brittle. - French error strings in an otherwise-English UI.
- Missing a11y: the processing overlay is a fixed full-screen modal with no
role="dialog"/aria-modal/focus management/aria-live; the job-URL input has no label. reviews.json/calendar.css/scrollbar.cssdiffs are CRLF→LF churn only;tailwind.csschange is hex-lowercasing only. Revert to keep the diff focused.- Emoji in the button label ("Start My Training 🚀") — not in the design system.
✅ Good
- Atomic structure (atoms/molecules/organisms) is correct.
- Solid test coverage on UploaderCard + AnalysisResultCard (deadline urgency, drag/drop, validation paths).
- Thorough JSDoc;
label htmlFor/idon the file input; timer/interval cleanup inuseEffect.
Fixes for all of the above are being pushed to this branch; I'll pin each comment to the fixing commit.
…, a11y
- restore createAuthGuard on /cv-analysis (route handles CV PII)
- rewrite cv-import feature to semantic tokens + Tailwind (dark-mode), drop inline hex
- add isAllowedJobUrl (URL parse + https allowlist) replacing startsWith('http')
- wire onStartCourse to real navigation, drop console.log dead-ends
- label simulated AI processing + static result card as UI-first stub
- restructure components to kebab-dir/index.tsx, rename specs
- english error strings, CSS hover, overlay/input a11y (role/aria/labels)
- revert EOL/hex churn files
…st, reuse iconMap
5aad3ab to
3bbf6bb
Compare
What type of PR is this? (check all applicable)
Description
This Pull Request introduces the Compatibility Analysis module. This feature allows users to bridge the gap between their current profile and a specific job offer by leveraging AI to extract skills and generate a tailored learning journey.
Features and Workflow
The module follows a structured three-step process as seen in the implemented UI:
Document and Data Intake
AI Processing
Personalized Outcome
Technical Changes
Closes EpitechPromo2027/G-EIP-600-NAN-6-1-eip-tugdual.de-reviers#
Closes EpitechPromo2027/G-EIP-600-NAN-6-1-eip-tugdual.de-reviers#
Workspace
Screenshots