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])
}
| let timeoutId: NodeJS.Timeout; | ||
|
|
||
| const interval = setInterval(() => { | ||
| setProgress((prev) => { | ||
| // Si on est déjà à 100 ou plus, on arrête tout | ||
| if (prev >= 100) { | ||
| clearInterval(interval); | ||
| timeoutId = setTimeout(onFinished, 500); | ||
| return 100; | ||
| } | ||
|
|
||
| // On calcule la prochaine étape sans jamais dépasser 100 | ||
| const nextProgress = Math.min(prev + 2, 100); | ||
|
|
||
| // Mise à jour du message en fonction de la progression réelle corrigée | ||
| const msgIndex = Math.floor((nextProgress / 100) * messages.length); | ||
| setCurrentMessage(messages[msgIndex] || messages[messages.length - 1]); | ||
|
|
||
| return nextProgress; | ||
| }); | ||
| }, 100); | ||
|
|
||
| // Nettoyage de l'intervalle ET du timeout au démontage du composant | ||
| return () => { | ||
| clearInterval(interval); | ||
| if (timeoutId) { | ||
| clearTimeout(timeoutId); | ||
| } | ||
| }; |
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