diff --git a/src/components/ProfileSettings.tsx b/src/components/ProfileSettings.tsx index 9983a69..ad1a21a 100644 --- a/src/components/ProfileSettings.tsx +++ b/src/components/ProfileSettings.tsx @@ -101,6 +101,14 @@ export const ProfileSettings: React.FC = ({ const [isParsingResume, setIsParsingResume] = useState(false); const [parseError, setParseError] = useState(null); + const [parseStep, setParseStep] = useState(''); + const [parsedLiveStats, setParsedLiveStats] = useState<{ + name?: string; + skillsCount?: number; + expCount?: number; + eduCount?: number; + provider?: string; + } | null>(null); const handleResumeFileUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; @@ -108,6 +116,8 @@ export const ProfileSettings: React.FC = ({ setIsParsingResume(true); setParseError(null); + setParsedLiveStats(null); + setParseStep(`Reading document ${file.name}...`); try { let bodyData: any = { @@ -116,6 +126,7 @@ export const ProfileSettings: React.FC = ({ }; if (file.name.endsWith('.docx') || file.name.endsWith('.doc')) { + setParseStep('Extracting raw text from DOCX archive via Mammoth...'); const arrayBuffer = await file.arrayBuffer(); const base64 = btoa( new Uint8Array(arrayBuffer).reduce((data, byte) => data + String.fromCharCode(byte), '') @@ -125,6 +136,7 @@ export const ProfileSettings: React.FC = ({ bodyData.resumeText = await file.text(); } + setParseStep('Analyzing profile structure with AI model...'); const res = await fetch('/api/gemini/parse-resume', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -134,6 +146,20 @@ export const ProfileSettings: React.FC = ({ const data = await res.json(); if (data.success && data.profile) { const parsed = data.profile; + setParseStep('Structuring work history & technical skill tokens...'); + + const skills = Array.isArray(parsed.skills) && parsed.skills.length > 0 ? parsed.skills : prev.skills; + const experience = Array.isArray(parsed.experience) && parsed.experience.length > 0 ? parsed.experience : prev.experience; + const education = Array.isArray(parsed.education) && parsed.education.length > 0 ? parsed.education : prev.education; + + setParsedLiveStats({ + name: parsed.fullName || 'Candidate', + skillsCount: skills.length, + expCount: experience.length, + eduCount: education.length, + provider: data.providerUsed || 'Gemini 3.6 Flash' + }); + setProfile(prev => ({ ...prev, fullName: parsed.fullName || prev.fullName, @@ -144,16 +170,19 @@ export const ProfileSettings: React.FC = ({ githubUrl: parsed.githubUrl || prev.githubUrl, portfolioUrl: parsed.portfolioUrl || prev.portfolioUrl, summary: parsed.summary || prev.summary, - skills: Array.isArray(parsed.skills) && parsed.skills.length > 0 ? parsed.skills : prev.skills, - experience: Array.isArray(parsed.experience) && parsed.experience.length > 0 ? parsed.experience : prev.experience, - education: Array.isArray(parsed.education) && parsed.education.length > 0 ? parsed.education : prev.education + skills, + experience, + education })); + + setParseStep('✓ Successfully parsed and updated profile fields!'); + setTimeout(() => setIsParsingResume(false), 3000); } else { setParseError(data.error || 'Failed to parse resume content.'); + setIsParsingResume(false); } } catch (err: any) { setParseError(`Failed to read/parse file: ${err.message}`); - } finally { setIsParsingResume(false); } }; @@ -187,8 +216,8 @@ export const ProfileSettings: React.FC = ({
+ {/* Dynamic Live Parsing Status Modal / Banner */} + {isParsingResume && ( +
+
+
+ + {parseStep} +
+ {parsedLiveStats && ( + + Engine: {parsedLiveStats.provider} + + )} +
+ + {parsedLiveStats && ( +
+
+ Candidate + {parsedLiveStats.name} +
+
+ Skills Found + {parsedLiveStats.skillsCount} Technical Tokens +
+
+ Experience Items + {parsedLiveStats.expCount} Roles & Bullets +
+
+ Credentials + {parsedLiveStats.eduCount} Certifications +
+
+ )} +
+ )} + {parseError && (