-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy patheslint_report.json
More file actions
1 lines (1 loc) · 535 KB
/
Copy patheslint_report.json
File metadata and controls
1 lines (1 loc) · 535 KB
1
[{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\eslint.config.mjs","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\next.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\postcss.config.mjs","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\actions\\extract.ts","messages":[{"ruleId":"@typescript-eslint/no-require-imports","severity":2,"message":"A `require()` style import is forbidden.","line":18,"column":35,"nodeType":"CallExpression","messageId":"noRequireImports","endLine":18,"endColumn":54},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":22,"column":67,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":22,"endColumn":70,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[741,744],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[741,744],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":23,"column":67,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":23,"endColumn":70,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[846,849],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[846,849],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":27,"column":68,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":27,"endColumn":71,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1076,1079],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1076,1079],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":28,"column":59,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":28,"endColumn":62,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1145,1148],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1145,1148],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":29,"column":56,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":29,"endColumn":59,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1211,1214],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1211,1214],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'e' is defined but never used.","line":37,"column":34,"nodeType":"Identifier","messageId":"unusedVar","endLine":37,"endColumn":35},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":44,"column":32,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":44,"endColumn":35,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1882,1885],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1882,1885],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":92,"column":21,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":92,"endColumn":24,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[3960,3963],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[3960,3963],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":8,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use server';\r\n\r\nimport mammoth from \"mammoth\";\r\nimport { GoogleGenAI } from \"@google/genai\";\r\n\r\nexport async function extractText(formData: FormData) {\r\n try {\r\n const file = formData.get(\"file\") as File;\r\n if (!file) {\r\n return { success: false, error: \"No file provided\" };\r\n }\r\n\r\n const buffer = Buffer.from(await file.arrayBuffer());\r\n let extractedText = \"\";\r\n\r\n if (file.type === \"application/pdf\") {\r\n try {\r\n const PDFParser = require(\"pdf2json\");\r\n const pdfParser = new PDFParser(null, 1);\r\n\r\n extractedText = await new Promise((resolve, reject) => {\r\n pdfParser.on(\"pdfParser_dataError\", (errData: any) => reject(errData.parserError));\r\n pdfParser.on(\"pdfParser_dataReady\", (pdfData: any) => {\r\n try {\r\n // Custom text extraction to fix spacing issues\r\n let text = \"\";\r\n pdfData.formImage.Pages.forEach((page: any) => {\r\n page.Texts.forEach((item: any) => {\r\n item.R.forEach((t: any) => {\r\n // decode and append with space\r\n text += decodeURIComponent(t.T) + \" \";\r\n });\r\n });\r\n text += \"\\n\";\r\n });\r\n resolve(text);\r\n } catch (e) {\r\n // Fallback\r\n resolve(pdfParser.getRawTextContent());\r\n }\r\n });\r\n pdfParser.parseBuffer(buffer);\r\n });\r\n } catch (pdfError: any) {\r\n console.error(\"PDF Parsing failed:\", pdfError);\r\n return { success: false, error: `Failed to parse PDF: ${pdfError.message}` };\r\n }\r\n }\r\n else if (file.type === \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\") {\r\n try {\r\n const result = await mammoth.extractRawText({ buffer });\r\n extractedText = result.value;\r\n } catch (wordError) {\r\n console.error(\"Word Parsing failed:\", wordError);\r\n return { success: false, error: \"Failed to parse Word document.\" };\r\n }\r\n }\r\n else if (file.type.startsWith(\"image/\")) {\r\n try {\r\n const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY! });\r\n\r\n const prompt = \"Extract all text from this job description image. Maintain the structure and key details. Output only the extracted text.\";\r\n\r\n const response = await ai.models.generateContent({\r\n model: \"gemini-3-flash-preview\",\r\n contents: [{\r\n role: \"user\",\r\n parts: [\r\n { text: prompt },\r\n {\r\n inlineData: {\r\n data: buffer.toString(\"base64\"),\r\n mimeType: file.type,\r\n }\r\n }\r\n ]\r\n }]\r\n });\r\n\r\n extractedText = response.text || \"\";\r\n } catch (ocrError) {\r\n console.error(\"OCR failed:\", ocrError);\r\n return { success: false, error: \"Failed to extract text from image.\" };\r\n }\r\n }\r\n else {\r\n return { success: false, error: \"Unsupported file type. Please upload PDF, Word, or Image.\" };\r\n }\r\n\r\n return { success: true, text: extractedText.trim() };\r\n\r\n } catch (error: any) {\r\n console.error(\"Extraction Error:\", error);\r\n return { success: false, error: error.message || \"Failed to extract text.\" };\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\actions\\optimize.ts","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":96,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":96,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[4160,4163],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[4160,4163],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use server';\r\n\r\nimport { GoogleGenAI } from \"@google/genai\";\r\n\r\nexport async function optimizeResume(resumeText: string, jobDescription: string) {\r\n const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY! });\r\n\r\n const prompt = `\r\n You are an expert Resume Writer and ATS Optimizer. \r\n Your goal is to rewrite the candidate's resume to achieve a 100% Match Score against the provided Job Description.\r\n\r\n JOB DESCRIPTION:\r\n ${jobDescription}\r\n\r\n CURRENT RESUME:\r\n ${resumeText}\r\n\r\n INSTRUCTIONS:\r\n 1. Analyze the Job Description for key hard skills, soft skills, and responsibilities.\r\n 2. Rewrite the \"Professional Summary\" to immediately hook the recruiter and include top keywords.\r\n 3. Rewrite \"Experience\" bullet points:\r\n - Use strong action verbs.\r\n - Quantify results where possible (e.g., \"Increased X by Y%\").\r\n - Naturally weave in the missing keywords from the JD.\r\n - Keep the *truth* of the candidate's experience, but frame it to match the JD.\r\n 4. Update \"Skills\" to include all relevant technical and soft skills found in the JD that the candidate reasonably possesses based on their resume.\r\n 5. Maintain a professional, clean tone.\r\n\r\n OUTPUT FORMAT:\r\n Return a single valid JSON object with this exact structure (no markdown):\r\n {\r\n \"fullName\": \"Full Name in proper case\",\r\n \"contactInfo\": {\r\n \"email\": \"email@example.com\",\r\n \"phone\": \"+1 (xxx) xxx-xxxx\",\r\n \"linkedin\": \"linkedin.com/in/username\",\r\n \"website\": \"portfolio-url.com\",\r\n \"location\": \"City, State\"\r\n },\r\n \"professionalSummary\": \"A compelling 3-4 sentence summary that immediately hooks the reader, highlights key achievements with numbers, and includes top keywords from the JD. Make it impactful and specific.\",\r\n \"skills\": [\"Include 15-20 relevant skills from the JD that the candidate has\", \"Technical skills first\", \"Then soft skills\"],\r\n \"experience\": [\r\n {\r\n \"company\": \"Company Name\",\r\n \"role\": \"Job Title\",\r\n \"date\": \"Month Year - Month Year\",\r\n \"location\": \"City, State\",\r\n \"description\": [\"Write 4-5 strong bullet points per role\", \"Each bullet should start with a strong action verb\", \"Include metrics and quantified results (%, $, numbers)\", \"Naturally integrate missing keywords from the JD\", \"Focus on impact and achievements, not just duties\"]\r\n }\r\n ],\r\n \"education\": [\r\n {\r\n \"institution\": \"University or School Name\",\r\n \"degree\": \"Degree Type and Major\",\r\n \"date\": \"Graduation Year or Date Range\",\r\n \"gpa\": \"GPA if above 3.5 (optional)\"\r\n }\r\n ],\r\n \"projects\": [\r\n {\r\n \"name\": \"Project Name\",\r\n \"description\": \"2-3 sentence description highlighting the problem solved and impact\",\r\n \"techStack\": [\"Relevant\", \"Technologies\", \"Used\"],\r\n \"link\": \"project-url.com (if available)\"\r\n }\r\n ],\r\n \"certifications\": [\"List any relevant certifications\", \"AWS Certified\", \"Google Cloud\", \"etc.\"],\r\n \"languages\": [\"English (Native)\", \"Spanish (Conversational)\", \"etc.\"],\r\n \"achievements\": [\"Include 2-3 key achievements or awards\", \"Dean's List, Hackathon Winner, etc.\"]\r\n }\r\n\r\n IMPORTANT INSTRUCTIONS:\r\n - Generate at least 4-5 bullet points per experience entry\r\n - Include ALL relevant skills from the JD (aim for 15-20)\r\n - Make the professional summary compelling and keyword-rich\r\n - Add certifications if mentioned or relevant to the JD\r\n - Add languages if mentioned in original resume\r\n - Add achievements to fill any gaps\r\n - Every bullet point should be impactful with metrics where possible\r\n `;\r\n\r\n try {\r\n const response = await ai.models.generateContent({\r\n model: \"gemini-3-flash-preview\",\r\n contents: prompt,\r\n config: {\r\n responseMimeType: \"application/json\",\r\n }\r\n });\r\n\r\n const text = response.text;\r\n if (!text) throw new Error(\"No response from AI\");\r\n\r\n return { success: true, data: JSON.parse(text) };\r\n\r\n } catch (error: any) {\r\n console.error(\"Optimization Error:\", error);\r\n return { success: false, error: error.message || \"Failed to optimize resume.\" };\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\actions\\resume.ts","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":12,"column":14,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":12,"endColumn":17,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[283,286],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[283,286],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use server';\r\n\r\nimport { prisma } from \"@/lib/prisma\";\r\nimport { revalidatePath } from \"next/cache\";\r\n\r\nexport async function saveResume(data: {\r\n userId: string;\r\n fileName: string;\r\n jobTitle: string;\r\n matchScore: number;\r\n fileType: \"PDF\" | \"DOCX\";\r\n content: any; // The optimization JSON\r\n}) {\r\n try {\r\n await prisma.resume.create({\r\n data: {\r\n userId: data.userId,\r\n fileName: data.fileName,\r\n jobTitle: data.jobTitle,\r\n matchScore: data.matchScore,\r\n fileType: data.fileType,\r\n fileUrl: \"\", // We can generate on fly from content for local dev\r\n content: JSON.stringify(data.content),\r\n }\r\n });\r\n\r\n revalidatePath('/dashboard');\r\n return { success: true };\r\n } catch (error) {\r\n console.error(\"Failed to save resume:\", error);\r\n return { success: false, error: \"Failed to save resume to database.\" };\r\n }\r\n}\r\n\r\nexport async function getUserResumes(userId: string) {\r\n try {\r\n const resumes = await prisma.resume.findMany({\r\n where: { userId },\r\n orderBy: { createdAt: 'desc' }\r\n });\r\n return { success: true, data: resumes };\r\n } catch (error) {\r\n console.error(\"Failed to fetch resumes:\", error);\r\n return { success: false, error: \"Failed to fetch resumes.\" };\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\actions\\sarvam.ts","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":24,"column":21,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":24,"endColumn":24,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[648,651],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[648,651],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":34,"column":34,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":34,"endColumn":37,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1158,1161],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1158,1161],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":43,"column":21,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":43,"endColumn":24,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1408,1411],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1408,1411],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use server';\r\n\r\nimport { SarvamAIClient } from 'sarvamai';\r\n\r\nconst client = new SarvamAIClient({\r\n apiSubscriptionKey: process.env.SARVAM_API_KEY as string,\r\n});\r\n\r\nexport async function transcribeAudio(formData: FormData) {\r\n try {\r\n const file = formData.get('audio') as File;\r\n if (!file) {\r\n throw new Error('No audio file provided');\r\n }\r\n\r\n const response = await client.speechToText.transcribe({\r\n file: file,\r\n model: \"saarika:v2.5\",\r\n language_code: \"en-IN\",\r\n });\r\n\r\n return { success: true, text: response.transcript };\r\n\r\n } catch (error: any) {\r\n console.error('Transcription error:', error);\r\n return { success: false, error: error.message || 'Transcription failed' };\r\n }\r\n}\r\n\r\nexport async function generateCoachResponse(history: { role: 'user' | 'assistant' | 'system', content: string }[]) {\r\n try {\r\n // casting history to any to bypass potential SDK type mismatches if they are strict about 'ChatCompletionRequestMessage'\r\n const response = await client.chat.completions({\r\n messages: history as any,\r\n // model property removed as it caused build error and is not in JS SDK types\r\n });\r\n\r\n return {\r\n success: true,\r\n content: response.choices[0].message.content\r\n };\r\n\r\n } catch (error: any) {\r\n console.error('Chat error:', error);\r\n return { success: false, error: error.message || 'Chat generation failed' };\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\api\\analyze\\route.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'fs' is defined but never used.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":10,"suggestions":[{"messageId":"removeUnusedImportDeclaration","data":{"varName":"fs"},"fix":{"range":[147,167],"text":""},"desc":"Remove unused import declaration."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'path' is defined but never used.","line":5,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":5,"endColumn":12,"suggestions":[{"messageId":"removeUnusedImportDeclaration","data":{"varName":"path"},"fix":{"range":[169,193],"text":""},"desc":"Remove unused import declaration."}]},{"ruleId":"@typescript-eslint/no-require-imports","severity":2,"message":"A `require()` style import is forbidden.","line":26,"column":35,"nodeType":"CallExpression","messageId":"noRequireImports","endLine":26,"endColumn":54},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":30,"column":67,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":30,"endColumn":70,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1100,1103],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1100,1103],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":31,"column":67,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":31,"endColumn":70,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1205,1208],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1205,1208],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":35,"column":68,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":35,"endColumn":71,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1435,1438],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1435,1438],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":36,"column":59,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":36,"endColumn":62,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1504,1507],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1504,1507],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":37,"column":56,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":37,"endColumn":59,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1570,1573],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1570,1573],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'e' is defined but never used.","line":45,"column":34,"nodeType":"Identifier","messageId":"unusedVar","endLine":45,"endColumn":35},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":53,"column":32,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":53,"endColumn":35,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[2243,2246],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[2243,2246],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":68,"column":21,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":68,"endColumn":24,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[3014,3017],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[3014,3017],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":10,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { NextRequest, NextResponse } from \"next/server\";\r\nimport mammoth from \"mammoth\";\r\nimport { analyzeResumeWithGemini } from \"@/lib/gemini\";\r\nimport fs from 'fs';\r\nimport path from 'path';\r\n\r\n// Force dynamic to prevent ANY build-time evaluation\r\nexport const dynamic = 'force-dynamic';\r\nexport const runtime = 'nodejs';\r\n\r\nexport async function POST(req: NextRequest) {\r\n try {\r\n const formData = await req.formData();\r\n const file = formData.get(\"resume\") as File;\r\n const jobDescription = formData.get(\"jobDescription\") as string;\r\n\r\n if (!file) {\r\n return NextResponse.json({ error: \"Missing file\" }, { status: 400 });\r\n }\r\n\r\n const buffer = Buffer.from(await file.arrayBuffer());\r\n let resumeText = \"\";\r\n\r\n if (file.type === \"application/pdf\") {\r\n try {\r\n const PDFParser = require(\"pdf2json\");\r\n const pdfParser = new PDFParser(null, 1);\r\n\r\n resumeText = await new Promise((resolve, reject) => {\r\n pdfParser.on(\"pdfParser_dataError\", (errData: any) => reject(errData.parserError));\r\n pdfParser.on(\"pdfParser_dataReady\", (pdfData: any) => {\r\n try {\r\n // Custom text extraction to fix spacing issues\r\n let text = \"\";\r\n pdfData.formImage.Pages.forEach((page: any) => {\r\n page.Texts.forEach((item: any) => {\r\n item.R.forEach((t: any) => {\r\n // decode and append with space\r\n text += decodeURIComponent(t.T) + \" \";\r\n });\r\n });\r\n text += \"\\n\";\r\n });\r\n resolve(text);\r\n } catch (e) {\r\n // Fallback\r\n resolve(pdfParser.getRawTextContent());\r\n }\r\n });\r\n\r\n pdfParser.parseBuffer(buffer);\r\n });\r\n } catch (pdfError: any) {\r\n console.error(\"PDF Parsing failed:\", pdfError);\r\n return NextResponse.json({ error: `Failed to parse PDF: ${pdfError.message}` }, { status: 500 });\r\n }\r\n } else if (file.type === \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\") {\r\n const result = await mammoth.extractRawText({ buffer });\r\n resumeText = result.value;\r\n } else {\r\n return NextResponse.json({ error: \"Unsupported file type\" }, { status: 400 });\r\n }\r\n\r\n const analysisResults = await analyzeResumeWithGemini(resumeText, jobDescription);\r\n\r\n return NextResponse.json({ ...analysisResults, resumeText }); // Return raw text for future re-use\r\n\r\n } catch (error: any) {\r\n console.error(\"Analysis Error:\", error);\r\n return NextResponse.json({ error: error.message || \"Internal Server Error\" }, { status: 500 });\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\api\\resumes\\[id]\\route.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\dashboard\\loading.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\dashboard\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'redirect' is defined but never used.","line":4,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":18,"suggestions":[{"messageId":"removeUnusedImportDeclaration","data":{"varName":"redirect"},"fix":{"range":[147,190],"text":""},"desc":"Remove unused import declaration."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":8,"column":24,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":8,"endColumn":27,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[304,307],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[304,307],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { auth } from \"@clerk/nextjs/server\";\r\nimport { prisma } from \"@/lib/prisma\";\r\nimport DashboardClient from \"@/components/DashboardClient\";\r\nimport { redirect } from \"next/navigation\";\r\n\r\nexport default async function DashboardPage() {\r\n const { userId } = await auth();\r\n let serverHistory: any[] = [];\r\n\r\n if (userId) {\r\n try {\r\n serverHistory = await prisma.resume.findMany({\r\n where: { userId },\r\n orderBy: { createdAt: 'desc' },\r\n select: {\r\n id: true,\r\n fileName: true,\r\n jobTitle: true,\r\n matchScore: true,\r\n createdAt: true,\r\n fileType: true,\r\n content: true // Needed for re-generating docs\r\n }\r\n });\r\n } catch (error) {\r\n console.error(\"Failed to fetch resumes:\", error);\r\n }\r\n } else {\r\n // Optional: Redirect to login or just show local history\r\n // redirect('/sign-in'); \r\n }\r\n\r\n return <DashboardClient serverHistory={serverHistory} />;\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\not-found.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'ArrowLeft' is defined but never used.","line":6,"column":16,"nodeType":"Identifier","messageId":"unusedVar","endLine":6,"endColumn":25,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"ArrowLeft"},"fix":{"range":[152,163],"text":""},"desc":"Remove unused variable \"ArrowLeft\"."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":41,"column":66,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[2155,2299],"text":"\r\n Even AI gets lost sometimes. The page you're searching for seems to have been archived or moved.\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[2155,2299],"text":"\r\n Even AI gets lost sometimes. The page you‘re searching for seems to have been archived or moved.\r\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[2155,2299],"text":"\r\n Even AI gets lost sometimes. The page you're searching for seems to have been archived or moved.\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[2155,2299],"text":"\r\n Even AI gets lost sometimes. The page you’re searching for seems to have been archived or moved.\r\n "},"desc":"Replace with `’`."}]}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport Link from \"next/link\";\r\nimport { motion } from \"framer-motion\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Home, ArrowLeft, Search, FileX } from \"lucide-react\";\r\n\r\nexport default function NotFound() {\r\n return (\r\n <main className=\"min-h-[80vh] flex flex-col items-center justify-center px-6 relative overflow-hidden\">\r\n {/* Ambient Background Glow */}\r\n <div className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[500px] h-[500px] bg-teal-500/10 rounded-full blur-[120px] -z-10 animate-pulse\" />\r\n <div className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[300px] h-[300px] bg-purple-500/10 rounded-full blur-[100px] -z-10 animate-pulse delay-700\" />\r\n\r\n <div className=\"text-center space-y-8 max-w-2xl mx-auto\">\r\n <motion.div\r\n initial={{ opacity: 0, scale: 0.5 }}\r\n animate={{ opacity: 1, scale: 1 }}\r\n transition={{ duration: 0.5 }}\r\n className=\"relative inline-block\"\r\n >\r\n <div className=\"absolute inset-0 bg-teal-500/20 blur-3xl rounded-full\" />\r\n <div className=\"bg-slate-900/50 border border-slate-800 p-8 rounded-3xl backdrop-blur-xl relative z-10 shadow-2xl\">\r\n <FileX className=\"h-24 w-24 text-teal-400 mx-auto\" />\r\n </div>\r\n </motion.div>\r\n\r\n <motion.div\r\n initial={{ opacity: 0, y: 20 }}\r\n animate={{ opacity: 1, y: 0 }}\r\n transition={{ delay: 0.2 }}\r\n className=\"space-y-4\"\r\n >\r\n <h1 className=\"text-7xl md:text-9xl font-black tracking-tighter text-white font-display\">\r\n 404\r\n </h1>\r\n <h2 className=\"text-2xl md:text-3xl font-bold text-slate-200\">\r\n Resume Not Found (Or Page)\r\n </h2>\r\n <p className=\"text-slate-400 text-lg max-w-md mx-auto leading-relaxed\">\r\n Even AI gets lost sometimes. The page you're searching for seems to have been archived or moved.\r\n </p>\r\n </motion.div>\r\n\r\n <motion.div\r\n initial={{ opacity: 0, y: 20 }}\r\n animate={{ opacity: 1, y: 0 }}\r\n transition={{ delay: 0.4 }}\r\n className=\"flex flex-col md:flex-row items-center justify-center gap-4 pt-4\"\r\n >\r\n <Button asChild size=\"lg\" className=\"rounded-full px-8 bg-teal-600 hover:bg-teal-500 text-white shadow-lg shadow-teal-500/20 w-full md:w-auto transition-all hover:scale-105\">\r\n <Link href=\"/\">\r\n <Home className=\"mr-2 h-4 w-4\" /> Back Home\r\n </Link>\r\n </Button>\r\n <Button asChild variant=\"outline\" size=\"lg\" className=\"rounded-full px-8 border-slate-700 text-slate-400 hover:bg-slate-800 w-full md:w-auto transition-all hover:scale-105\">\r\n <Link href=\"/dashboard\">\r\n <Search className=\"mr-2 h-4 w-4\" /> Go to Library\r\n </Link>\r\n </Button>\r\n </motion.div>\r\n\r\n <motion.div\r\n initial={{ opacity: 0 }}\r\n animate={{ opacity: 1 }}\r\n transition={{ delay: 0.6 }}\r\n className=\"pt-12\"\r\n >\r\n <div className=\"inline-flex items-center gap-2 px-4 py-2 bg-slate-900/40 border border-slate-800 rounded-full text-sm text-slate-500 italic\">\r\n <span className=\"w-2 h-2 rounded-full bg-teal-500 animate-pulse\" />\r\n AI is currently re-indexing the universe...\r\n </div>\r\n </motion.div>\r\n </div>\r\n </main>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\optimize\\loading.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\optimize\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'useRef' is defined but never used.","line":3,"column":20,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":26,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"useRef"},"fix":{"range":[34,42],"text":""},"desc":"Remove unused variable \"useRef\"."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'Input' is defined but never used.","line":7,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":7,"endColumn":15,"suggestions":[{"messageId":"removeUnusedImportDeclaration","data":{"varName":"Input"},"fix":{"range":[278,324],"text":""},"desc":"Remove unused import declaration."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'ScrollArea' is defined but never used.","line":10,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":10,"endColumn":20,"suggestions":[{"messageId":"removeUnusedImportDeclaration","data":{"varName":"ScrollArea"},"fix":{"range":[428,485],"text":""},"desc":"Remove unused import declaration."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'CheckCircle2' is defined but never used.","line":12,"column":42,"nodeType":"Identifier","messageId":"unusedVar","endLine":12,"endColumn":54,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"CheckCircle2"},"fix":{"range":[574,588],"text":""},"desc":"Remove unused variable \"CheckCircle2\"."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'Github' is defined but never used.","line":12,"column":94,"nodeType":"Identifier","messageId":"unusedVar","endLine":12,"endColumn":100,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"Github"},"fix":{"range":[626,634],"text":""},"desc":"Remove unused variable \"Github\"."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'Link' is defined but never used.","line":14,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":14,"endColumn":12,"suggestions":[{"messageId":"removeUnusedImportDeclaration","data":{"varName":"Link"},"fix":{"range":[692,721],"text":""},"desc":"Remove unused import declaration."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'LOADING_MESSAGES' is assigned a value but never used.","line":32,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":32,"endColumn":23},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":44,"column":40,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":44,"endColumn":43,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1714,1717],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1714,1717],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":88,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":88,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[2780,2783],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[2780,2783],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":369,"column":82,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":369,"endColumn":85,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[18670,18673],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[18670,18673],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":10,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useRef, useEffect } from 'react';\r\nimport { motion, AnimatePresence } from 'framer-motion';\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Card, CardContent, CardHeader, CardTitle, CardDescription } from \"@/components/ui/card\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { Label } from \"@/components/ui/label\";\r\nimport { Textarea } from \"@/components/ui/textarea\";\r\nimport { ScrollArea } from \"@/components/ui/scroll-area\";\r\nimport { Badge } from \"@/components/ui/badge\";\r\nimport { Loader2, UploadCloud, FileText, CheckCircle2, AlertTriangle, Lightbulb, ArrowRight, Github } from \"lucide-react\";\r\nimport Image from \"next/image\";\r\nimport Link from \"next/link\";\r\nimport InterviewCoach from \"@/components/InterviewCoach\";\r\nimport ResumeOptimizer from \"@/components/ResumeOptimizer\";\r\nimport LoadingText from \"@/components/LoadingText\";\r\n\r\nimport { Variants } from \"framer-motion\";\r\n\r\n// Animation Variants\r\nconst fadeInUp: Variants = {\r\n hidden: { opacity: 0, y: 30 },\r\n visible: { opacity: 1, y: 0, transition: { duration: 0.6, ease: \"easeOut\" } }\r\n};\r\n\r\nconst staggerContainer: Variants = {\r\n hidden: { opacity: 0 },\r\n visible: { opacity: 1, transition: { staggerChildren: 0.2 } }\r\n};\r\n\r\nconst LOADING_MESSAGES = [\r\n \"Scanning for ATS compatibility...\",\r\n \"Analyzing impact verbs...\",\r\n \"Detecting missing keywords...\",\r\n \"Comparing against top industry standards...\",\r\n \"Formatting structure...\"\r\n];\r\n\r\nexport default function Home() {\r\n const [file, setFile] = useState<File | null>(null);\r\n const [jobDescription, setJobDescription] = useState('');\r\n const [loading, setLoading] = useState(false);\r\n const [result, setResult] = useState<any>(null);\r\n const [error, setError] = useState('');\r\n const [isMounted, setIsMounted] = useState(false);\r\n\r\n useEffect(() => {\r\n setIsMounted(true);\r\n }, []);\r\n\r\n const clearResult = () => {\r\n setResult(null);\r\n window.scrollTo({ top: 0, behavior: 'smooth' });\r\n };\r\n\r\n const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {\r\n if (e.target.files && e.target.files[0]) {\r\n setFile(e.target.files[0]);\r\n }\r\n };\r\n\r\n const analyzeResume = async () => {\r\n if (!file) {\r\n setError('Please upload a resume to proceed.');\r\n return;\r\n }\r\n\r\n setLoading(true);\r\n setError('');\r\n\r\n const formData = new FormData();\r\n formData.append('resume', file);\r\n formData.append('jobDescription', jobDescription);\r\n\r\n try {\r\n const response = await fetch('/api/analyze', {\r\n method: 'POST',\r\n body: formData,\r\n });\r\n\r\n const data = await response.json();\r\n\r\n if (!response.ok) throw new Error(data.error || 'Analysis failed');\r\n\r\n setResult(data);\r\n\r\n } catch (err: any) {\r\n setError(err.message || 'Something went wrong.');\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n if (!isMounted) return null;\r\n\r\n return (\r\n <main className=\"min-h-screen bg-slate-950 text-slate-50 overflow-x-hidden selection:bg-teal-500/30\">\r\n\r\n {/* Background Ambience */}\r\n <div className=\"fixed inset-0 z-0 pointer-events-none\">\r\n <div className=\"absolute top-[-10%] left-[-10%] w-[600px] h-[600px] bg-teal-500/10 rounded-full blur-[120px]\" />\r\n <div className=\"absolute bottom-[-10%] right-[-10%] w-[600px] h-[600px] bg-cyan-500/10 rounded-full blur-[120px]\" />\r\n </div>\r\n\r\n <div className=\"max-w-6xl mx-auto px-4 md:px-8 relative z-10 pb-20\">\r\n\r\n {/* Hero Section */}\r\n <motion.section\r\n initial=\"hidden\"\r\n animate=\"visible\"\r\n variants={staggerContainer}\r\n className=\"text-center space-y-8 pt-10 pb-20 min-h-[80vh] flex flex-col justify-center items-center\"\r\n >\r\n <motion.div variants={fadeInUp} className=\"inline-block\">\r\n <Badge variant=\"secondary\" className=\"bg-teal-950/50 text-teal-300 border-teal-500/20 px-4 py-1.5 text-sm rounded-full backdrop-blur-md\">\r\n Γ£¿ AI-Powered Career Optimization\r\n </Badge>\r\n </motion.div>\r\n\r\n <motion.h1 variants={fadeInUp} className=\"text-5xl md:text-8xl font-bold tracking-tighter leading-none font-display text-white\">\r\n Land Your Dream <br />\r\n <span className=\"text-transparent bg-clip-text bg-gradient-to-r from-teal-400 via-cyan-400 to-emerald-400 text-glow\">\r\n Job Faster.\r\n </span>\r\n </motion.h1>\r\n\r\n <motion.p variants={fadeInUp} className=\"text-slate-400 text-lg md:text-xl max-w-2xl mx-auto leading-relaxed italic font-light\">\r\n Upload your resume and get instant, AI-driven feedback to beat the ATS and impress recruiters. Tailored interview prep included.\r\n </motion.p>\r\n\r\n <motion.div variants={fadeInUp} className=\"pt-4\">\r\n <Button\r\n onClick={() => document.getElementById('analysis-section')?.scrollIntoView({ behavior: 'smooth' })}\r\n className=\"rounded-full px-8 py-6 text-lg bg-white/10 hover:bg-white/20 text-white backdrop-blur-md border border-white/10 transition-all hover:scale-105 cursor-pointer\"\r\n >\r\n Start Free Analysis <ArrowRight className=\"ml-2 h-5 w-5\" />\r\n </Button>\r\n </motion.div>\r\n </motion.section>\r\n\r\n {/* Main Interface */}\r\n <div id=\"analysis-section\" className=\"scroll-mt-24\">\r\n <AnimatePresence mode=\"wait\">\r\n {!result ? (\r\n <motion.div\r\n key=\"upload\"\r\n initial={{ opacity: 0, scale: 0.95 }}\r\n animate={{ opacity: 1, scale: 1 }}\r\n exit={{ opacity: 0, scale: 0.95, transition: { duration: 0.2 } }}\r\n transition={{ duration: 0.5 }}\r\n className=\"space-y-24\"\r\n >\r\n {/* Upload Card */}\r\n <Card className=\"glass-card border-slate-800 bg-slate-900/60 backdrop-blur-2xl max-w-4xl mx-auto overflow-hidden\">\r\n {/* Progress Bar Loader */}\r\n {loading && <motion.div layoutId=\"loader\" className=\"h-1 bg-gradient-to-r from-teal-500 to-cyan-500 absolute top-0 left-0 w-full\" initial={{ width: 0 }} animate={{ width: \"100%\" }} transition={{ duration: 2, repeat: Infinity }} />}\r\n\r\n <CardHeader className=\"text-center pb-10 pt-10\">\r\n <CardTitle className=\"text-3xl font-display\">Start Analysis</CardTitle>\r\n <CardDescription className=\"text-slate-400 text-lg italic\">Process your resume in seconds.</CardDescription>\r\n </CardHeader>\r\n <CardContent className=\"space-y-8 px-8 pb-10\">\r\n <div className=\"grid md:grid-cols-2 gap-8\">\r\n {/* Dropzone */}\r\n <div className=\"space-y-3 group\">\r\n <Label className=\"text-base font-medium text-slate-300\">Resume File <span className=\"text-teal-400\">*</span></Label>\r\n <div className=\"border-2 border-dashed border-slate-700/50 rounded-2xl p-8 text-center bg-slate-900/40 hover:bg-slate-800/60 hover:border-teal-500/50 transition-all cursor-pointer relative h-[220px] flex flex-col items-center justify-center group-hover:shadow-[0_0_20px_rgba(20,184,166,0.1)]\">\r\n <input type=\"file\" accept=\".pdf,.docx\" onChange={handleFileChange} className=\"absolute inset-0 w-full h-full opacity-0 cursor-pointer z-50\" />\r\n <div className=\"flex flex-col items-center gap-4 transition-transform group-hover:-translate-y-1\">\r\n {file ? (\r\n <>\r\n <div className=\"bg-teal-500/20 p-4 rounded-full ring-1 ring-teal-500/50\">\r\n <FileText className=\"h-8 w-8 text-teal-400\" />\r\n </div>\r\n <div className=\"text-center\">\r\n <p className=\"font-medium text-slate-200 text-lg\">{file.name}</p>\r\n <p className=\"text-sm text-slate-500\">{(file.size / 1024 / 1024).toFixed(2)} MB</p>\r\n </div>\r\n </>\r\n ) : (\r\n <>\r\n <div className=\"bg-slate-800 p-4 rounded-full group-hover:bg-slate-700 transition-colors\">\r\n <UploadCloud className=\"h-8 w-8 text-slate-400 group-hover:text-teal-400 transition-colors\" />\r\n </div>\r\n <div>\r\n <p className=\"font-medium text-slate-300 text-lg\">Click to Upload</p>\r\n <p className=\"text-sm text-slate-500 mt-1\">PDF or DOCX</p>\r\n </div>\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* Job Desc */}\r\n <div className=\"space-y-3\">\r\n <div className=\"flex justify-between items-center\">\r\n <Label className=\"text-base font-medium text-slate-300\">Job Description</Label>\r\n <Badge variant=\"outline\" className=\"text-[10px] text-slate-500 border-slate-800 uppercase tracking-wider\">Optional</Badge>\r\n </div>\r\n <Textarea\r\n placeholder=\"Paste the job description here...\"\r\n className=\"h-[220px] bg-slate-900/40 border-slate-700/50 resize-none focus:ring-teal-500/50 focus:border-teal-500/50 text-slate-300 leading-relaxed rounded-2xl\"\r\n value={jobDescription}\r\n onChange={(e) => setJobDescription(e.target.value)}\r\n />\r\n </div>\r\n </div>\r\n\r\n {error && (\r\n <motion.div initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }} className=\"flex items-center gap-3 text-red-400 bg-red-950/20 border border-red-900/50 p-4 rounded-xl\">\r\n <AlertTriangle className=\"h-5 w-5 shrink-0\" />\r\n <span className=\"text-sm font-medium\">{error}</span>\r\n </motion.div>\r\n )}\r\n\r\n <Button\r\n onClick={analyzeResume}\r\n className=\"w-full bg-gradient-to-r from-teal-600 to-cyan-600 hover:from-teal-500 hover:to-cyan-500 text-white font-bold py-8 text-xl shadow-[0_0_30px_rgba(20,184,166,0.3)] hover:shadow-[0_0_40px_rgba(20,184,166,0.5)] transition-all rounded-xl relative overflow-hidden group cursor-pointer\"\r\n disabled={loading}\r\n >\r\n <span className=\"relative z-10 flex items-center justify-center gap-2 w-full\">\r\n {loading ? (\r\n <div className=\"flex flex-col items-center gap-2\">\r\n <div className=\"flex items-center gap-2\">\r\n <Loader2 className=\"animate-spin h-5 w-5\" />\r\n <span>Analyzing...</span>\r\n </div>\r\n <LoadingText\r\n messages={[\r\n \"Scanning for ATS compatibility...\",\r\n \"Analyzing impact verbs...\",\r\n \"Detecting missing keywords...\",\r\n \"Comparing against top industry standards...\",\r\n \"Formatting structure...\"\r\n ]}\r\n className=\"text-sm font-normal text-teal-100/80\"\r\n />\r\n </div>\r\n ) : (\r\n <>Run Analysis <ArrowRight className=\"group-hover:translate-x-1 transition-transform\" /></>\r\n )}\r\n </span>\r\n </Button>\r\n </CardContent>\r\n </Card>\r\n\r\n {/* How It Works Section */}\r\n <section className=\"space-y-16 py-10\">\r\n <motion.div\r\n initial={{ opacity: 0, y: 50 }}\r\n whileInView={{ opacity: 1, y: 0 }}\r\n viewport={{ once: true, margin: \"-100px\" }}\r\n transition={{ duration: 0.8 }}\r\n className=\"text-center\"\r\n >\r\n <h2 className=\"text-3xl md:text-5xl font-bold font-display mb-4\">How <span className=\"text-teal-400\">careerzen</span> Works</h2>\r\n <p className=\"text-slate-400 max-w-xl mx-auto italic text-lg\">Three simple steps to resume perfection.</p>\r\n </motion.div>\r\n\r\n <div className=\"grid md:grid-cols-3 gap-12\">\r\n {[\r\n { title: \"Upload\", desc: \"Drag & drop your resume securely.\", img: \"/assets/upload_mockup.png\" },\r\n { title: \"Analyze\", desc: \"Our AI scans for 50+ ATS checkpoints.\", img: \"/assets/analysis_mockup.png\" },\r\n { title: \"Improve\", desc: \"Get actionable tips & interview prep.\", img: \"/assets/results_mockup.png\" }\r\n ].map((step, i) => (\r\n <motion.div\r\n key={i}\r\n initial={{ opacity: 0, y: 80 }}\r\n whileInView={{ opacity: 1, y: 0 }}\r\n viewport={{ once: true, margin: \"-50px\" }}\r\n transition={{ delay: i * 0.2, duration: 0.8, ease: \"easeOut\" }}\r\n className=\"space-y-6 text-center group\"\r\n >\r\n <div className=\"relative h-[250px] w-full rounded-3xl overflow-hidden glass-card border-none shadow-2xl group-hover:scale-105 transition-transform duration-500\">\r\n <Image src={step.img} alt={step.title} fill className=\"object-cover\" />\r\n <div className=\"absolute inset-0 bg-gradient-to-t from-slate-950 via-transparent to-transparent opacity-80\" />\r\n </div>\r\n <div>\r\n <h3 className=\"text-2xl font-bold mb-2 font-display\">{step.title}</h3>\r\n <p className=\"text-slate-400 leading-relaxed italic\">{step.desc}</p>\r\n </div>\r\n </motion.div>\r\n ))}\r\n </div>\r\n </section>\r\n </motion.div>\r\n ) : (\r\n <motion.div\r\n key=\"results\"\r\n variants={staggerContainer}\r\n initial=\"hidden\"\r\n animate=\"visible\"\r\n className=\"space-y-8\"\r\n >\r\n <motion.div variants={fadeInUp} className=\"flex flex-col md:flex-row items-center justify-between gap-4\">\r\n <div>\r\n <h2 className=\"text-4xl font-bold font-display\">Analysis Report</h2>\r\n <p className=\"text-slate-400\">Detailed insights for your profile</p>\r\n </div>\r\n <Button onClick={clearResult} variant=\"outline\" className=\"border-slate-800 bg-slate-900/50 hover:bg-slate-800 text-slate-300 cursor-pointer\">\r\n Upload New Resume\r\n </Button>\r\n </motion.div>\r\n\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4\">\r\n <ScoreCard title=\"Match Score\" score={result.matchPercentage} color=\"teal\" />\r\n <ScoreCard title=\"ATS Score\" score={result.atsScore} color=\"cyan\" />\r\n </div>\r\n\r\n {/* AI Resume Optimizer - Only show if score < 100 and we have the text */}\r\n {(result.matchPercentage < 100 || result.atsScore < 100) && result.resumeText && (\r\n <motion.div variants={fadeInUp} className=\"flex justify-center w-full py-6\">\r\n <ResumeOptimizer\r\n resumeText={result.resumeText}\r\n jobDescription={jobDescription || \"Standard role based on resume content.\"}\r\n originalFileName={file?.name || \"MyResume\"}\r\n />\r\n </motion.div>\r\n )}\r\n\r\n <motion.div variants={fadeInUp}>\r\n <Card className=\"glass-card border-teal-500/10\">\r\n <CardHeader><CardTitle>Candidate Summary</CardTitle></CardHeader>\r\n <CardContent><p className=\"text-slate-300 text-lg leading-relaxed\">{result.candidateSummary}</p></CardContent>\r\n </Card>\r\n </motion.div>\r\n\r\n <div className=\"grid md:grid-cols-2 gap-6\">\r\n <FeedbackCard title=\"Missing Keywords\" icon={AlertTriangle} items={result.missingKeywords} type=\"error\" />\r\n <FeedbackCard title=\"Improvements\" icon={Lightbulb} items={result.improvements} type=\"warning\" />\r\n </div>\r\n\r\n {result.interviewQuestions && (\r\n\r\n <motion.div variants={fadeInUp} className=\"w-full\">\r\n <InterviewCoach questions={result.interviewQuestions} />\r\n </motion.div>\r\n\r\n )}\r\n </motion.div>\r\n )}\r\n </AnimatePresence>\r\n </div>\r\n\r\n </div>\r\n\r\n <div className=\"pt-20\" />\r\n </main>\r\n );\r\n}\r\n\r\n// Sub-components for cleaner code\r\nfunction ScoreCard({ title, score, color }: { title: string, score: number, color: 'teal' | 'cyan' }) {\r\n return (\r\n <motion.div variants={fadeInUp}>\r\n <Card className={`glass-card border-none bg-${color}-500/5 overflow-hidden relative`}>\r\n <div className={`absolute top-0 right-0 w-20 h-20 bg-${color}-500/10 rounded-full blur-2xl -mr-10 -mt-10`} />\r\n <CardHeader className=\"pb-2\"><CardTitle className={`text-sm font-medium text-${color}-300 uppercase tracking-widest`}>{title}</CardTitle></CardHeader>\r\n <CardContent>\r\n <div className={`text-6xl font-bold text-${color}-400 font-display`}>{score}<span className=\"text-2xl text-slate-600\">%</span></div>\r\n </CardContent>\r\n </Card>\r\n </motion.div>\r\n );\r\n}\r\n\r\nfunction FeedbackCard({ title, icon: Icon, items, type }: { title: string, icon: any, items: string[], type: 'error' | 'warning' }) {\r\n const colorClass = type === 'error' ? 'red' : 'yellow';\r\n return (\r\n <motion.div variants={fadeInUp}>\r\n <Card className={`glass-card border-l-4 border-l-${colorClass}-500`}>\r\n <CardHeader>\r\n <CardTitle className={`flex items-center gap-2 text-${colorClass}-400`}>\r\n <Icon className=\"h-5 w-5\" /> {title}\r\n </CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n {items?.length > 0 ? (\r\n <div className=\"flex flex-wrap gap-2\">\r\n {items.map((item, i) => (\r\n type === 'error'\r\n ? <Badge key={i} variant=\"secondary\" className=\"bg-red-950/40 text-red-300 border-red-900/50\">{item}</Badge>\r\n : <div key={i} className=\"flex gap-2 text-slate-300 w-full\"><span className=\"text-yellow-500\">ΓÇó</span>{item}</div>\r\n ))}\r\n </div>\r\n ) : <p className=\"text-slate-500 italic\">None found.</p>}\r\n </CardContent>\r\n </Card>\r\n </motion.div>\r\n )\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\resume\\[id]\\loading.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\app\\resume\\[id]\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\AdvancedSearch.tsx","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":10,"column":14,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":10,"endColumn":17,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[389,392],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[389,392],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":188,"column":116,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":188,"endColumn":119,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[8950,8953],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[8950,8953],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":212,"column":117,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":212,"endColumn":120,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[10686,10689],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[10686,10689],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":236,"column":113,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":236,"endColumn":116,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[12408,12411],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[12408,12411],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect, useRef } from 'react';\r\nimport { motion, AnimatePresence } from 'framer-motion';\r\nimport { Search, Calendar, Award, Filter, X, Clock, TrendingUp, ChevronDown } from 'lucide-react';\r\nimport { Input } from '@/components/ui/input';\r\n\r\ninterface AdvancedSearchProps {\r\n onSearch: (query: string, filters: SearchFilters) => void;\r\n history: any[];\r\n}\r\n\r\nexport interface SearchFilters {\r\n dateRange: 'all' | 'today' | 'yesterday' | 'week' | 'month' | 'threeMonths' | 'year' | 'lastYear';\r\n scoreRange: 'all' | 'perfect' | 'high' | 'medium';\r\n sortBy: 'date' | 'score' | 'name';\r\n}\r\n\r\nconst dateRangeOptions = [\r\n { value: 'all', label: 'All Time', icon: Calendar },\r\n { value: 'today', label: 'Today', icon: Clock },\r\n { value: 'yesterday', label: 'Yesterday', icon: Clock },\r\n { value: 'week', label: 'This Week', icon: Calendar },\r\n { value: 'month', label: 'Last 30 Days', icon: Calendar },\r\n { value: 'threeMonths', label: 'Last 3 Months', icon: Calendar },\r\n { value: 'year', label: 'This Year', icon: Calendar },\r\n { value: 'lastYear', label: 'Last Year', icon: Calendar },\r\n];\r\n\r\nconst scoreRangeOptions = [\r\n { value: 'all', label: 'All Scores', icon: Award },\r\n { value: 'perfect', label: '100% Match', icon: TrendingUp },\r\n { value: 'high', label: '80%+ Match', icon: Award },\r\n { value: 'medium', label: '50-79%', icon: Award },\r\n];\r\n\r\nconst sortOptions = [\r\n { value: 'date', label: 'Newest First' },\r\n { value: 'score', label: 'Highest Score' },\r\n { value: 'name', label: 'Name A-Z' },\r\n];\r\n\r\nexport default function AdvancedSearch({ onSearch, history }: AdvancedSearchProps) {\r\n const [query, setQuery] = useState('');\r\n const [showFilters, setShowFilters] = useState(false);\r\n const [showSuggestions, setShowSuggestions] = useState(false);\r\n const [filters, setFilters] = useState<SearchFilters>({\r\n dateRange: 'all',\r\n scoreRange: 'all',\r\n sortBy: 'date',\r\n });\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n\r\n // Get unique suggestions from history\r\n const suggestions = Array.from(new Set(\r\n history\r\n .filter(item =>\r\n item.fileName?.toLowerCase().includes(query.toLowerCase()) ||\r\n item.jobTitle?.toLowerCase().includes(query.toLowerCase())\r\n )\r\n .map(item => item.fileName)\r\n )).slice(0, 5);\r\n\r\n useEffect(() => {\r\n const debounce = setTimeout(() => {\r\n onSearch(query, filters);\r\n }, 300);\r\n return () => clearTimeout(debounce);\r\n }, [query, filters, onSearch]);\r\n\r\n // Close suggestions when clicking outside\r\n useEffect(() => {\r\n const handleClickOutside = (e: MouseEvent) => {\r\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\r\n setShowSuggestions(false);\r\n }\r\n };\r\n document.addEventListener('mousedown', handleClickOutside);\r\n return () => document.removeEventListener('mousedown', handleClickOutside);\r\n }, []);\r\n\r\n const activeFiltersCount = [\r\n filters.dateRange !== 'all',\r\n filters.scoreRange !== 'all',\r\n ].filter(Boolean).length;\r\n\r\n return (\r\n <div ref={containerRef} className=\"relative w-full space-y-3\">\r\n {/* Search Input with Suggestions */}\r\n <div className=\"relative\">\r\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-500\" />\r\n <Input\r\n ref={inputRef}\r\n placeholder=\"Search by name, role, or date...\"\r\n className=\"pl-10 pr-20 bg-slate-900 border-slate-700 text-slate-200 \r\n focus:ring-teal-500/50 focus:border-teal-500/50 h-12\"\r\n value={query}\r\n onChange={(e) => setQuery(e.target.value)}\r\n onFocus={() => setShowSuggestions(true)}\r\n />\r\n\r\n {/* Filter Toggle Button */}\r\n <button\r\n onClick={() => setShowFilters(!showFilters)}\r\n className={`absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1 px-3 py-1.5 \r\n rounded-lg text-xs font-medium transition-all\r\n ${showFilters || activeFiltersCount > 0\r\n ? 'bg-teal-500/20 text-teal-400 border border-teal-500/30'\r\n : 'bg-slate-800 text-slate-400 hover:bg-slate-700'\r\n } cursor-pointer`}\r\n >\r\n <Filter className=\"h-3.5 w-3.5\" />\r\n Filters\r\n {activeFiltersCount > 0 && (\r\n <span className=\"ml-1 bg-teal-500 text-slate-900 px-1.5 py-0.5 rounded-full text-[10px] font-bold\">\r\n {activeFiltersCount}\r\n </span>\r\n )}\r\n <ChevronDown className={`h-3 w-3 transition-transform ${showFilters ? 'rotate-180' : ''}`} />\r\n </button>\r\n\r\n {/* Clear Button */}\r\n {query && (\r\n <button\r\n onClick={() => setQuery('')}\r\n className=\"absolute right-24 top-1/2 -translate-y-1/2 p-1 hover:bg-slate-800 rounded cursor-pointer\"\r\n >\r\n <X className=\"h-4 w-4 text-slate-500\" />\r\n </button>\r\n )}\r\n\r\n {/* Search Suggestions Dropdown */}\r\n <AnimatePresence>\r\n {showSuggestions && query && suggestions.length > 0 && (\r\n <motion.div\r\n initial={{ opacity: 0, y: -10 }}\r\n animate={{ opacity: 1, y: 0 }}\r\n exit={{ opacity: 0, y: -10 }}\r\n className=\"absolute top-full left-0 right-0 mt-2 bg-slate-900 border border-slate-700 \r\n rounded-xl shadow-xl z-50 overflow-hidden\"\r\n >\r\n <div className=\"p-2 border-b border-slate-800\">\r\n <span className=\"text-xs text-slate-500 font-medium\">Suggestions</span>\r\n </div>\r\n {suggestions.map((suggestion, i) => (\r\n <motion.button\r\n key={i}\r\n initial={{ opacity: 0, x: -10 }}\r\n animate={{ opacity: 1, x: 0 }}\r\n transition={{ delay: i * 0.05 }}\r\n onClick={() => {\r\n setQuery(suggestion);\r\n setShowSuggestions(false);\r\n }}\r\n className=\"w-full px-4 py-3 text-left hover:bg-slate-800/50 flex items-center gap-3\r\n text-sm text-slate-300 transition-colors cursor-pointer\"\r\n >\r\n <Clock className=\"h-4 w-4 text-slate-500\" />\r\n {suggestion}\r\n </motion.button>\r\n ))}\r\n </motion.div>\r\n )}\r\n </AnimatePresence>\r\n </div>\r\n\r\n {/* Filter Panel */}\r\n <AnimatePresence>\r\n {showFilters && (\r\n <motion.div\r\n initial={{ opacity: 0, height: 0 }}\r\n animate={{ opacity: 1, height: 'auto' }}\r\n exit={{ opacity: 0, height: 0 }}\r\n className=\"overflow-hidden\"\r\n >\r\n <div className=\"bg-slate-900/80 border border-slate-700 rounded-xl p-4 space-y-4\">\r\n <div className=\"grid md:grid-cols-3 gap-4\">\r\n {/* Date Range */}\r\n <div className=\"space-y-2\">\r\n <label className=\"text-xs font-medium text-slate-400 uppercase tracking-wider\">\r\n Date Range\r\n </label>\r\n <div className=\"flex flex-wrap gap-2\">\r\n {dateRangeOptions.map((option) => (\r\n <button\r\n key={option.value}\r\n onClick={() => setFilters({ ...filters, dateRange: option.value as any })}\r\n className={`px-3 py-1.5 rounded-lg text-xs font-medium transition-all\r\n flex items-center gap-1.5 cursor-pointer\r\n ${filters.dateRange === option.value\r\n ? 'bg-teal-500/20 text-teal-400 border border-teal-500/30'\r\n : 'bg-slate-800 text-slate-400 hover:bg-slate-700 border border-transparent'\r\n }`}\r\n >\r\n <option.icon className=\"h-3 w-3\" />\r\n {option.label}\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n {/* Score Range */}\r\n <div className=\"space-y-2\">\r\n <label className=\"text-xs font-medium text-slate-400 uppercase tracking-wider\">\r\n Match Score\r\n </label>\r\n <div className=\"flex flex-wrap gap-2\">\r\n {scoreRangeOptions.map((option) => (\r\n <button\r\n key={option.value}\r\n onClick={() => setFilters({ ...filters, scoreRange: option.value as any })}\r\n className={`px-3 py-1.5 rounded-lg text-xs font-medium transition-all\r\n flex items-center gap-1.5 cursor-pointer\r\n ${filters.scoreRange === option.value\r\n ? 'bg-amber-500/20 text-amber-400 border border-amber-500/30'\r\n : 'bg-slate-800 text-slate-400 hover:bg-slate-700 border border-transparent'\r\n }`}\r\n >\r\n <option.icon className=\"h-3 w-3\" />\r\n {option.label}\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n {/* Sort By */}\r\n <div className=\"space-y-2\">\r\n <label className=\"text-xs font-medium text-slate-400 uppercase tracking-wider\">\r\n Sort By\r\n </label>\r\n <div className=\"flex flex-wrap gap-2\">\r\n {sortOptions.map((option) => (\r\n <button\r\n key={option.value}\r\n onClick={() => setFilters({ ...filters, sortBy: option.value as any })}\r\n className={`px-3 py-1.5 rounded-lg text-xs font-medium transition-all cursor-pointer\r\n ${filters.sortBy === option.value\r\n ? 'bg-purple-500/20 text-purple-400 border border-purple-500/30'\r\n : 'bg-slate-800 text-slate-400 hover:bg-slate-700 border border-transparent'\r\n }`}\r\n >\r\n {option.label}\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* Reset Filters */}\r\n {activeFiltersCount > 0 && (\r\n <button\r\n onClick={() => setFilters({ dateRange: 'all', scoreRange: 'all', sortBy: 'date' })}\r\n className=\"text-xs text-slate-500 hover:text-white transition-colors\"\r\n >\r\n Reset all filters\r\n </button>\r\n )}\r\n </div>\r\n </motion.div>\r\n )}\r\n </AnimatePresence>\r\n </div>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ClientOnly.tsx","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nC:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ClientOnly.tsx:19:9\n 17 |\n 18 | useEffect(() => {\n> 19 | setHasMounted(true);\n | ^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 20 | }, []);\n 21 |\n 22 | if (!hasMounted) {","line":19,"column":9,"nodeType":null,"endLine":19,"endColumn":22}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useEffect, useState } from 'react';\r\n\r\ninterface ClientOnlyProps {\r\n children: React.ReactNode;\r\n fallback?: React.ReactNode;\r\n}\r\n\r\n/**\r\n * Wrapper component that only renders children on the client side.\r\n * This prevents hydration mismatches caused by browser extensions like Dark Reader\r\n * that modify the DOM before React hydrates.\r\n */\r\nexport function ClientOnly({ children, fallback = null }: ClientOnlyProps) {\r\n const [hasMounted, setHasMounted] = useState(false);\r\n\r\n useEffect(() => {\r\n setHasMounted(true);\r\n }, []);\r\n\r\n if (!hasMounted) {\r\n return <>{fallback}</>;\r\n }\r\n\r\n return <>{children}</>;\r\n}\r\n\r\nexport default ClientOnly;\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\DashboardClient.tsx","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":17,"column":77,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":17,"endColumn":80,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[807,810],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[807,810],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":18,"column":44,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":18,"endColumn":47,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[862,865],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[862,865],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":19,"column":60,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":19,"endColumn":63,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[934,937],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[934,937],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nC:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\DashboardClient.tsx:30:9\n 28 | .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());\n 29 |\n> 30 | setHistory(combined);\n | ^^^^^^^^^^ Avoid calling setState() directly within an effect\n 31 | setFilteredHistory(combined);\n 32 | }, [serverHistory]);\n 33 |","line":30,"column":9,"nodeType":null,"endLine":30,"endColumn":19},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":106,"column":31,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":106,"endColumn":34,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[4584,4587],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[4584,4587],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":112,"column":62,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":112,"endColumn":65,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[4776,4779],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[4776,4779],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":128,"column":18,"nodeType":"Identifier","messageId":"unusedVar","endLine":128,"endColumn":23},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":154,"column":22,"nodeType":"Identifier","messageId":"unusedVar","endLine":154,"endColumn":27},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":175,"column":22,"nodeType":"Identifier","messageId":"unusedVar","endLine":175,"endColumn":27}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":8,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useEffect, useState, useCallback } from 'react';\r\nimport { Card, CardContent } from \"@/components/ui/card\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { ArrowLeft, Calendar, FileText, ChevronRight, BarChart, Sparkles, Trash2, Edit2, X, Check, Eye } from \"lucide-react\";\r\nimport Link from 'next/link';\r\nimport { motion, AnimatePresence } from 'framer-motion';\r\nimport { useRouter } from 'next/navigation';\r\nimport { generateDocx } from '@/lib/docx-generator';\r\nimport { saveAs } from 'file-saver';\r\nimport { pdf } from '@react-pdf/renderer';\r\nimport { ResumePDF } from './ResumePDF';\r\nimport AdvancedSearch, { SearchFilters } from './AdvancedSearch';\r\nimport { useToast } from './Toast';\r\n\r\nexport default function DashboardClient({ serverHistory }: { serverHistory: any[] }) {\r\n const [history, setHistory] = useState<any[]>([]);\r\n const [filteredHistory, setFilteredHistory] = useState<any[]>([]);\r\n const [editingId, setEditingId] = useState<string | null>(null);\r\n const [editValue, setEditValue] = useState('');\r\n const [deletingId, setDeletingId] = useState<string | null>(null);\r\n const router = useRouter();\r\n const { showToast } = useToast();\r\n\r\n useEffect(() => {\r\n const combined = serverHistory.map(h => ({ ...h, source: 'cloud' }))\r\n .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());\r\n\r\n setHistory(combined);\r\n setFilteredHistory(combined);\r\n }, [serverHistory]);\r\n\r\n // Advanced search handler\r\n const handleSearch = useCallback((query: string, filters: SearchFilters) => {\r\n let result = [...history];\r\n\r\n // Text search\r\n if (query) {\r\n result = result.filter(item =>\r\n item.fileName?.toLowerCase().includes(query.toLowerCase()) ||\r\n item.jobTitle?.toLowerCase().includes(query.toLowerCase())\r\n );\r\n }\r\n\r\n // Date filter\r\n const now = new Date();\r\n if (filters.dateRange !== 'all') {\r\n result = result.filter(item => {\r\n const date = new Date(item.createdAt || item.date);\r\n const yesterday = new Date(now);\r\n yesterday.setDate(now.getDate() - 1);\r\n\r\n switch (filters.dateRange) {\r\n case 'today':\r\n return date.toDateString() === now.toDateString();\r\n case 'yesterday':\r\n return date.toDateString() === yesterday.toDateString();\r\n case 'week':\r\n const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);\r\n return date >= weekAgo;\r\n case 'month':\r\n const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);\r\n return date >= monthAgo;\r\n case 'threeMonths':\r\n const threeMonthsAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);\r\n return date >= threeMonthsAgo;\r\n case 'year':\r\n return date.getFullYear() === now.getFullYear();\r\n case 'lastYear':\r\n return date.getFullYear() === now.getFullYear() - 1;\r\n default:\r\n return true;\r\n }\r\n });\r\n }\r\n\r\n // Score filter\r\n if (filters.scoreRange !== 'all') {\r\n result = result.filter(item => {\r\n const score = item.matchScore || item.result?.matchPercentage || 0;\r\n switch (filters.scoreRange) {\r\n case 'perfect': return score === 100;\r\n case 'high': return score >= 80;\r\n case 'medium': return score >= 50 && score < 80;\r\n default: return true;\r\n }\r\n });\r\n }\r\n\r\n // Sort\r\n result.sort((a, b) => {\r\n switch (filters.sortBy) {\r\n case 'score':\r\n return (b.matchScore || b.result?.matchPercentage || 0) - (a.matchScore || a.result?.matchPercentage || 0);\r\n case 'name':\r\n return (a.fileName || '').localeCompare(b.fileName || '');\r\n default: // date\r\n return new Date(b.createdAt || b.date).getTime() - new Date(a.createdAt || a.date).getTime();\r\n }\r\n });\r\n\r\n setFilteredHistory(result);\r\n }, [history]);\r\n\r\n const handleView = (item: any) => {\r\n if (item.source === 'cloud' && item.id) {\r\n router.push(`/resume/${item.id}`);\r\n }\r\n };\r\n\r\n const handleDownload = async (e: React.MouseEvent, item: any, format: 'PDF' | 'DOCX') => {\r\n e.stopPropagation();\r\n if (!item.content) return;\r\n\r\n const data = JSON.parse(item.content);\r\n const fileName = item.fileName || 'Resume';\r\n\r\n try {\r\n if (format === 'PDF') {\r\n const blob = await pdf(<ResumePDF data={data} />).toBlob();\r\n saveAs(blob, `${fileName}.pdf`);\r\n } else {\r\n const blob = await generateDocx(data);\r\n saveAs(blob, `${fileName}.docx`);\r\n }\r\n showToast(`Downloaded ${format} successfully!`, 'success');\r\n } catch (error) {\r\n showToast('Download failed. Please try again.', 'error');\r\n }\r\n };\r\n\r\n // CRUD: Update/Rename\r\n const handleRename = async (id: string) => {\r\n if (!editValue.trim()) return;\r\n\r\n const item = history.find(h => h.id === id);\r\n if (!item) return;\r\n\r\n if (item.source === 'cloud') {\r\n try {\r\n const res = await fetch(`/api/resumes/${id}`, {\r\n method: 'PUT',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ fileName: editValue }),\r\n });\r\n\r\n if (res.ok) {\r\n setHistory(prev => prev.map(h => h.id === id ? { ...h, fileName: editValue } : h));\r\n setFilteredHistory(prev => prev.map(h => h.id === id ? { ...h, fileName: editValue } : h));\r\n setEditingId(null);\r\n showToast('Resume renamed successfully!', 'success');\r\n }\r\n } catch (error) {\r\n showToast('Failed to rename.', 'error');\r\n }\r\n }\r\n };\r\n\r\n // CRUD: Delete\r\n const handleDelete = async (id: string) => {\r\n const item = history.find(h => h.id === id);\r\n if (!item) return;\r\n\r\n if (item.source === 'cloud') {\r\n try {\r\n const res = await fetch(`/api/resumes/${id}`, { method: 'DELETE' });\r\n\r\n if (res.ok) {\r\n setHistory(prev => prev.filter(h => h.id !== id));\r\n setFilteredHistory(prev => prev.filter(h => h.id !== id));\r\n setDeletingId(null);\r\n showToast('Resume deleted successfully!', 'success');\r\n }\r\n } catch (error) {\r\n showToast('Failed to delete.', 'error');\r\n }\r\n }\r\n };\r\n\r\n return (\r\n <main className=\"p-6 md:p-12\">\r\n <div className=\"max-w-4xl mx-auto space-y-8\">\r\n\r\n {/* Header */}\r\n <div className=\"flex flex-col gap-4\">\r\n <div className=\"flex flex-col md:flex-row md:items-center justify-between gap-4\">\r\n <div>\r\n <Link href=\"/\" className=\"flex items-center gap-2 text-slate-400 hover:text-white transition-colors mb-2\">\r\n <ArrowLeft className=\"h-4 w-4\" /> Back to Analyzer\r\n </Link>\r\n <h1 className=\"text-3xl font-bold font-display\">Your Library</h1>\r\n <p className=\"text-slate-500 text-sm mt-1\">{history.length} resumes saved</p>\r\n </div>\r\n </div>\r\n\r\n {/* Advanced Search */}\r\n <AdvancedSearch onSearch={handleSearch} history={history} />\r\n </div>\r\n\r\n {/* List */}\r\n <div className=\"grid gap-4\">\r\n {filteredHistory.length === 0 ? (\r\n <div className=\"text-center py-20 bg-slate-900/50 rounded-2xl border border-slate-800\">\r\n <BarChart className=\"h-12 w-12 text-slate-600 mx-auto mb-4\" />\r\n <h3 className=\"text-xl font-medium text-slate-300\">No Resumes Found</h3>\r\n <p className=\"text-slate-500 mt-2\">Upload a resume or save an optimized one to see it here.</p>\r\n <Button asChild className=\"mt-6 bg-teal-600 hover:bg-teal-500\">\r\n <Link href=\"/\">Upload Resume</Link>\r\n </Button>\r\n </div>\r\n ) : (\r\n filteredHistory.map((item, i) => (\r\n <motion.div\r\n key={item.id || i}\r\n initial={{ opacity: 0, y: 20 }}\r\n animate={{ opacity: 1, y: 0 }}\r\n transition={{ delay: i * 0.05 }}\r\n layout\r\n >\r\n <Card className=\"glass-card hover:bg-slate-800/50 transition-all group border-slate-800 relative overflow-hidden\">\r\n <CardContent className=\"flex flex-col md:flex-row md:items-center justify-between p-6 gap-4\">\r\n <div className=\"flex items-center gap-4 flex-1 min-w-0 cursor-pointer\" onClick={() => handleView(item)}>\r\n <div className={`p-3 rounded-full flex-shrink-0 ${item.matchScore === 100 ? 'bg-amber-500/10 text-amber-400' : 'bg-teal-500/10 text-teal-400'}`}>\r\n {item.matchScore === 100 ? <Sparkles className=\"h-6 w-6\" /> : <FileText className=\"h-6 w-6\" />}\r\n </div>\r\n <div className=\"min-w-0 cursor-pointer\">\r\n {editingId === item.id ? (\r\n <div className=\"flex items-center gap-2\" onClick={e => e.stopPropagation()}>\r\n <input\r\n type=\"text\"\r\n value={editValue}\r\n onChange={(e) => setEditValue(e.target.value)}\r\n className=\"bg-slate-800 border border-slate-600 rounded px-2 py-1 text-sm w-48\"\r\n autoFocus\r\n onKeyDown={(e) => e.key === 'Enter' && handleRename(item.id)}\r\n />\r\n <button onClick={() => handleRename(item.id)} className=\"p-1 hover:bg-emerald-900/50 rounded\">\r\n <Check className=\"h-4 w-4 text-emerald-400\" />\r\n </button>\r\n <button onClick={() => setEditingId(null)} className=\"p-1 hover:bg-slate-700 rounded\">\r\n <X className=\"h-4 w-4 text-slate-400\" />\r\n </button>\r\n </div>\r\n ) : (\r\n <>\r\n <div className=\"flex items-center gap-2\">\r\n <h3 className=\"font-semibold text-lg text-slate-200 group-hover:text-teal-300 transition-colors truncate\">\r\n {item.fileName}\r\n </h3>\r\n {item.content && <span className=\"text-[10px] bg-amber-950/40 text-amber-500 px-2 py-0.5 rounded border border-amber-900/50 flex-shrink-0\">OPTIMIZED</span>}\r\n </div>\r\n <div className=\"flex items-center gap-3 text-slate-500 text-sm mt-1\">\r\n <span className=\"flex items-center gap-1 cursor-pointer\" title=\"Time in IST\">\r\n <Calendar className=\"h-3 w-3\" />\r\n {new Date(item.createdAt || item.date).toLocaleString('en-IN', {\r\n timeZone: 'Asia/Kolkata',\r\n day: '2-digit',\r\n month: 'short',\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n hour12: true\r\n })}\r\n </span>\r\n <span>ΓÇó</span>\r\n <span>Score: <span className={item.matchScore >= 90 || item.result?.matchPercentage >= 70 ? \"text-teal-400 font-bold\" : \"text-yellow-400 font-bold\"}>{item.matchScore || item.result?.matchPercentage}%</span></span>\r\n </div>\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n\r\n {/* Actions */}\r\n <div className=\"flex items-center gap-2 self-end md:self-auto flex-shrink-0\">\r\n {/* View Button */}\r\n {item.source === 'cloud' && item.id && (\r\n <Button\r\n size=\"sm\"\r\n variant=\"outline\"\r\n className=\"h-8 text-xs border-teal-700 text-teal-400 hover:bg-teal-900/50\"\r\n onClick={() => router.push(`/resume/${item.id}`)}\r\n >\r\n <Eye className=\"h-3 w-3 mr-1\" /> View\r\n </Button>\r\n )}\r\n\r\n {item.content && (\r\n <>\r\n <Button size=\"sm\" variant=\"outline\" className=\"h-8 text-xs border-slate-700 hover:bg-slate-800\" onClick={(e) => handleDownload(e, item, 'PDF')}>\r\n PDF\r\n </Button>\r\n <Button size=\"sm\" variant=\"outline\" className=\"h-8 text-xs border-slate-700 hover:bg-slate-800\" onClick={(e) => handleDownload(e, item, 'DOCX')}>\r\n Word\r\n </Button>\r\n </>\r\n )}\r\n\r\n {/* Edit Button - for both Cloud and Local */}\r\n <button\r\n onClick={(e) => {\r\n e.stopPropagation();\r\n setEditingId(item.id);\r\n setEditValue(item.fileName);\r\n }}\r\n className=\"p-2 hover:bg-slate-700 rounded transition-colors cursor-pointer\"\r\n title=\"Rename\"\r\n >\r\n <Edit2 className=\"h-4 w-4 text-slate-400\" />\r\n </button>\r\n\r\n {/* Delete Button - for both Cloud and Local */}\r\n <button\r\n onClick={(e) => {\r\n e.stopPropagation();\r\n setDeletingId(item.id);\r\n }}\r\n className=\"p-2 hover:bg-red-900/50 rounded transition-colors cursor-pointer\"\r\n title=\"Delete\"\r\n >\r\n <Trash2 className=\"h-4 w-4 text-red-400\" />\r\n </button>\r\n\r\n {item.source === 'local' && (\r\n <ChevronRight className=\"h-5 w-5 text-slate-600 group-hover:text-white transition-colors\" />\r\n )}\r\n </div>\r\n </CardContent>\r\n\r\n {/* Delete Confirmation Overlay */}\r\n <AnimatePresence>\r\n {deletingId === item.id && (\r\n <motion.div\r\n initial={{ opacity: 0 }}\r\n animate={{ opacity: 1 }}\r\n exit={{ opacity: 0 }}\r\n className=\"absolute inset-0 bg-red-950/90 backdrop-blur-sm flex items-center justify-center gap-4 z-10\"\r\n >\r\n <p className=\"text-red-300 font-medium\">Delete this resume?</p>\r\n <Button\r\n size=\"sm\"\r\n onClick={() => handleDelete(item.id)}\r\n className=\"bg-red-600 hover:bg-red-500 text-white\"\r\n >\r\n Yes, Delete\r\n </Button>\r\n <Button\r\n size=\"sm\"\r\n variant=\"outline\"\r\n onClick={() => setDeletingId(null)}\r\n className=\"border-red-700 text-red-300 hover:bg-red-900/50\"\r\n >\r\n Cancel\r\n </Button>\r\n </motion.div>\r\n )}\r\n </AnimatePresence>\r\n </Card>\r\n </motion.div>\r\n ))\r\n )}\r\n </div>\r\n </div>\r\n </main>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\Footer.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\InterviewCoach.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'CardFooter' is defined but never used.","line":5,"column":52,"nodeType":"Identifier","messageId":"unusedVar","endLine":5,"endColumn":62,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"CardFooter"},"fix":{"range":[178,190],"text":""},"desc":"Remove unused variable \"CardFooter\"."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'Badge' is defined but never used.","line":8,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":8,"endColumn":15,"suggestions":[{"messageId":"removeUnusedImportDeclaration","data":{"varName":"Badge"},"fix":{"range":[332,378],"text":""},"desc":"Remove unused import declaration."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'MessageSquare' is defined but never used.","line":9,"column":29,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":42,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"MessageSquare"},"fix":{"range":[406,421],"text":""},"desc":"Remove unused variable \"MessageSquare\"."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'Volume2' is defined but never used.","line":9,"column":55,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":62,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"Volume2"},"fix":{"range":[432,441],"text":""},"desc":"Remove unused variable \"Volume2\"."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'PlayCircle' is defined but never used.","line":9,"column":97,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":107,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"PlayCircle"},"fix":{"range":[474,486],"text":""},"desc":"Remove unused variable \"PlayCircle\"."}]},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'stopRecording'. Either include it or remove the dependency array.","line":57,"column":8,"nodeType":"ArrayExpression","endLine":57,"endColumn":21,"suggestions":[{"desc":"Update the dependencies array to be: [isRecording, stopRecording]","fix":{"range":[2163,2176],"text":"[isRecording, stopRecording]"}}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":168,"column":73,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":168,"endColumn":76,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[6616,6619],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[6616,6619],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":7,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useRef, useEffect } from 'react';\r\nimport { motion, AnimatePresence } from 'framer-motion';\r\nimport { Card, CardContent, CardHeader, CardTitle, CardFooter } from \"@/components/ui/card\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { ScrollArea } from \"@/components/ui/scroll-area\";\r\nimport { Badge } from \"@/components/ui/badge\";\r\nimport { Mic, MicOff, Send, MessageSquare, User, Bot, Volume2, Sparkles, Loader2, ChevronRight, PlayCircle } from \"lucide-react\";\r\nimport { transcribeAudio, generateCoachResponse } from '@/app/actions/sarvam';\r\n\r\ninterface InterviewCoachProps {\r\n questions: { question: string, answer: string }[];\r\n}\r\n\r\ninterface Message {\r\n role: 'user' | 'assistant';\r\n content: string;\r\n timestamp: number;\r\n}\r\n\r\nexport default function InterviewCoach({ questions }: InterviewCoachProps) {\r\n const [activeQuestion, setActiveQuestion] = useState<number | null>(null);\r\n const [messages, setMessages] = useState<Message[]>([]);\r\n const [isRecording, setIsRecording] = useState(false);\r\n const [isProcessing, setIsProcessing] = useState(false);\r\n const [inputText, setInputText] = useState('');\r\n\r\n const mediaRecorderRef = useRef<MediaRecorder | null>(null);\r\n const audioChunksRef = useRef<Blob[]>([]);\r\n const [recordingTime, setRecordingTime] = useState(0);\r\n const chatEndRef = useRef<HTMLDivElement>(null);\r\n\r\n // ... (refs remain same)\r\n\r\n useEffect(() => {\r\n chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });\r\n }, [messages, isProcessing]);\r\n\r\n // Timer effect for recording limit\r\n useEffect(() => {\r\n let interval: NodeJS.Timeout;\r\n if (isRecording) {\r\n interval = setInterval(() => {\r\n setRecordingTime(prev => {\r\n if (prev >= 29) { // 30s limit - 1s buffer\r\n stopRecording();\r\n return 0;\r\n }\r\n return prev + 1;\r\n });\r\n }, 1000);\r\n } else {\r\n setRecordingTime(0);\r\n }\r\n return () => clearInterval(interval);\r\n }, [isRecording]);\r\n\r\n const startPractice = (index: number) => {\r\n setActiveQuestion(index);\r\n setMessages([\r\n {\r\n role: 'assistant',\r\n content: `Let's practice! Here is the question:\\n\\n${questions[index].question}\\n\\nTake a moment to think, then you can type your answer or use the microphone to speak.`,\r\n timestamp: Date.now()\r\n }\r\n ]);\r\n };\r\n\r\n const stopPractice = () => {\r\n setActiveQuestion(null);\r\n setMessages([]);\r\n setInputText('');\r\n };\r\n\r\n\r\n const FormattedMessage = ({ content }: { content: string }) => {\r\n // 1. Clean markdown symbols\r\n const cleanText = content\r\n .replace(/[*_#`~-]/g, '') // Remove common markdown chars\r\n .replace(/\\n\\s*\\n/g, '\\n'); // Remove extra blank lines\r\n\r\n // 2. Split into lines to check for keywords\r\n const lines = cleanText.split('\\n');\r\n\r\n return (\r\n <div className=\"space-y-1\">\r\n {lines.map((line, i) => {\r\n const trimmed = line.trim();\r\n if (!trimmed) return <div key={i} className=\"h-2\" />;\r\n\r\n // Highlighting logic\r\n if (trimmed.match(/^(feedback|good|positive|great)/i) || trimmed.includes(\"Γ£à\")) {\r\n return (\r\n <div key={i} className=\"text-emerald-300 font-semibold bg-emerald-950/30 px-2 py-1 rounded w-fit mb-1 mt-2\">\r\n {trimmed}\r\n </div>\r\n );\r\n }\r\n if (trimmed.match(/^(improvement|tip|suggest|weak)/i) || trimmed.includes(\"ΓÜá∩╕Å\") || trimmed.includes(\"≡ƒÆí\")) {\r\n return (\r\n <div key={i} className=\"text-amber-300 font-semibold bg-amber-950/30 px-2 py-1 rounded w-fit mb-1 mt-2\">\r\n {trimmed}\r\n </div>\r\n );\r\n }\r\n if (trimmed.match(/^(question|next|ask|follow)/i) || trimmed.includes(\"Γ¥ô\") || trimmed.includes(\"≡ƒñö\")) {\r\n return (\r\n <div key={i} className=\"text-blue-300 font-semibold bg-blue-950/30 px-2 py-1 rounded w-fit mb-1 mt-2\">\r\n {trimmed}\r\n </div>\r\n );\r\n }\r\n\r\n // Numbered lists highlighting\r\n if (trimmed.match(/^\\d+\\./)) {\r\n return (\r\n <div key={i} className=\"pl-2 border-l-2 border-slate-600 ml-1 text-slate-200\">\r\n {trimmed}\r\n </div>\r\n );\r\n }\r\n\r\n return <p key={i} className=\"text-slate-200\">{trimmed}</p>;\r\n })}\r\n </div>\r\n );\r\n };\r\n\r\n const handleSendMessage = async (text: string) => {\r\n if (!text.trim()) return;\r\n\r\n const newMessages = [\r\n ...messages,\r\n { role: 'user', content: text, timestamp: Date.now() }\r\n ] as Message[];\r\n\r\n setMessages(newMessages);\r\n setInputText('');\r\n setIsProcessing(true);\r\n\r\n try {\r\n // Prepare context for the AI\r\n const history = newMessages\r\n .filter((m, i) => !(i === 0 && m.role === 'assistant'))\r\n .map(m => ({\r\n role: m.role,\r\n content: m.content\r\n }));\r\n\r\n if (history.length <= 2) {\r\n const context = `You are an expert Interview Coach. \r\n The user is practicing this question: \"${questions[activeQuestion!].question}\".\r\n The suggested answer/tip was: \"${questions[activeQuestion!].answer}\".\r\n \r\n Analyze the user's response. Structure your response efficiently.\r\n \r\n Use these exact headers to separate sections:\r\n Γ£à FEEDBACK: (Positive aspects)\r\n ≡ƒÆí TIPS: (Improvements)\r\n ≡ƒñö QUESTION: (Follow-up)\r\n \r\n CRITICAL INSTRUCTIONS:\r\n - DO NOT use bold (**), italics (*), or headers (###).\r\n - Keep it plain text.\r\n - Be concise.`;\r\n\r\n history.unshift({ role: 'system', content: context } as any);\r\n }\r\n\r\n const response = await generateCoachResponse(history);\r\n\r\n if (response.success && response.content) {\r\n setMessages(prev => [\r\n ...prev,\r\n { role: 'assistant', content: response.content, timestamp: Date.now() }\r\n ]);\r\n } else {\r\n setMessages(prev => [\r\n ...prev,\r\n { role: 'assistant', content: \"Sorry, I couldn't generate a response. Please try again.\", timestamp: Date.now() }\r\n ]);\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n } finally {\r\n setIsProcessing(false);\r\n }\r\n };\r\n\r\n // ... (keep recording functions) ...\r\n\r\n // Update render:\r\n // Replace the message content div\r\n // ...\r\n\r\n // ...\r\n\r\n\r\n // ... (rest of startRecording, stopRecording remains same)\r\n\r\n // Render loop snippet:\r\n\r\n const startRecording = async () => {\r\n try {\r\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\r\n const mediaRecorder = new MediaRecorder(stream);\r\n mediaRecorderRef.current = mediaRecorder;\r\n audioChunksRef.current = [];\r\n\r\n mediaRecorder.ondataavailable = (event) => {\r\n if (event.data.size > 0) {\r\n audioChunksRef.current.push(event.data);\r\n }\r\n };\r\n\r\n mediaRecorder.onstop = async () => {\r\n const audioBlob = new Blob(audioChunksRef.current, { type: 'audio/webm' }); // webm is standard for MediaRecorder\r\n // const audioFile = new File([audioBlob], \"voice_input.webm\", { type: 'audio/webm' });\r\n\r\n setIsProcessing(true); // Show loading while transcribing\r\n\r\n // Convert Blob to File for formData\r\n const formData = new FormData();\r\n formData.append('audio', audioBlob, 'recording.webm');\r\n\r\n try {\r\n const result = await transcribeAudio(formData);\r\n if (result.success && result.text) {\r\n handleSendMessage(result.text); // Send transcribed text as user message\r\n } else {\r\n // Handle error\r\n console.error(\"Transcription failed\", result.error);\r\n setMessages(prev => [\r\n ...prev,\r\n { role: 'assistant', content: \"I had trouble hearing that. Could you try speaking again or typing your answer?\", timestamp: Date.now() }\r\n ]);\r\n setIsProcessing(false);\r\n }\r\n } catch (err) {\r\n console.error(err);\r\n setIsProcessing(false);\r\n }\r\n\r\n // Stop all tracks\r\n stream.getTracks().forEach(track => track.stop());\r\n };\r\n\r\n mediaRecorder.start();\r\n setIsRecording(true);\r\n } catch (err) {\r\n console.error(\"Error accessing microphone:\", err);\r\n alert(\"Could not access microphone. Please ensuring permission is granted.\");\r\n }\r\n };\r\n\r\n const stopRecording = () => {\r\n if (mediaRecorderRef.current && isRecording) {\r\n mediaRecorderRef.current.stop();\r\n setIsRecording(false);\r\n }\r\n };\r\n\r\n return (\r\n <Card className=\"glass-card border-t-4 border-t-teal-500 w-full\">\r\n <CardHeader className=\"flex flex-row items-start justify-between\">\r\n <div>\r\n <CardTitle className=\"flex items-center gap-2 text-2xl font-display text-white\">\r\n <Sparkles className=\"text-teal-400 h-6 w-6\" /> AI Interview Coach\r\n </CardTitle>\r\n <p className=\"text-slate-400 mt-2\">Practice your answers with real-time AI feedback.</p>\r\n </div>\r\n {activeQuestion !== null && (\r\n <Button variant=\"ghost\" onClick={stopPractice} className=\"text-slate-400 hover:text-white\">\r\n Exit Practice\r\n </Button>\r\n )}\r\n </CardHeader>\r\n\r\n <CardContent className=\"p-0\">\r\n <AnimatePresence mode=\"wait\">\r\n {activeQuestion === null ? (\r\n <motion.div\r\n initial={{ opacity: 0 }}\r\n animate={{ opacity: 1 }}\r\n exit={{ opacity: 0 }}\r\n className=\"p-6 space-y-4\"\r\n >\r\n <ScrollArea className=\"h-[400px] w-full pr-4\">\r\n <div className=\"space-y-4\">\r\n {questions.map((q, i) => (\r\n <motion.div\r\n key={i}\r\n initial={{ opacity: 0, y: 10 }}\r\n animate={{ opacity: 1, y: 0 }}\r\n transition={{ delay: i * 0.05 }}\r\n className=\"bg-slate-950/30 p-5 rounded-xl border border-slate-800/50 hover:border-teal-500/30 transition-colors group cursor-pointer\"\r\n onClick={() => startPractice(i)}\r\n >\r\n <div className=\"flex justify-between items-start gap-4\">\r\n <div>\r\n <h4 className=\"text-base font-medium text-slate-200 mb-2 group-hover:text-teal-300 transition-colors\">\r\n {q.question}\r\n </h4>\r\n <p className=\"text-sm text-slate-500 line-clamp-1 italic\">\r\n Tip: {q.answer}\r\n </p>\r\n </div>\r\n <Button size=\"sm\" className=\"bg-teal-500/10 text-teal-400 hover:bg-teal-500 hover:text-white shrink-0\">\r\n Practice <ChevronRight className=\"h-4 w-4 ml-1\" />\r\n </Button>\r\n </div>\r\n </motion.div>\r\n ))}\r\n </div>\r\n </ScrollArea>\r\n </motion.div>\r\n ) : (\r\n <motion.div\r\n initial={{ opacity: 0, x: 20 }}\r\n animate={{ opacity: 1, x: 0 }}\r\n exit={{ opacity: 0, x: -20 }}\r\n className=\"flex flex-col h-[500px]\"\r\n >\r\n <ScrollArea className=\"flex-1 p-6\">\r\n <div className=\"space-y-6\">\r\n {messages.map((msg, i) => (\r\n <motion.div\r\n key={i}\r\n initial={{ opacity: 0, y: 10 }}\r\n animate={{ opacity: 1, y: 0 }}\r\n className={`flex gap-3 ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}\r\n >\r\n {msg.role === 'assistant' && (\r\n <div className=\"w-10 h-10 rounded-full bg-gradient-to-br from-teal-400 to-emerald-500 flex items-center justify-center shrink-0 shadow-lg shadow-teal-500/20\">\r\n <Sparkles className=\"h-5 w-5 text-white\" />\r\n </div>\r\n )}\r\n\r\n <div className={`\r\n max-w-[85%] rounded-2xl p-5 text-[15px] leading-7 shadow-xl\r\n ${msg.role === 'user'\r\n ? 'bg-slate-800 text-slate-200 rounded-tr-none border border-slate-700'\r\n : 'bg-gradient-to-br from-indigo-900/90 to-slate-900/90 text-white rounded-tl-none border border-indigo-500/30 backdrop-blur-md'}\r\n `}>\r\n {msg.role === 'assistant' ? (\r\n <FormattedMessage content={msg.content} />\r\n ) : (\r\n <p className=\"whitespace-pre-wrap font-light tracking-wide\">{msg.content}</p>\r\n )}\r\n </div>\r\n\r\n {msg.role === 'user' && (\r\n <div className=\"w-10 h-10 rounded-full bg-slate-700 flex items-center justify-center shrink-0 border border-slate-600\">\r\n <User className=\"h-5 w-5 text-slate-300\" />\r\n </div>\r\n )}\r\n </motion.div>\r\n ))}\r\n\r\n {isProcessing && (\r\n <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} className=\"flex gap-3 justify-start\">\r\n <div className=\"w-8 h-8 rounded-full bg-teal-500/20 flex items-center justify-center shrink-0 border border-teal-500/30\">\r\n <Bot className=\"h-4 w-4 text-teal-400\" />\r\n </div>\r\n <div className=\"bg-slate-800/50 rounded-2xl p-4 rounded-tl-none flex items-center gap-2 text-slate-400 text-sm\">\r\n <Loader2 className=\"h-4 w-4 animate-spin\" /> Thinking...\r\n </div>\r\n </motion.div>\r\n )}\r\n <div ref={chatEndRef} />\r\n </div>\r\n </ScrollArea>\r\n\r\n <div className=\"p-4 bg-slate-950/50 border-t border-slate-800/50 backdrop-blur-sm\">\r\n <div className=\"flex items-center gap-3\">\r\n <Button\r\n size=\"icon\"\r\n variant={isRecording ? \"destructive\" : \"secondary\"}\r\n className={`rounded-full h-12 w-12 transition-all ${isRecording ? 'animate-pulse ring-4 ring-red-500/20' : 'hover:bg-slate-700'}`}\r\n onClick={isRecording ? stopRecording : startRecording}\r\n disabled={isProcessing}\r\n >\r\n {isRecording ? <MicOff className=\"h-5 w-5\" /> : <Mic className=\"h-5 w-5\" />}\r\n </Button>\r\n\r\n <div className=\"flex-1 relative\">\r\n <input\r\n type=\"text\"\r\n className=\"w-full bg-slate-900/80 border-slate-700 text-slate-200 rounded-full py-3 px-5 focus:outline-none focus:ring-2 focus:ring-teal-500/50 focus:border-transparent placeholder:text-slate-500\"\r\n placeholder={isRecording ? \"Listening...\" : \"Type your answer...\"}\r\n value={inputText}\r\n onChange={(e) => setInputText(e.target.value)}\r\n onKeyDown={(e) => e.key === 'Enter' && !isProcessing && handleSendMessage(inputText)}\r\n disabled={isRecording || isProcessing}\r\n />\r\n </div>\r\n\r\n <Button\r\n size=\"icon\"\r\n className=\"rounded-full h-12 w-12 bg-teal-600 hover:bg-teal-500 text-white shadow-[0_0_20px_rgba(20,184,166,0.2)]\"\r\n onClick={() => handleSendMessage(inputText)}\r\n disabled={!inputText.trim() || isProcessing || isRecording}\r\n >\r\n <Send className=\"h-5 w-5\" />\r\n </Button>\r\n </div>\r\n\r\n {/* Voice Wave Animation Hint */}\r\n <AnimatePresence>\r\n {isRecording && (\r\n <motion.div\r\n initial={{ height: 0, opacity: 0 }}\r\n animate={{ height: 'auto', opacity: 1 }}\r\n exit={{ height: 0, opacity: 0 }}\r\n className=\"overflow-hidden\"\r\n >\r\n <div className=\"flex items-center justify-center gap-1 mt-2 h-4\">\r\n {[1, 2, 3, 4, 5, 4, 3, 2, 1].map((n, i) => (\r\n <motion.div\r\n key={i}\r\n animate={{ height: [4, n * 3, 4] }}\r\n transition={{ repeat: Infinity, duration: 0.5, delay: i * 0.05 }}\r\n className={`w-1 rounded-full ${recordingTime > 20 ? 'bg-red-500' : 'bg-teal-500'}`}\r\n />\r\n ))}\r\n <span className={`text-xs ml-2 font-medium ${recordingTime > 20 ? 'text-red-400' : 'text-teal-400'}`}>\r\n Recording... {30 - recordingTime}s left\r\n </span>\r\n </div>\r\n </motion.div>\r\n )}\r\n </AnimatePresence>\r\n </div>\r\n </motion.div>\r\n )}\r\n </AnimatePresence>\r\n </CardContent>\r\n </Card>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\LoadingText.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\Navbar.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'motion' is defined but never used.","line":7,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":7,"endColumn":16,"suggestions":[{"messageId":"removeUnusedImportDeclaration","data":{"varName":"motion"},"fix":{"range":[218,257],"text":""},"desc":"Remove unused import declaration."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport Link from \"next/link\";\r\nimport { SignedIn, SignedOut, SignInButton, UserButton } from \"@clerk/nextjs\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Github } from \"lucide-react\";\r\nimport { motion } from \"framer-motion\";\r\nimport Image from \"next/image\";\r\n\r\nexport default function Navbar() {\r\n return (\r\n <header className=\"sticky top-0 z-50 w-full border-b border-white/5 bg-slate-950/60 backdrop-blur-xl transition-all\">\r\n <div className=\"max-w-6xl mx-auto flex h-20 items-center justify-between px-6 md:px-12\">\r\n <Link href=\"/\" className=\"flex items-center gap-3 cursor-pointer group\">\r\n <div className=\"relative w-10 h-10 group-hover:scale-105 transition-transform\">\r\n <Image src=\"/logo.png\" alt=\"Logo\" fill className=\"object-contain\" />\r\n </div>\r\n <span className=\"text-xl font-bold font-display tracking-tight group-hover:text-teal-100 transition-colors\">\r\n career<span className=\"text-teal-400\">zen</span>\r\n </span>\r\n </Link>\r\n\r\n <div className=\"flex items-center gap-4\">\r\n <div className=\"hidden md:block mr-2\">\r\n <a\r\n href=\"https://github.com/aniruddhaadak80/smart-resume-analyzer\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"group relative inline-flex items-center justify-center p-0.5 overflow-hidden text-sm font-medium rounded-full hover:scale-105 transition-transform duration-300\"\r\n >\r\n <div className=\"absolute inset-0 bg-gradient-to-r from-purple-500 via-pink-500 to-teal-500 animate-gradient-xy opacity-70 blur-md group-hover:opacity-100 transition-opacity\"></div>\r\n <div className=\"relative flex items-center px-4 py-2 bg-slate-950 rounded-full transition-all ease-in duration-75 group-hover:bg-slate-900\">\r\n <Github className=\"w-4 h-4 mr-2 text-white group-hover:text-teal-400 transition-colors\" />\r\n <span className=\"bg-gradient-to-r from-white to-slate-400 bg-clip-text text-transparent font-bold group-hover:from-teal-300 group-hover:to-cyan-300\">Star on GitHub</span>\r\n </div>\r\n </a>\r\n </div>\r\n <SignedOut>\r\n <SignInButton mode=\"modal\" forceRedirectUrl=\"/dashboard\">\r\n <Button variant=\"ghost\" className=\"text-slate-300 hover:text-white hover:bg-slate-800/50\">Sign In</Button>\r\n </SignInButton>\r\n <SignInButton mode=\"modal\" forceRedirectUrl=\"/dashboard\">\r\n <Button className=\"bg-teal-500/10 text-teal-300 border border-teal-500/20 hover:bg-teal-500/20 rounded-full\">Get Started</Button>\r\n </SignInButton>\r\n </SignedOut>\r\n <SignedIn>\r\n <div className=\"flex items-center gap-4\">\r\n <Link href=\"/dashboard\" prefetch={true} className=\"text-sm font-medium text-slate-300 hover:text-white hover:underline underline-offset-4 hidden md:inline-block cursor-pointer\">\r\n Dashboard\r\n </Link>\r\n <Link href=\"/optimize\" prefetch={true} className=\"text-sm font-medium text-slate-300 hover:text-white hover:underline underline-offset-4 hidden md:inline-block cursor-pointer\">\r\n Optimize\r\n </Link>\r\n <UserButton afterSignOutUrl=\"/\" appearance={{\r\n elements: {\r\n avatarBox: \"w-10 h-10 ring-2 ring-teal-500/20\"\r\n }\r\n }} />\r\n </div>\r\n </SignedIn>\r\n </div>\r\n </div>\r\n </header>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\OptimizeClient.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'Download' is defined but never used.","line":5,"column":54,"nodeType":"Identifier","messageId":"unusedVar","endLine":5,"endColumn":62,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"Download"},"fix":{"range":[172,182],"text":""},"desc":"Remove unused variable \"Download\"."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":75,"column":56,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":75,"endColumn":59,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[3814,3817],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[3814,3817],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":115,"column":18,"nodeType":"Identifier","messageId":"unusedVar","endLine":115,"endColumn":23},{"ruleId":"@typescript-eslint/no-unused-expressions","severity":1,"message":"Expected an assignment or function call and instead saw an expression.","line":118,"column":13,"nodeType":"ExpressionStatement","messageId":"unusedExpression","endLine":118,"endColumn":89},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":194,"column":18,"nodeType":"Identifier","messageId":"unusedVar","endLine":194,"endColumn":23},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":462,"column":81,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":462,"endColumn":84,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[26724,26727],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[26724,26727],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect } from 'react';\r\nimport { motion, AnimatePresence } from 'framer-motion';\r\nimport { ArrowLeft, Sparkles, Loader2, CheckCircle2, Download, Save, FileText, FileType2, Upload, Briefcase } from 'lucide-react';\r\nimport Link from 'next/link';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Textarea } from '@/components/ui/textarea';\r\nimport { Card, CardContent } from '@/components/ui/card';\r\nimport { optimizeResume } from '@/app/actions/optimize';\r\nimport { saveResume } from '@/app/actions/resume';\r\nimport { extractText } from '@/app/actions/extract';\r\nimport { useUser } from '@clerk/nextjs';\r\nimport { generateDocx } from '@/lib/docx-generator';\r\nimport { saveAs } from 'file-saver';\r\nimport { pdf } from '@react-pdf/renderer';\r\nimport { ResumePDF } from './ResumePDF';\r\nimport { useToast } from './Toast';\r\nimport { useRouter } from 'next/navigation';\r\n\r\n// Skill color palette\r\nconst skillColors = [\r\n { bg: 'from-rose-900/50 to-pink-900/50', text: 'text-rose-300', border: 'border-rose-500/30' },\r\n { bg: 'from-amber-900/50 to-orange-900/50', text: 'text-amber-300', border: 'border-amber-500/30' },\r\n { bg: 'from-emerald-900/50 to-teal-900/50', text: 'text-emerald-300', border: 'border-emerald-500/30' },\r\n { bg: 'from-cyan-900/50 to-blue-900/50', text: 'text-cyan-300', border: 'border-cyan-500/30' },\r\n { bg: 'from-violet-900/50 to-purple-900/50', text: 'text-violet-300', border: 'border-violet-500/30' },\r\n { bg: 'from-fuchsia-900/50 to-pink-900/50', text: 'text-fuchsia-300', border: 'border-fuchsia-500/30' },\r\n { bg: 'from-sky-900/50 to-indigo-900/50', text: 'text-sky-300', border: 'border-sky-500/30' },\r\n { bg: 'from-lime-900/50 to-green-900/50', text: 'text-lime-300', border: 'border-lime-500/30' },\r\n];\r\n\r\n// Keyword highlighting\r\nconst highlightSummary = (text: string) => {\r\n if (!text) return null;\r\n const patterns = [\r\n { regex: /(\\d+%|\\d+\\+|\\$[\\d,]+|\\d+ years?|\\d+x)/gi, class: 'text-emerald-400 font-bold' },\r\n { regex: /(React|Node\\.js|Python|JavaScript|TypeScript|AWS|Docker|MongoDB|PostgreSQL|Next\\.js|Express\\.js|Java|C\\+\\+|Tailwind|REST|API|GraphQL|Kubernetes|CI\\/CD|Git|Agile|Scrum)/gi, class: 'text-cyan-400 font-semibold' },\r\n { regex: /(Full Stack|Front-end|Backend|Senior|Lead|Manager|Engineer|Developer|Architect|Expert|Proven|Award-winning|Certified)/gi, class: 'text-amber-400 font-semibold' },\r\n ];\r\n\r\n let parts: { text: string; className?: string }[] = [{ text }];\r\n\r\n patterns.forEach(pattern => {\r\n const newParts: { text: string; className?: string }[] = [];\r\n parts.forEach(part => {\r\n if (part.className) {\r\n newParts.push(part);\r\n return;\r\n }\r\n const segments = part.text.split(pattern.regex);\r\n const matches = part.text.match(pattern.regex) || [];\r\n let matchIndex = 0;\r\n segments.forEach((segment, i) => {\r\n if (segment) newParts.push({ text: segment });\r\n if (i < segments.length - 1 && matches[matchIndex]) {\r\n newParts.push({ text: matches[matchIndex], className: pattern.class });\r\n matchIndex++;\r\n }\r\n });\r\n });\r\n parts = newParts;\r\n });\r\n\r\n return parts.map((part, i) =>\r\n part.className ? <span key={i} className={part.className}>{part.text}</span> : part.text\r\n );\r\n};\r\n\r\nexport default function OptimizeClient() {\r\n const [resumeText, setResumeText] = useState('');\r\n const [jobDescription, setJobDescription] = useState('');\r\n const [fileName, setFileName] = useState('');\r\n const [isOptimizing, setIsOptimizing] = useState(false);\r\n const [optimizedData, setOptimizedData] = useState<any>(null);\r\n const [isSaving, setIsSaving] = useState(false);\r\n const [isDownloading, setIsDownloading] = useState<'PDF' | 'DOCX' | null>(null);\r\n const [isExtractingResume, setIsExtractingResume] = useState(false);\r\n const [isExtractingJD, setIsExtractingJD] = useState(false);\r\n const [resumeFile, setResumeFile] = useState<File | null>(null);\r\n const [jdFile, setJdFile] = useState<File | null>(null);\r\n const { user } = useUser();\r\n const { showToast } = useToast();\r\n const router = useRouter();\r\n\r\n const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>, type: 'resume' | 'jd') => {\r\n const file = e.target.files?.[0];\r\n if (!file) return;\r\n\r\n if (type === 'resume') {\r\n setIsExtractingResume(true);\r\n setResumeFile(file);\r\n setFileName(file.name.split('.')[0]);\r\n } else {\r\n setIsExtractingJD(true);\r\n setJdFile(file);\r\n }\r\n\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n\r\n try {\r\n const result = await extractText(formData);\r\n if (result.success && result.text) {\r\n if (type === 'resume') {\r\n setResumeText(result.text);\r\n showToast('Resume text extracted successfully!', 'success');\r\n } else {\r\n setJobDescription(result.text);\r\n showToast('Job description extracted successfully!', 'success');\r\n }\r\n } else {\r\n showToast(result.error || 'Failed to extract text.', 'error');\r\n }\r\n } catch (error) {\r\n showToast('Something went wrong during extraction.', 'error');\r\n } finally {\r\n type === 'resume' ? setIsExtractingResume(false) : setIsExtractingJD(false);\r\n }\r\n };\r\n\r\n // Load effects\r\n useEffect(() => {\r\n // Ready\r\n }, []);\r\n\r\n const handleOptimize = async () => {\r\n if (!resumeText.trim() || !jobDescription.trim()) {\r\n showToast('Please provide both resume text and job description.', 'error');\r\n return;\r\n }\r\n\r\n setIsOptimizing(true);\r\n try {\r\n const result = await optimizeResume(resumeText, jobDescription);\r\n if (result.success) {\r\n setOptimizedData(result.data);\r\n showToast('Resume optimized successfully! ≡ƒÄë', 'success', 5000);\r\n } else {\r\n showToast('Optimization failed. Please try again.', 'error', 5000);\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n showToast('Something went wrong. Please try again.', 'error', 5000);\r\n } finally {\r\n setIsOptimizing(false);\r\n }\r\n };\r\n\r\n const handleDownload = async (format: 'PDF' | 'DOCX') => {\r\n if (!optimizedData) return;\r\n setIsDownloading(format);\r\n\r\n try {\r\n if (format === 'PDF') {\r\n const blob = await pdf(<ResumePDF data={optimizedData} />).toBlob();\r\n saveAs(blob, `Optimized_${fileName || 'Resume'}.pdf`);\r\n } else {\r\n const blob = await generateDocx(optimizedData);\r\n saveAs(blob, `Optimized_${fileName || 'Resume'}.docx`);\r\n }\r\n showToast(`Downloaded ${format} successfully!`, 'success');\r\n } catch (error) {\r\n console.error('Download error:', error);\r\n showToast(`Failed to download ${format}.`, 'error');\r\n } finally {\r\n setIsDownloading(null);\r\n }\r\n };\r\n\r\n const handleSave = async () => {\r\n if (!optimizedData || !user) {\r\n showToast('Please sign in to save resumes.', 'error');\r\n return;\r\n }\r\n\r\n setIsSaving(true);\r\n try {\r\n const result = await saveResume({\r\n userId: user.id,\r\n fileName: `Optimized_${fileName || 'Resume'}`,\r\n jobTitle: optimizedData.experience?.[0]?.role || \"Optimized Role\",\r\n matchScore: 100,\r\n fileType: \"PDF\",\r\n content: optimizedData,\r\n });\r\n\r\n if (result.success) {\r\n showToast('Resume saved to your library! ≡ƒÄë', 'success');\r\n router.push('/dashboard');\r\n } else {\r\n showToast('Failed to save resume.', 'error');\r\n }\r\n } catch (error) {\r\n showToast('Failed to save resume.', 'error');\r\n } finally {\r\n setIsSaving(false);\r\n }\r\n };\r\n\r\n return (\r\n <main className=\"p-6 md:p-12\">\r\n <div className=\"max-w-7xl mx-auto\">\r\n {/* Header */}\r\n <motion.div\r\n initial={{ opacity: 0, y: -20 }}\r\n animate={{ opacity: 1, y: 0 }}\r\n className=\"mb-8\"\r\n >\r\n <Link href=\"/\" className=\"flex items-center gap-2 text-slate-400 hover:text-white transition-colors mb-4\">\r\n <ArrowLeft className=\"h-4 w-4\" /> Back to Analyzer\r\n </Link>\r\n <div className=\"flex items-center gap-3\">\r\n <div className=\"p-3 bg-gradient-to-br from-amber-500/20 to-orange-500/20 rounded-xl border border-amber-500/30\">\r\n <Sparkles className=\"h-8 w-8 text-amber-400\" />\r\n </div>\r\n <div>\r\n <h1 className=\"text-3xl font-bold font-display bg-gradient-to-r from-amber-300 to-orange-300 bg-clip-text text-transparent\">\r\n AI Resume Optimizer\r\n </h1>\r\n <p className=\"text-slate-400\">Target 100% match score with AI-powered optimization</p>\r\n </div>\r\n </div>\r\n </motion.div>\r\n\r\n <AnimatePresence mode=\"wait\">\r\n {!optimizedData ? (\r\n <motion.div\r\n key=\"input\"\r\n initial={{ opacity: 0, y: 20 }}\r\n animate={{ opacity: 1, y: 0 }}\r\n exit={{ opacity: 0, y: -20 }}\r\n className=\"grid md:grid-cols-2 gap-6\"\r\n >\r\n {/* Resume Input */}\r\n <Card className=\"bg-slate-900/50 border-slate-800\">\r\n <CardContent className=\"p-6 space-y-4\">\r\n <div className=\"flex items-center justify-between mb-2\">\r\n <label className=\"flex items-center gap-2 text-lg font-bold text-teal-400\">\r\n <FileText className=\"h-5 w-5\" />\r\n Your Resume Content\r\n </label>\r\n <div className=\"relative\">\r\n <input\r\n type=\"file\"\r\n accept=\".pdf,.docx\"\r\n onChange={(e) => handleFileUpload(e, 'resume')}\r\n className=\"absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10\"\r\n disabled={isExtractingResume}\r\n />\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n className=\"gap-2 border-teal-500/30 text-teal-400 hover:bg-teal-500/10 cursor-pointer\"\r\n disabled={isExtractingResume}\r\n >\r\n {isExtractingResume ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Upload className=\"h-4 w-4\" />}\r\n {resumeFile ? 'Change File' : 'Upload PDF/Word'}\r\n </Button>\r\n </div>\r\n </div>\r\n <div className=\"relative\">\r\n <Textarea\r\n placeholder=\"Paste your resume text here... or upload a file above.\"\r\n className=\"min-h-[400px] bg-slate-900 border-slate-700 text-slate-200 resize-none\"\r\n value={resumeText}\r\n onChange={(e) => setResumeText(e.target.value)}\r\n disabled={isExtractingResume}\r\n />\r\n {isExtractingResume && (\r\n <div className=\"absolute inset-0 bg-slate-950/40 backdrop-blur-[2px] rounded-md flex items-center justify-center\">\r\n <div className=\"flex flex-col items-center gap-2\">\r\n <Loader2 className=\"h-8 w-8 text-teal-500 animate-spin\" />\r\n <p className=\"text-teal-400 text-sm font-medium\">Extracting text...</p>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n <p className=\"text-xs text-slate-500\">{resumeText.length} characters</p>\r\n </CardContent>\r\n </Card>\r\n\r\n {/* Job Description Input */}\r\n <Card className=\"bg-slate-900/50 border-slate-800\">\r\n <CardContent className=\"p-6 space-y-4\">\r\n <div className=\"flex items-center justify-between mb-2\">\r\n <label className=\"flex items-center gap-2 text-lg font-bold text-purple-400\">\r\n <Briefcase className=\"h-5 w-5\" />\r\n Target Job Description\r\n </label>\r\n <div className=\"relative\">\r\n <input\r\n type=\"file\"\r\n accept=\".pdf,.docx,image/*\"\r\n onChange={(e) => handleFileUpload(e, 'jd')}\r\n className=\"absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10\"\r\n disabled={isExtractingJD}\r\n />\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n className=\"gap-2 border-purple-500/30 text-purple-400 hover:bg-purple-500/10 cursor-pointer\"\r\n disabled={isExtractingJD}\r\n >\r\n {isExtractingJD ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Upload className=\"h-4 w-4\" />}\r\n {jdFile ? 'Change File' : 'Upload File/Image'}\r\n </Button>\r\n </div>\r\n </div>\r\n <div className=\"relative\">\r\n <Textarea\r\n placeholder=\"Paste the job description here... or upload a file/image above.\"\r\n className=\"min-h-[400px] bg-slate-900 border-slate-700 text-slate-200 resize-none\"\r\n value={jobDescription}\r\n onChange={(e) => setJobDescription(e.target.value)}\r\n disabled={isExtractingJD}\r\n />\r\n {isExtractingJD && (\r\n <div className=\"absolute inset-0 bg-slate-950/40 backdrop-blur-[2px] rounded-md flex items-center justify-center\">\r\n <div className=\"flex flex-col items-center gap-2\">\r\n <Loader2 className=\"h-8 w-8 text-purple-500 animate-spin\" />\r\n <p className=\"text-purple-400 text-sm font-medium\">Parsing job details...</p>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n <p className=\"text-xs text-slate-500\">{jobDescription.length} characters</p>\r\n </CardContent>\r\n </Card>\r\n\r\n {/* Optimize Button */}\r\n <div className=\"md:col-span-2 flex justify-center\">\r\n <motion.button\r\n onClick={handleOptimize}\r\n disabled={isOptimizing || !resumeText.trim() || !jobDescription.trim()}\r\n whileHover={{ scale: 1.02 }}\r\n whileTap={{ scale: 0.98 }}\r\n className=\"relative overflow-hidden px-12 py-5 rounded-xl font-bold text-xl\r\n bg-gradient-to-r from-amber-500 via-orange-500 to-red-500 text-white\r\n shadow-xl shadow-orange-500/30 border-2 border-amber-400/30\r\n disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer\"\r\n >\r\n <motion.div\r\n className=\"absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent\"\r\n animate={{ x: ['-100%', '200%'] }}\r\n transition={{ duration: 1.5, repeat: Infinity }}\r\n />\r\n <span className=\"relative flex items-center gap-3\">\r\n {isOptimizing ? (\r\n <>\r\n <Loader2 className=\"h-6 w-6 animate-spin\" />\r\n Optimizing Your Resume...\r\n </>\r\n ) : (\r\n <>\r\n <Sparkles className=\"h-6 w-6\" />\r\n Optimize for 100% Match\r\n </>\r\n )}\r\n </span>\r\n </motion.button>\r\n </div>\r\n </motion.div>\r\n ) : (\r\n <motion.div\r\n key=\"results\"\r\n initial={{ opacity: 0, y: 20 }}\r\n animate={{ opacity: 1, y: 0 }}\r\n className=\"space-y-6\"\r\n >\r\n {/* Success Banner */}\r\n <motion.div\r\n initial={{ scale: 0.9 }}\r\n animate={{ scale: 1 }}\r\n className=\"bg-gradient-to-r from-emerald-950/50 via-emerald-900/30 to-teal-950/50 \r\n border-2 border-emerald-500/30 p-6 rounded-2xl flex items-center justify-between\r\n shadow-lg shadow-emerald-500/10\"\r\n >\r\n <div className=\"flex items-center gap-4\">\r\n <CheckCircle2 className=\"h-12 w-12 text-emerald-400\" />\r\n <div>\r\n <h4 className=\"font-bold text-2xl text-emerald-300\">≡ƒÄë Optimization Complete!</h4>\r\n <p className=\"text-emerald-400/80\">Your resume is now targeting a <span className=\"font-bold text-emerald-300\">100% Match Score</span></p>\r\n </div>\r\n </div>\r\n <div className=\"flex gap-3\">\r\n <Button\r\n onClick={() => handleDownload('PDF')}\r\n disabled={isDownloading !== null}\r\n className=\"bg-gradient-to-r from-red-600 to-rose-600 hover:from-red-500 hover:to-rose-500\"\r\n >\r\n {isDownloading === 'PDF' ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <FileText className=\"h-4 w-4\" />}\r\n PDF\r\n </Button>\r\n <Button\r\n onClick={() => handleDownload('DOCX')}\r\n disabled={isDownloading !== null}\r\n className=\"bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-500 hover:to-indigo-500\"\r\n >\r\n {isDownloading === 'DOCX' ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <FileType2 className=\"h-4 w-4\" />}\r\n Word\r\n </Button>\r\n <Button\r\n onClick={handleSave}\r\n disabled={isSaving}\r\n className=\"bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-500 hover:to-teal-500\"\r\n >\r\n {isSaving ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Save className=\"h-4 w-4\" />}\r\n Save\r\n </Button>\r\n </div>\r\n </motion.div>\r\n\r\n {/* Content Grid */}\r\n <div className=\"grid md:grid-cols-2 gap-6\">\r\n {/* Professional Summary */}\r\n <Card className=\"bg-slate-900/50 border-slate-800\">\r\n <CardContent className=\"p-6 space-y-4\">\r\n <label className=\"flex items-center gap-2 text-lg font-bold text-amber-400\">\r\n <Sparkles className=\"h-5 w-5\" />\r\n Professional Summary\r\n </label>\r\n <div className=\"text-slate-300 leading-relaxed max-h-64 overflow-y-auto custom-scrollbar\">\r\n {highlightSummary(optimizedData.professionalSummary)}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n\r\n {/* Skills */}\r\n <Card className=\"bg-slate-900/50 border-slate-800\">\r\n <CardContent className=\"p-6 space-y-4\">\r\n <label className=\"flex items-center gap-2 text-lg font-bold text-cyan-400\">\r\n <CheckCircle2 className=\"h-5 w-5\" />\r\n Skills ({optimizedData.skills?.length || 0})\r\n </label>\r\n <div className=\"flex flex-wrap gap-2 max-h-64 overflow-y-auto custom-scrollbar\">\r\n {optimizedData.skills?.map((skill: string, i: number) => {\r\n const color = skillColors[i % skillColors.length];\r\n return (\r\n <span\r\n key={i}\r\n className={`bg-gradient-to-r ${color.bg} ${color.text} px-3 py-1.5 rounded-lg text-sm font-medium border ${color.border}`}\r\n >\r\n {skill}\r\n </span>\r\n );\r\n })}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n </div>\r\n\r\n {/* Experience */}\r\n {optimizedData.experience && (\r\n <Card className=\"bg-slate-900/50 border-slate-800\">\r\n <CardContent className=\"p-6 space-y-6\">\r\n <label className=\"flex items-center gap-2 text-lg font-bold text-purple-400\">\r\n <Briefcase className=\"h-5 w-5\" />\r\n Experience\r\n </label>\r\n <div className=\"space-y-6 max-h-96 overflow-y-auto custom-scrollbar\">\r\n {optimizedData.experience.map((exp: any, i: number) => (\r\n <div key={i} className=\"border-l-2 border-purple-500/30 pl-4\">\r\n <div className=\"flex justify-between items-start mb-2\">\r\n <div>\r\n <h4 className=\"font-bold text-lg text-slate-200\">{exp.role}</h4>\r\n <p className=\"text-purple-400\">{exp.company}</p>\r\n </div>\r\n <span className=\"text-slate-500 text-sm\">{exp.date}</span>\r\n </div>\r\n <ul className=\"space-y-2 mt-3\">\r\n {exp.description?.map((bullet: string, j: number) => (\r\n <li key={j} className=\"text-slate-300 text-sm flex gap-2\">\r\n <span className=\"text-purple-400\">Γû╕</span>\r\n {bullet}\r\n </li>\r\n ))}\r\n </ul>\r\n </div>\r\n ))}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n )}\r\n\r\n {/* Start Over Button */}\r\n <div className=\"flex justify-center pt-4\">\r\n <Button\r\n variant=\"outline\"\r\n onClick={() => setOptimizedData(null)}\r\n className=\"border-slate-700 text-slate-400 hover:bg-slate-800\"\r\n >\r\n <ArrowLeft className=\"h-4 w-4 mr-2\" />\r\n Start New Optimization\r\n </Button>\r\n </div>\r\n </motion.div>\r\n )}\r\n </AnimatePresence>\r\n </div>\r\n </main>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ResumeDetailClient.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'Download' is defined but never used.","line":4,"column":21,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":29,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"Download"},"fix":{"range":[76,86],"text":""},"desc":"Remove unused variable \"Download\"."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":181,"column":67,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":181,"endColumn":70,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[9791,9794],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[9791,9794],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":220,"column":65,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":220,"endColumn":68,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[12123,12126],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[12123,12126],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { motion } from 'framer-motion';\r\nimport { ArrowLeft, Download, FileText, Calendar, Award, Sparkles, Briefcase, GraduationCap, Code, Trophy, Globe } from 'lucide-react';\r\nimport Link from 'next/link';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Card, CardContent } from '@/components/ui/card';\r\nimport { generateDocx } from '@/lib/docx-generator';\r\nimport { saveAs } from 'file-saver';\r\nimport { pdf } from '@react-pdf/renderer';\r\nimport { ResumePDF } from './ResumePDF';\r\nimport { useState } from 'react';\r\n\r\n// Skill color palette\r\nconst skillColors = [\r\n 'from-rose-900/50 to-pink-900/50 text-rose-300 border-rose-500/30',\r\n 'from-amber-900/50 to-orange-900/50 text-amber-300 border-amber-500/30',\r\n 'from-emerald-900/50 to-teal-900/50 text-emerald-300 border-emerald-500/30',\r\n 'from-cyan-900/50 to-blue-900/50 text-cyan-300 border-cyan-500/30',\r\n 'from-violet-900/50 to-purple-900/50 text-violet-300 border-violet-500/30',\r\n 'from-fuchsia-900/50 to-pink-900/50 text-fuchsia-300 border-fuchsia-500/30',\r\n 'from-sky-900/50 to-indigo-900/50 text-sky-300 border-sky-500/30',\r\n 'from-lime-900/50 to-green-900/50 text-lime-300 border-lime-500/30',\r\n];\r\n\r\ninterface Resume {\r\n id: string;\r\n fileName: string;\r\n jobTitle: string | null;\r\n matchScore: number | null;\r\n content: string | null;\r\n createdAt: Date;\r\n}\r\n\r\nexport default function ResumeDetailClient({ resume }: { resume: Resume }) {\r\n const [isDownloading, setIsDownloading] = useState<'PDF' | 'DOCX' | null>(null);\r\n const data = resume.content ? JSON.parse(resume.content) : {};\r\n\r\n const handleDownload = async (format: 'PDF' | 'DOCX') => {\r\n setIsDownloading(format);\r\n try {\r\n const fileName = resume.fileName.replace(/\\.[^/.]+$/, \"\");\r\n if (format === 'PDF') {\r\n const blob = await pdf(<ResumePDF data={data} />).toBlob();\r\n saveAs(blob, `${fileName}.pdf`);\r\n } else {\r\n const blob = await generateDocx(data);\r\n saveAs(blob, `${fileName}.docx`);\r\n }\r\n } catch (error) {\r\n console.error('Download error:', error);\r\n } finally {\r\n setIsDownloading(null);\r\n }\r\n };\r\n\r\n return (\r\n <main className=\"min-h-screen bg-slate-950 text-slate-50 p-6 md:p-12\">\r\n <div className=\"max-w-6xl mx-auto space-y-8\">\r\n {/* Header */}\r\n <motion.div\r\n initial={{ opacity: 0, y: -20 }}\r\n animate={{ opacity: 1, y: 0 }}\r\n className=\"flex flex-col md:flex-row md:items-center justify-between gap-4\"\r\n >\r\n <div>\r\n <Link href=\"/dashboard\" className=\"flex items-center gap-2 text-slate-400 hover:text-white transition-colors mb-2\">\r\n <ArrowLeft className=\"h-4 w-4\" /> Back to Library\r\n </Link>\r\n <h1 className=\"text-3xl font-bold font-display flex items-center gap-3\">\r\n <FileText className=\"h-8 w-8 text-teal-400\" />\r\n {resume.fileName}\r\n </h1>\r\n <div className=\"flex items-center gap-4 mt-2 text-slate-400 text-sm\">\r\n <span className=\"flex items-center gap-1\">\r\n <Calendar className=\"h-4 w-4\" />\r\n {new Date(resume.createdAt).toLocaleDateString('en-US', {\r\n year: 'numeric',\r\n month: 'long',\r\n day: 'numeric'\r\n })}\r\n </span>\r\n <span className=\"flex items-center gap-1\">\r\n <Award className=\"h-4 w-4 text-emerald-400\" />\r\n <span className=\"text-emerald-400 font-bold\">{resume.matchScore || 0}% Match</span>\r\n </span>\r\n </div>\r\n </div>\r\n\r\n {/* Download Buttons */}\r\n <div className=\"flex gap-3\">\r\n <Button\r\n onClick={() => handleDownload('PDF')}\r\n disabled={isDownloading !== null}\r\n className=\"bg-gradient-to-r from-red-600 to-rose-600 hover:from-red-500 hover:to-rose-500 text-white\"\r\n >\r\n {isDownloading === 'PDF' ? 'Downloading...' : 'Download PDF'}\r\n </Button>\r\n <Button\r\n onClick={() => handleDownload('DOCX')}\r\n disabled={isDownloading !== null}\r\n className=\"bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-500 hover:to-indigo-500 text-white\"\r\n >\r\n {isDownloading === 'DOCX' ? 'Downloading...' : 'Download Word'}\r\n </Button>\r\n </div>\r\n </motion.div>\r\n\r\n {/* Content Grid */}\r\n <div className=\"grid md:grid-cols-3 gap-6\">\r\n {/* Left Column - Contact & Skills */}\r\n <motion.div\r\n initial={{ opacity: 0, x: -20 }}\r\n animate={{ opacity: 1, x: 0 }}\r\n transition={{ delay: 0.1 }}\r\n className=\"space-y-6\"\r\n >\r\n {/* Contact Info */}\r\n <Card className=\"bg-slate-900/50 border-slate-800\">\r\n <CardContent className=\"p-6 space-y-4\">\r\n <h3 className=\"text-lg font-bold text-teal-400 flex items-center gap-2\">\r\n <Globe className=\"h-5 w-5\" /> Contact Info\r\n </h3>\r\n <div className=\"space-y-3 text-sm\">\r\n {data.contactInfo?.email && (\r\n <div>\r\n <span className=\"text-slate-500\">Email</span>\r\n <p className=\"text-cyan-400\">{data.contactInfo.email}</p>\r\n </div>\r\n )}\r\n {data.contactInfo?.phone && (\r\n <div>\r\n <span className=\"text-slate-500\">Phone</span>\r\n <p className=\"text-slate-200\">{data.contactInfo.phone}</p>\r\n </div>\r\n )}\r\n {data.contactInfo?.linkedin && (\r\n <div>\r\n <span className=\"text-slate-500\">LinkedIn</span>\r\n <p className=\"text-cyan-400 truncate\">{data.contactInfo.linkedin}</p>\r\n </div>\r\n )}\r\n {data.contactInfo?.location && (\r\n <div>\r\n <span className=\"text-slate-500\">Location</span>\r\n <p className=\"text-slate-200\">{data.contactInfo.location}</p>\r\n </div>\r\n )}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n\r\n {/* Skills */}\r\n <Card className=\"bg-slate-900/50 border-slate-800\">\r\n <CardContent className=\"p-6 space-y-4\">\r\n <h3 className=\"text-lg font-bold text-cyan-400 flex items-center gap-2\">\r\n <Code className=\"h-5 w-5\" /> Skills ({data.skills?.length || 0})\r\n </h3>\r\n <div className=\"flex flex-wrap gap-2 max-h-64 overflow-y-auto custom-scrollbar\">\r\n {data.skills?.map((skill: string, i: number) => (\r\n <span\r\n key={i}\r\n className={`bg-gradient-to-r ${skillColors[i % skillColors.length]} \r\n px-3 py-1.5 rounded-lg text-xs font-medium border`}\r\n >\r\n {skill}\r\n </span>\r\n ))}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n\r\n {/* Education */}\r\n {data.education && data.education.length > 0 && (\r\n <Card className=\"bg-slate-900/50 border-slate-800\">\r\n <CardContent className=\"p-6 space-y-4\">\r\n <h3 className=\"text-lg font-bold text-purple-400 flex items-center gap-2\">\r\n <GraduationCap className=\"h-5 w-5\" /> Education\r\n </h3>\r\n <div className=\"space-y-4\">\r\n {data.education.map((edu: any, i: number) => (\r\n <div key={i} className=\"border-l-2 border-purple-500/30 pl-4\">\r\n <h4 className=\"font-bold text-slate-200\">{edu.degree}</h4>\r\n <p className=\"text-purple-400 text-sm\">{edu.institution}</p>\r\n <p className=\"text-slate-500 text-xs\">{edu.date}</p>\r\n </div>\r\n ))}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n )}\r\n </motion.div>\r\n\r\n {/* Right Column - Summary & Experience */}\r\n <motion.div\r\n initial={{ opacity: 0, x: 20 }}\r\n animate={{ opacity: 1, x: 0 }}\r\n transition={{ delay: 0.2 }}\r\n className=\"md:col-span-2 space-y-6\"\r\n >\r\n {/* Professional Summary */}\r\n <Card className=\"bg-slate-900/50 border-slate-800\">\r\n <CardContent className=\"p-6 space-y-4\">\r\n <h3 className=\"text-lg font-bold text-amber-400 flex items-center gap-2\">\r\n <Sparkles className=\"h-5 w-5\" /> Professional Summary\r\n </h3>\r\n <p className=\"text-slate-300 leading-relaxed\">\r\n {data.professionalSummary}\r\n </p>\r\n </CardContent>\r\n </Card>\r\n\r\n {/* Experience */}\r\n <Card className=\"bg-slate-900/50 border-slate-800\">\r\n <CardContent className=\"p-6 space-y-6\">\r\n <h3 className=\"text-lg font-bold text-emerald-400 flex items-center gap-2\">\r\n <Briefcase className=\"h-5 w-5\" /> Experience\r\n </h3>\r\n <div className=\"space-y-6\">\r\n {data.experience?.map((exp: any, i: number) => (\r\n <div key={i} className=\"border-l-2 border-emerald-500/30 pl-4\">\r\n <div className=\"flex justify-between items-start mb-2\">\r\n <div>\r\n <h4 className=\"font-bold text-lg text-slate-200\">{exp.role}</h4>\r\n <p className=\"text-emerald-400\">{exp.company}</p>\r\n </div>\r\n <span className=\"text-slate-500 text-sm\">{exp.date}</span>\r\n </div>\r\n <ul className=\"space-y-2 mt-3\">\r\n {exp.description?.map((bullet: string, j: number) => (\r\n <li key={j} className=\"text-slate-300 text-sm flex gap-2\">\r\n <span className=\"text-emerald-400\">Γû╕</span>\r\n {bullet}\r\n </li>\r\n ))}\r\n </ul>\r\n </div>\r\n ))}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n\r\n {/* Achievements */}\r\n {data.achievements && data.achievements.length > 0 && (\r\n <Card className=\"bg-slate-900/50 border-slate-800\">\r\n <CardContent className=\"p-6 space-y-4\">\r\n <h3 className=\"text-lg font-bold text-yellow-400 flex items-center gap-2\">\r\n <Trophy className=\"h-5 w-5\" /> Key Achievements\r\n </h3>\r\n <ul className=\"space-y-2\">\r\n {data.achievements.map((achievement: string, i: number) => (\r\n <li key={i} className=\"text-slate-300 flex gap-2\">\r\n <span className=\"text-yellow-400\">Γÿà</span>\r\n {achievement}\r\n </li>\r\n ))}\r\n </ul>\r\n </CardContent>\r\n </Card>\r\n )}\r\n </motion.div>\r\n </div>\r\n </div>\r\n </main>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ResumeOptimizer.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'Button' is defined but never used.","line":4,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":16,"suggestions":[{"messageId":"removeUnusedImportDeclaration","data":{"varName":"Button"},"fix":{"range":[52,100],"text":""},"desc":"Remove unused import declaration."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'Download' is defined but never used.","line":5,"column":43,"nodeType":"Identifier","messageId":"unusedVar","endLine":5,"endColumn":51,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"Download"},"fix":{"range":[142,152],"text":""},"desc":"Remove unused variable \"Download\"."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'OPTIMIZATION_MESSAGES' is assigned a value but never used.","line":32,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":32,"endColumn":28},{"ruleId":"prefer-const","severity":2,"message":"'result' is never reassigned. Use 'const' instead.","line":52,"column":9,"nodeType":"Identifier","messageId":"useConst","endLine":52,"endColumn":15,"fix":{"range":[3542,3560],"text":"const result = text;"}},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":84,"column":56,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":84,"endColumn":59,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[4933,4936],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[4933,4936],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'openFullPageOptimizer' is assigned a value but never used.","line":91,"column":11,"nodeType":"Identifier","messageId":"unusedVar","endLine":91,"endColumn":32},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'e' is defined but never used.","line":151,"column":18,"nodeType":"Identifier","messageId":"unusedVar","endLine":151,"endColumn":19}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":1,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState } from 'react';\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Loader2, Sparkles, CheckCircle2, Download, Save, FileText, FileType2 } from \"lucide-react\";\r\nimport { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter } from \"@/components/ui/dialog\";\r\nimport { optimizeResume } from \"@/app/actions/optimize\";\r\nimport { saveResume } from \"@/app/actions/resume\";\r\nimport { useUser } from \"@clerk/nextjs\";\r\nimport { generateDocx } from '@/lib/docx-generator';\r\nimport { saveAs } from 'file-saver';\r\nimport { pdf } from '@react-pdf/renderer';\r\nimport { ResumePDF } from './ResumePDF';\r\nimport { useToast } from './Toast';\r\nimport { motion, AnimatePresence } from 'framer-motion';\r\nimport LoadingText from './LoadingText';\r\n\r\n// Skill color palette - unique gradients for each skill\r\nconst skillColors = [\r\n { bg: 'from-rose-900/50 to-pink-900/50', text: 'text-rose-300', border: 'border-rose-500/30', hover: 'hover:border-rose-400/50 hover:bg-rose-800/40' },\r\n { bg: 'from-amber-900/50 to-orange-900/50', text: 'text-amber-300', border: 'border-amber-500/30', hover: 'hover:border-amber-400/50 hover:bg-amber-800/40' },\r\n { bg: 'from-emerald-900/50 to-teal-900/50', text: 'text-emerald-300', border: 'border-emerald-500/30', hover: 'hover:border-emerald-400/50 hover:bg-emerald-800/40' },\r\n { bg: 'from-cyan-900/50 to-blue-900/50', text: 'text-cyan-300', border: 'border-cyan-500/30', hover: 'hover:border-cyan-400/50 hover:bg-cyan-800/40' },\r\n { bg: 'from-violet-900/50 to-purple-900/50', text: 'text-violet-300', border: 'border-violet-500/30', hover: 'hover:border-violet-400/50 hover:bg-violet-800/40' },\r\n { bg: 'from-fuchsia-900/50 to-pink-900/50', text: 'text-fuchsia-300', border: 'border-fuchsia-500/30', hover: 'hover:border-fuchsia-400/50 hover:bg-fuchsia-800/40' },\r\n { bg: 'from-sky-900/50 to-indigo-900/50', text: 'text-sky-300', border: 'border-sky-500/30', hover: 'hover:border-sky-400/50 hover:bg-sky-800/40' },\r\n { bg: 'from-lime-900/50 to-green-900/50', text: 'text-lime-300', border: 'border-lime-500/30', hover: 'hover:border-lime-400/50 hover:bg-lime-800/40' },\r\n { bg: 'from-yellow-900/50 to-amber-900/50', text: 'text-yellow-300', border: 'border-yellow-500/30', hover: 'hover:border-yellow-400/50 hover:bg-yellow-800/40' },\r\n { bg: 'from-red-900/50 to-rose-900/50', text: 'text-red-300', border: 'border-red-500/30', hover: 'hover:border-red-400/50 hover:bg-red-800/40' },\r\n];\r\n\r\nconst OPTIMIZATION_MESSAGES = [\r\n \"Rewriting summary for maximum hook...\",\r\n \"Injecting 15+ missing keywords...\",\r\n \"Qualifying achievements with metrics...\",\r\n \"Polishing grammar and tone...\",\r\n \"Finalizing your 100% match score...\"\r\n];\r\n\r\n// Function to highlight keywords in summary\r\nconst highlightSummary = (text: string) => {\r\n if (!text) return null;\r\n\r\n // Patterns to highlight\r\n // Patterns to highlight\r\n const patterns = [\r\n { regex: /(?:\\d+%|\\d+\\+|\\$[\\d,]+|\\d+ years?|\\d+x)/gi, class: 'text-emerald-400 font-bold' }, // Metrics\r\n { regex: /(?:React|Node\\.js|Python|JavaScript|TypeScript|AWS|Docker|MongoDB|PostgreSQL|Next\\.js|Express\\.js|Java|C\\+\\+|Tailwind|REST|API|GraphQL|Kubernetes|CI\\/CD|Git|Agile|Scrum)/gi, class: 'text-cyan-400 font-semibold' }, // Tech\r\n { regex: /(?:Full Stack|Front-end|Backend|Senior|Lead|Manager|Engineer|Developer|Architect|Expert|Proven|Award-winning|Certified)/gi, class: 'text-amber-400 font-semibold' }, // Titles/achievements\r\n ];\r\n\r\n let result = text;\r\n let parts: { text: string; className?: string }[] = [{ text: result }];\r\n\r\n patterns.forEach(pattern => {\r\n const newParts: { text: string; className?: string }[] = [];\r\n parts.forEach(part => {\r\n if (part.className) {\r\n newParts.push(part);\r\n return;\r\n }\r\n const segments = part.text.split(pattern.regex);\r\n const matches = part.text.match(pattern.regex) || [];\r\n let matchIndex = 0;\r\n segments.forEach((segment, i) => {\r\n if (segment) newParts.push({ text: segment });\r\n if (i < segments.length - 1 && matches[matchIndex]) {\r\n newParts.push({ text: matches[matchIndex], className: pattern.class });\r\n matchIndex++;\r\n }\r\n });\r\n });\r\n parts = newParts;\r\n });\r\n\r\n return parts.map((part, i) =>\r\n part.className ? <span key={i} className={part.className}>{part.text}</span> : part.text\r\n );\r\n};\r\n\r\nexport default function ResumeOptimizer({ resumeText, jobDescription, originalFileName }: { resumeText: string, jobDescription: string, originalFileName: string }) {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [isOptimizing, setIsOptimizing] = useState(false);\r\n const [optimizedData, setOptimizedData] = useState<any>(null);\r\n const [isSaving, setIsSaving] = useState(false);\r\n const [isDownloading, setIsDownloading] = useState<'PDF' | 'DOCX' | null>(null);\r\n const { user } = useUser();\r\n const { showToast } = useToast();\r\n\r\n // Function to open full-page optimizer\r\n const openFullPageOptimizer = () => {\r\n window.location.href = '/optimize';\r\n };\r\n\r\n const handleOptimize = async () => {\r\n setIsOptimizing(true);\r\n try {\r\n const result = await optimizeResume(resumeText, jobDescription);\r\n if (result.success) {\r\n setOptimizedData(result.data);\r\n showToast('Resume optimized successfully! ≡ƒÄë', 'success', 5000);\r\n } else {\r\n showToast('Optimization failed. Please try again.', 'error', 5000);\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n showToast('Something went wrong. Please try again.', 'error', 5000);\r\n } finally {\r\n setIsOptimizing(false);\r\n }\r\n };\r\n\r\n const handleDownload = async (format: 'PDF' | 'DOCX') => {\r\n if (!optimizedData) return;\r\n setIsDownloading(format);\r\n\r\n const fileName = `Optimized_${originalFileName.replace(/\\.[^/.]+$/, \"\")}`;\r\n\r\n try {\r\n if (format === 'PDF') {\r\n const blob = await pdf(<ResumePDF data={optimizedData} />).toBlob();\r\n saveAs(blob, `${fileName}.pdf`);\r\n showToast('≡ƒôä PDF downloaded successfully!', 'success', 4000);\r\n } else {\r\n const blob = await generateDocx(optimizedData);\r\n saveAs(blob, `${fileName}.docx`);\r\n showToast('≡ƒô¥ Word document downloaded successfully!', 'success', 4000);\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n showToast('Download failed. Please try again.', 'error', 5000);\r\n } finally {\r\n setIsDownloading(null);\r\n }\r\n };\r\n\r\n const handleSave = async (format: 'PDF' | 'DOCX') => {\r\n if (!user || !optimizedData) return;\r\n setIsSaving(true);\r\n try {\r\n await saveResume({\r\n userId: user.id,\r\n fileName: `Optimized_${originalFileName}`,\r\n jobTitle: optimizedData.experience[0]?.role || \"Unknown Role\",\r\n matchScore: 100,\r\n fileType: format,\r\n content: optimizedData\r\n });\r\n showToast('Γ£¿ Saved to Dashboard! Your optimized resume is safe in the cloud.', 'success', 6000);\r\n setIsOpen(false);\r\n } catch (e) {\r\n showToast('Failed to save. Please try again.', 'error', 5000);\r\n } finally {\r\n setIsSaving(false);\r\n }\r\n }\r\n\r\n return (\r\n <Dialog open={isOpen} onOpenChange={setIsOpen}>\r\n <DialogTrigger asChild>\r\n {/* Premium Animated Button */}\r\n <motion.button\r\n whileHover={{ scale: 1.05, boxShadow: \"0 0 30px rgba(245,158,11,0.5)\" }}\r\n whileTap={{ scale: 0.95 }}\r\n className=\"relative overflow-hidden cursor-pointer px-6 py-3 rounded-xl font-bold text-white shadow-xl \r\n bg-gradient-to-r from-amber-500 via-orange-500 to-red-500 \r\n border-2 border-amber-400/30 group\"\r\n >\r\n {/* Animated background shimmer */}\r\n <motion.div\r\n className=\"absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent\"\r\n animate={{ x: ['-100%', '200%'] }}\r\n transition={{ duration: 2, repeat: Infinity, ease: 'linear' }}\r\n />\r\n\r\n {/* Sparkle particles */}\r\n <div className=\"absolute inset-0 overflow-hidden\">\r\n {[...Array(3)].map((_, i) => (\r\n <motion.div\r\n key={i}\r\n className=\"absolute w-1 h-1 bg-white rounded-full\"\r\n animate={{\r\n y: [40, -10],\r\n x: [20 + i * 30, 30 + i * 25],\r\n opacity: [0, 1, 0],\r\n }}\r\n transition={{\r\n duration: 1.5,\r\n repeat: Infinity,\r\n delay: i * 0.3,\r\n }}\r\n />\r\n ))}\r\n </div>\r\n\r\n <span className=\"relative flex items-center gap-2\">\r\n <Sparkles className=\"h-5 w-5 animate-pulse\" />\r\n <span className=\"text-sm sm:text-base\">AI Optimize (100% Match)</span>\r\n </span>\r\n </motion.button>\r\n </DialogTrigger>\r\n\r\n <DialogContent\r\n suppressHydrationWarning\r\n className=\"max-w-4xl max-h-[90vh] overflow-y-auto bg-slate-900 border-2 border-amber-500/30 text-slate-100 shadow-2xl\"\r\n style={{ backgroundColor: '#0f172a' }}\r\n >\r\n <DialogHeader>\r\n <DialogTitle className=\"text-2xl sm:text-3xl font-display flex items-center gap-3\">\r\n <motion.div\r\n animate={{ rotate: [0, 15, -15, 0] }}\r\n transition={{ duration: 2, repeat: Infinity }}\r\n >\r\n <Sparkles className=\"text-amber-400 h-8 w-8\" />\r\n </motion.div>\r\n <span className=\"bg-gradient-to-r from-amber-400 via-orange-400 to-red-400 bg-clip-text text-transparent\">\r\n AI Resume Optimizer\r\n </span>\r\n </DialogTitle>\r\n </DialogHeader>\r\n\r\n <AnimatePresence mode=\"wait\">\r\n {!optimizedData ? (\r\n <motion.div\r\n key=\"optimize-prompt\"\r\n initial={{ opacity: 0, y: 20 }}\r\n animate={{ opacity: 1, y: 0 }}\r\n exit={{ opacity: 0, y: -20 }}\r\n className=\"flex flex-col items-center justify-center py-12 space-y-8 text-center\"\r\n >\r\n <motion.div\r\n className=\"relative\"\r\n animate={{ scale: isOptimizing ? [1, 1.1, 1] : 1 }}\r\n transition={{ duration: 1, repeat: isOptimizing ? Infinity : 0 }}\r\n >\r\n <div className=\"absolute inset-0 bg-gradient-to-r from-amber-500 via-orange-500 to-red-500 blur-2xl opacity-30 rounded-full\" />\r\n <div className=\"relative bg-gradient-to-br from-amber-500/20 to-orange-500/20 p-8 rounded-full border border-amber-500/30\">\r\n <Sparkles className={`h-16 w-16 text-amber-400 ${isOptimizing ? 'animate-spin' : 'animate-pulse'}`} />\r\n </div>\r\n </motion.div>\r\n\r\n <div className=\"space-y-3\">\r\n <h3 className=\"text-2xl font-bold bg-gradient-to-r from-amber-300 to-orange-300 bg-clip-text text-transparent\">\r\n Ready to Supercharge Your Resume?\r\n </h3>\r\n <p className=\"text-slate-400 max-w-lg mx-auto leading-relaxed\">\r\n Our AI will analyze the job description and completely rewrite your resume to include\r\n <span className=\"text-amber-400 font-semibold\"> every missing keyword</span>,\r\n optimize for ATS systems, and target a\r\n <span className=\"text-emerald-400 font-bold\"> 100% Match Score</span>.\r\n </p>\r\n </div>\r\n\r\n <motion.button\r\n onClick={handleOptimize}\r\n disabled={isOptimizing}\r\n whileHover={{ scale: 1.05 }}\r\n whileTap={{ scale: 0.95 }}\r\n className=\"cursor-pointer relative overflow-hidden px-8 py-4 rounded-xl font-bold text-lg\r\n bg-gradient-to-r from-amber-500 via-orange-500 to-red-500 text-white\r\n shadow-xl shadow-orange-500/30 border-2 border-amber-400/30\r\n disabled:opacity-50 disabled:cursor-not-allowed\"\r\n >\r\n <motion.div\r\n className=\"absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent\"\r\n animate={{ x: ['-100%', '200%'] }}\r\n transition={{ duration: 1.5, repeat: Infinity }}\r\n />\r\n <span className=\"relative flex items-center gap-2\">\r\n {isOptimizing ? (\r\n <>\r\n <Loader2 className=\"h-5 w-5 animate-spin\" />\r\n <div className=\"flex flex-col items-start text-left\">\r\n <span>Optimizing Your Resume...</span>\r\n <LoadingText\r\n messages={[\r\n \"Rewriting summary for maximum hook...\",\r\n \"Injecting 15+ missing keywords...\",\r\n \"Qualifying achievements with metrics...\",\r\n \"Polishing grammar and tone...\",\r\n \"Finalizing your 100% match score...\"\r\n ]}\r\n className=\"text-xs font-normal text-white/70 h-4\"\r\n />\r\n </div>\r\n </>\r\n ) : (\r\n <>\r\n <Sparkles className=\"h-5 w-5\" />\r\n Start Optimization\r\n </>\r\n )}\r\n </span>\r\n </motion.button>\r\n </motion.div>\r\n ) : (\r\n <motion.div\r\n key=\"results\"\r\n initial={{ opacity: 0, y: 20 }}\r\n animate={{ opacity: 1, y: 0 }}\r\n className=\"space-y-6\"\r\n >\r\n {/* Success Banner */}\r\n <motion.div\r\n initial={{ scale: 0.9 }}\r\n animate={{ scale: 1 }}\r\n className=\"bg-gradient-to-r from-emerald-950/50 via-emerald-900/30 to-teal-950/50 \r\n border-2 border-emerald-500/30 p-5 rounded-2xl flex items-center gap-4\r\n shadow-lg shadow-emerald-500/10\"\r\n >\r\n <motion.div\r\n animate={{ scale: [1, 1.2, 1] }}\r\n transition={{ duration: 0.5, repeat: 3 }}\r\n >\r\n <CheckCircle2 className=\"h-10 w-10 text-emerald-400\" />\r\n </motion.div>\r\n <div>\r\n <h4 className=\"font-bold text-xl text-emerald-300\">≡ƒÄë Optimization Complete!</h4>\r\n <p className=\"text-emerald-400/80\">Your resume is now targeting a <span className=\"font-bold text-emerald-300\">100% Match Score</span></p>\r\n </div>\r\n </motion.div>\r\n\r\n {/* Content Preview Grid - Same Height */}\r\n <div className=\"grid md:grid-cols-2 gap-6\">\r\n {/* Professional Summary */}\r\n <motion.div\r\n initial={{ opacity: 0, x: -20 }}\r\n animate={{ opacity: 1, x: 0 }}\r\n transition={{ delay: 0.1 }}\r\n className=\"space-y-3 flex flex-col\"\r\n >\r\n <label className=\"flex items-center gap-2 text-sm font-bold text-amber-400 uppercase tracking-wider\">\r\n <div className=\"w-2 h-2 bg-amber-400 rounded-full animate-pulse\" />\r\n Professional Summary\r\n </label>\r\n <div className=\"flex-1 p-5 bg-gradient-to-br from-slate-900/80 to-slate-800/50 rounded-xl \r\n border-2 border-amber-500/20 text-sm leading-relaxed\r\n hover:border-amber-500/40 transition-colors\r\n max-h-64 overflow-y-auto custom-scrollbar\">\r\n {highlightSummary(optimizedData.professionalSummary)}\r\n </div>\r\n </motion.div>\r\n\r\n {/* Skills - Same height as Summary */}\r\n <motion.div\r\n initial={{ opacity: 0, x: 20 }}\r\n animate={{ opacity: 1, x: 0 }}\r\n transition={{ delay: 0.2 }}\r\n className=\"space-y-3 flex flex-col\"\r\n >\r\n <label className=\"flex items-center gap-2 text-sm font-bold text-cyan-400 uppercase tracking-wider\">\r\n <div className=\"w-2 h-2 bg-cyan-400 rounded-full animate-pulse\" />\r\n Key Skills ({optimizedData.skills?.length || 0})\r\n </label>\r\n <div className=\"flex-1 p-5 bg-gradient-to-br from-slate-900/80 to-slate-800/50 rounded-xl \r\n border-2 border-cyan-500/20 flex flex-wrap gap-2 content-start\r\n hover:border-cyan-500/40 transition-colors max-h-64 overflow-y-auto custom-scrollbar\">\r\n {optimizedData.skills?.map((skill: string, i: number) => {\r\n const colorScheme = skillColors[i % skillColors.length];\r\n return (\r\n <motion.span\r\n key={i}\r\n initial={{ opacity: 0, scale: 0.8 }}\r\n animate={{ opacity: 1, scale: 1 }}\r\n transition={{ delay: 0.3 + i * 0.02 }}\r\n className={`bg-gradient-to-r ${colorScheme.bg} ${colorScheme.text} \r\n px-3 py-1.5 rounded-lg text-xs font-medium\r\n border ${colorScheme.border} ${colorScheme.hover} \r\n transition-all cursor-default`}\r\n >\r\n {skill}\r\n </motion.span>\r\n );\r\n })}\r\n </div>\r\n </motion.div>\r\n </div>\r\n\r\n {/* Experience Preview */}\r\n {optimizedData.experience?.[0] && (\r\n <motion.div\r\n initial={{ opacity: 0, y: 20 }}\r\n animate={{ opacity: 1, y: 0 }}\r\n transition={{ delay: 0.3 }}\r\n className=\"space-y-3\"\r\n >\r\n <label className=\"flex items-center gap-2 text-sm font-bold text-purple-400 uppercase tracking-wider\">\r\n <div className=\"w-2 h-2 bg-purple-400 rounded-full animate-pulse\" />\r\n Experience Preview\r\n </label>\r\n <div className=\"p-5 bg-gradient-to-br from-slate-900/80 to-slate-800/50 rounded-xl \r\n border-2 border-purple-500/20 space-y-3\r\n hover:border-purple-500/40 transition-colors\">\r\n <div className=\"flex justify-between items-start\">\r\n <div>\r\n <h5 className=\"font-bold text-purple-300\">{optimizedData.experience[0].role}</h5>\r\n <p className=\"text-slate-400 text-sm\">{optimizedData.experience[0].company}</p>\r\n </div>\r\n <span className=\"text-xs text-slate-500\">{optimizedData.experience[0].date}</span>\r\n </div>\r\n <ul className=\"space-y-1 text-sm text-slate-300\">\r\n {optimizedData.experience[0].description?.slice(0, 2).map((bullet: string, i: number) => (\r\n <li key={i} className=\"flex gap-2\">\r\n <span className=\"text-purple-400\">Γû╕</span>\r\n {bullet}\r\n </li>\r\n ))}\r\n </ul>\r\n </div>\r\n </motion.div>\r\n )}\r\n\r\n {/* Action Buttons */}\r\n <DialogFooter className=\"flex-col sm:flex-row gap-4 mt-8 border-t-2 border-slate-800 pt-6\">\r\n {/* Download Buttons */}\r\n <div className=\"flex-1 flex gap-3 justify-start\">\r\n <motion.button\r\n onClick={() => handleDownload('PDF')}\r\n disabled={isDownloading === 'PDF'}\r\n whileHover={{ scale: 1.05, y: -2 }}\r\n whileTap={{ scale: 0.95 }}\r\n className=\"cursor-pointer flex items-center gap-2 px-5 py-3 rounded-xl font-semibold\r\n bg-gradient-to-r from-red-600 to-rose-600 text-white\r\n border-2 border-red-400/30 shadow-lg shadow-red-500/20\r\n hover:shadow-red-500/40 transition-all\r\n disabled:opacity-50 disabled:cursor-not-allowed\"\r\n >\r\n {isDownloading === 'PDF' ? (\r\n <Loader2 className=\"h-5 w-5 animate-spin\" />\r\n ) : (\r\n <FileText className=\"h-5 w-5\" />\r\n )}\r\n PDF\r\n </motion.button>\r\n\r\n <motion.button\r\n onClick={() => handleDownload('DOCX')}\r\n disabled={isDownloading === 'DOCX'}\r\n whileHover={{ scale: 1.05, y: -2 }}\r\n whileTap={{ scale: 0.95 }}\r\n className=\"cursor-pointer flex items-center gap-2 px-5 py-3 rounded-xl font-semibold\r\n bg-gradient-to-r from-blue-600 to-indigo-600 text-white\r\n border-2 border-blue-400/30 shadow-lg shadow-blue-500/20\r\n hover:shadow-blue-500/40 transition-all\r\n disabled:opacity-50 disabled:cursor-not-allowed\"\r\n >\r\n {isDownloading === 'DOCX' ? (\r\n <Loader2 className=\"h-5 w-5 animate-spin\" />\r\n ) : (\r\n <FileType2 className=\"h-5 w-5\" />\r\n )}\r\n Word\r\n </motion.button>\r\n </div>\r\n\r\n {/* Save Button */}\r\n {user ? (\r\n <motion.button\r\n onClick={() => handleSave('PDF')}\r\n disabled={isSaving}\r\n whileHover={{ scale: 1.05, y: -2 }}\r\n whileTap={{ scale: 0.95 }}\r\n className=\"cursor-pointer relative overflow-hidden flex items-center gap-2 px-6 py-3 rounded-xl font-bold\r\n bg-gradient-to-r from-emerald-500 via-teal-500 to-cyan-500 text-white\r\n border-2 border-emerald-400/30 shadow-lg shadow-emerald-500/30\r\n hover:shadow-emerald-500/50 transition-all\r\n disabled:opacity-50 disabled:cursor-not-allowed\"\r\n >\r\n <motion.div\r\n className=\"absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent\"\r\n animate={{ x: ['-100%', '200%'] }}\r\n transition={{ duration: 2, repeat: Infinity }}\r\n />\r\n <span className=\"relative flex items-center gap-2\">\r\n {isSaving ? (\r\n <Loader2 className=\"h-5 w-5 animate-spin\" />\r\n ) : (\r\n <Save className=\"h-5 w-5\" />\r\n )}\r\n Save to Dashboard\r\n </span>\r\n </motion.button>\r\n ) : (\r\n <p className=\"text-sm text-slate-500 self-center bg-slate-800/50 px-4 py-2 rounded-lg\">\r\n ≡ƒöÆ Sign in to save\r\n </p>\r\n )}\r\n </DialogFooter>\r\n </motion.div>\r\n )}\r\n </AnimatePresence>\r\n </DialogContent>\r\n </Dialog>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ResumePDF.tsx","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":223,"column":45,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":223,"endColumn":48,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[5390,5393],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[5390,5393],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":293,"column":55,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":293,"endColumn":58,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[9067,9070],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[9067,9070],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":309,"column":58,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":309,"endColumn":61,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[9938,9941],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[9938,9941],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":352,"column":49,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":352,"endColumn":52,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[12210,12213],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[12210,12213],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { Document, Page, Text, View, StyleSheet, Link } from '@react-pdf/renderer';\r\n\r\n// Premium color palette\r\nconst colors = {\r\n primary: '#0f172a', // Deep navy\r\n secondary: '#1e40af', // Royal blue\r\n accent: '#0ea5e9', // Sky blue\r\n highlight: '#f59e0b', // Amber for metrics\r\n success: '#10b981', // Emerald for achievements\r\n text: '#1e293b', // Dark slate\r\n textLight: '#475569', // Medium slate\r\n white: '#ffffff',\r\n};\r\n\r\nconst styles = StyleSheet.create({\r\n page: {\r\n flexDirection: 'row',\r\n backgroundColor: colors.white,\r\n fontFamily: 'Helvetica',\r\n },\r\n // Left Sidebar - Compact\r\n sidebar: {\r\n width: '30%',\r\n backgroundColor: colors.primary,\r\n paddingVertical: 12,\r\n paddingHorizontal: 10,\r\n color: colors.white,\r\n },\r\n // Right Main Content - Compact\r\n main: {\r\n width: '70%',\r\n paddingVertical: 12,\r\n paddingHorizontal: 16,\r\n backgroundColor: colors.white,\r\n },\r\n // Name Section - Compact but visible\r\n name: {\r\n fontSize: 14,\r\n fontWeight: 'bold',\r\n color: colors.white,\r\n marginBottom: 2,\r\n textTransform: 'uppercase',\r\n letterSpacing: 1,\r\n },\r\n title: {\r\n fontSize: 8,\r\n color: colors.accent,\r\n fontWeight: 'bold',\r\n marginBottom: 8,\r\n letterSpacing: 0.5,\r\n },\r\n // Section Titles - minimal spacing\r\n sectionTitle: {\r\n fontSize: 9,\r\n fontWeight: 'bold',\r\n color: colors.accent,\r\n marginBottom: 4,\r\n marginTop: 10,\r\n textTransform: 'uppercase',\r\n letterSpacing: 1,\r\n borderBottomWidth: 0.5,\r\n borderBottomColor: colors.accent,\r\n paddingBottom: 2,\r\n },\r\n sectionTitleMain: {\r\n fontSize: 11,\r\n fontWeight: 'bold',\r\n color: colors.secondary,\r\n marginBottom: 6,\r\n marginTop: 8,\r\n textTransform: 'uppercase',\r\n letterSpacing: 1,\r\n borderBottomWidth: 1,\r\n borderBottomColor: colors.secondary,\r\n paddingBottom: 3,\r\n },\r\n // Contact Items - Tight\r\n contactRow: {\r\n marginBottom: 3,\r\n },\r\n contactLabel: {\r\n fontSize: 6,\r\n color: '#94a3b8',\r\n marginBottom: 0,\r\n },\r\n contactValue: {\r\n fontSize: 7,\r\n color: colors.white,\r\n },\r\n contactLink: {\r\n fontSize: 7,\r\n color: colors.accent,\r\n textDecoration: 'none',\r\n },\r\n // Skills - Compact tags\r\n skillsContainer: {\r\n flexDirection: 'row',\r\n flexWrap: 'wrap',\r\n gap: 3,\r\n },\r\n skillTag: {\r\n fontSize: 6.5,\r\n color: colors.white,\r\n backgroundColor: 'rgba(14, 165, 233, 0.3)',\r\n paddingHorizontal: 4,\r\n paddingVertical: 2,\r\n borderRadius: 2,\r\n marginBottom: 3,\r\n marginRight: 3,\r\n },\r\n // Education - minimal\r\n eduItem: {\r\n marginBottom: 5,\r\n },\r\n eduDegree: {\r\n fontSize: 8,\r\n fontWeight: 'bold',\r\n color: colors.white,\r\n },\r\n eduSchool: {\r\n fontSize: 7,\r\n color: colors.accent,\r\n marginTop: 0,\r\n },\r\n eduDate: {\r\n fontSize: 6,\r\n color: '#94a3b8',\r\n marginTop: 0,\r\n },\r\n // Experience - Compact list\r\n expItem: {\r\n marginBottom: 8,\r\n },\r\n expRole: {\r\n fontSize: 10,\r\n fontWeight: 'bold',\r\n color: colors.primary,\r\n },\r\n expCompany: {\r\n fontSize: 9,\r\n fontWeight: 'bold',\r\n color: colors.secondary,\r\n marginTop: 1,\r\n },\r\n expMeta: {\r\n fontSize: 8,\r\n color: colors.textLight,\r\n marginTop: 1,\r\n marginBottom: 3,\r\n },\r\n expBullet: {\r\n fontSize: 9,\r\n color: colors.text,\r\n marginLeft: 8,\r\n marginBottom: 3,\r\n lineHeight: 1.3,\r\n },\r\n bulletPoint: {\r\n color: colors.secondary,\r\n fontWeight: 'bold',\r\n },\r\n // Highlighted text for metrics\r\n highlightText: {\r\n color: colors.secondary,\r\n fontWeight: 'bold',\r\n },\r\n // Summary - Compact\r\n summary: {\r\n fontSize: 9,\r\n color: colors.text,\r\n lineHeight: 1.3,\r\n textAlign: 'justify',\r\n marginBottom: 6,\r\n },\r\n // Projects - Very Compact\r\n projectItem: {\r\n marginBottom: 4,\r\n },\r\n projectName: {\r\n fontSize: 7.5,\r\n fontWeight: 'bold',\r\n color: colors.white,\r\n },\r\n projectDesc: {\r\n fontSize: 6.5,\r\n color: '#cbd5e1',\r\n lineHeight: 1.2,\r\n marginTop: 1,\r\n },\r\n projectTech: {\r\n fontSize: 6.5,\r\n color: colors.accent,\r\n marginTop: 1,\r\n },\r\n // Small items\r\n smallItem: {\r\n fontSize: 7,\r\n color: colors.white,\r\n marginBottom: 2,\r\n lineHeight: 1.2,\r\n },\r\n // Achievement - Compact\r\n achievementBullet: {\r\n fontSize: 9,\r\n color: colors.text,\r\n marginLeft: 8,\r\n marginBottom: 4,\r\n lineHeight: 1.3,\r\n },\r\n achieveStar: {\r\n color: colors.highlight,\r\n fontWeight: 'bold',\r\n },\r\n});\r\n\r\n// Helper to highlight metrics in text (numbers, percentages)\r\nconst formatBullet = (text: string) => {\r\n return text;\r\n};\r\n\r\nexport const ResumePDF = ({ data }: { data: any }) => {\r\n return (\r\n <Document>\r\n <Page size=\"A4\" style={styles.page}>\r\n {/* Left Sidebar - Compact */}\r\n <View style={styles.sidebar}>\r\n {/* Name & Title */}\r\n <Text style={styles.name}>{data.fullName}</Text>\r\n {data.experience?.[0]?.role && (\r\n <Text style={styles.title}>{data.experience[0].role}</Text>\r\n )}\r\n\r\n {/* Contact Info - Better structured */}\r\n <Text style={styles.sectionTitle}>Contact</Text>\r\n\r\n {data.contactInfo?.email && (\r\n <View style={styles.contactRow}>\r\n <Text style={styles.contactLabel}>EMAIL</Text>\r\n <Link src={`mailto:${data.contactInfo.email}`} style={styles.contactLink}>\r\n {data.contactInfo.email}\r\n </Link>\r\n </View>\r\n )}\r\n\r\n {data.contactInfo?.phone && (\r\n <View style={styles.contactRow}>\r\n <Text style={styles.contactLabel}>PHONE</Text>\r\n <Link src={`tel:${data.contactInfo.phone}`} style={styles.contactLink}>\r\n {data.contactInfo.phone}\r\n </Link>\r\n </View>\r\n )}\r\n\r\n {data.contactInfo?.linkedin && (\r\n <View style={styles.contactRow}>\r\n <Text style={styles.contactLabel}>LINKEDIN</Text>\r\n <Link src={data.contactInfo.linkedin.startsWith('http') ? data.contactInfo.linkedin : `https://${data.contactInfo.linkedin}`} style={styles.contactLink}>\r\n {data.contactInfo.linkedin.replace('https://', '').replace('www.', '').substring(0, 30)}\r\n </Link>\r\n </View>\r\n )}\r\n\r\n {data.contactInfo?.website && (\r\n <View style={styles.contactRow}>\r\n <Text style={styles.contactLabel}>PORTFOLIO</Text>\r\n <Link src={data.contactInfo.website.startsWith('http') ? data.contactInfo.website : `https://${data.contactInfo.website}`} style={styles.contactLink}>\r\n {data.contactInfo.website.replace('https://', '').replace('www.', '').substring(0, 30)}\r\n </Link>\r\n </View>\r\n )}\r\n\r\n {data.contactInfo?.location && (\r\n <View style={styles.contactRow}>\r\n <Text style={styles.contactLabel}>LOCATION</Text>\r\n <Text style={styles.contactValue}>{data.contactInfo.location}</Text>\r\n </View>\r\n )}\r\n\r\n {/* Skills */}\r\n <Text style={styles.sectionTitle}>Technical Skills</Text>\r\n <View style={styles.skillsContainer}>\r\n {data.skills?.map((skill: string, i: number) => (\r\n <Text key={i} style={styles.skillTag}>{skill}</Text>\r\n ))}\r\n </View>\r\n\r\n {/* Education */}\r\n {data.education && data.education.length > 0 && (\r\n <>\r\n <Text style={styles.sectionTitle}>Education</Text>\r\n {data.education.map((edu: any, i: number) => (\r\n <View key={i} style={styles.eduItem}>\r\n <Text style={styles.eduDegree}>{edu.degree}</Text>\r\n <Text style={styles.eduSchool}>{edu.institution}</Text>\r\n <Text style={styles.eduDate}>\r\n {edu.date} {edu.gpa && `ΓÇó GPA: ${edu.gpa}`}\r\n </Text>\r\n </View>\r\n ))}\r\n </>\r\n )}\r\n\r\n {/* Projects */}\r\n {data.projects && data.projects.length > 0 && (\r\n <>\r\n <Text style={styles.sectionTitle}>Projects</Text>\r\n {data.projects.map((project: any, i: number) => (\r\n <View key={i} style={styles.projectItem}>\r\n <Text style={styles.projectName}>{project.name}</Text>\r\n <Text style={styles.projectDesc}>\r\n {project.description?.substring(0, 90)}...\r\n </Text>\r\n {project.techStack && (\r\n <Text style={styles.projectTech}>{project.techStack.slice(0, 5).join(' ΓÇó ')}</Text>\r\n )}\r\n </View>\r\n ))}\r\n </>\r\n )}\r\n\r\n {/* Certifications */}\r\n {data.certifications && data.certifications.length > 0 && (\r\n <>\r\n <Text style={styles.sectionTitle}>Certifications</Text>\r\n {data.certifications.map((cert: string, i: number) => (\r\n <Text key={i} style={styles.smallItem}>Γû¬ {cert}</Text>\r\n ))}\r\n </>\r\n )}\r\n\r\n {/* Languages */}\r\n {data.languages && data.languages.length > 0 && (\r\n <>\r\n <Text style={styles.sectionTitle}>Languages</Text>\r\n {data.languages.map((lang: string, i: number) => (\r\n <Text key={i} style={styles.smallItem}>Γû¬ {lang}</Text>\r\n ))}\r\n </>\r\n )}\r\n </View>\r\n\r\n {/* Right Main Content - Compact */}\r\n <View style={styles.main}>\r\n {/* Professional Summary - LARGER */}\r\n <Text style={styles.sectionTitleMain}>Professional Summary</Text>\r\n <Text style={styles.summary}>{data.professionalSummary}</Text>\r\n\r\n {/* Experience - LARGER with spacing */}\r\n <Text style={styles.sectionTitleMain}>Professional Experience</Text>\r\n {data.experience?.map((exp: any, i: number) => (\r\n <View key={i} style={styles.expItem}>\r\n <Text style={styles.expRole}>{exp.role}</Text>\r\n <Text style={styles.expCompany}>{exp.company}</Text>\r\n <Text style={styles.expMeta}>\r\n {exp.date} {exp.location && ` ΓÇó ${exp.location}`}\r\n </Text>\r\n {exp.description?.map((bullet: string, j: number) => (\r\n <Text key={j} style={styles.expBullet}>\r\n <Text style={styles.bulletPoint}>Γû╕ </Text>{formatBullet(bullet)}\r\n </Text>\r\n ))}\r\n </View>\r\n ))}\r\n\r\n {/* Achievements - LARGER with spacing */}\r\n {data.achievements && data.achievements.length > 0 && (\r\n <>\r\n <Text style={styles.sectionTitleMain}>Key Achievements</Text>\r\n {data.achievements.map((achievement: string, i: number) => (\r\n <Text key={i} style={styles.achievementBullet}>\r\n <Text style={styles.achieveStar}>Γÿà </Text>{achievement}\r\n </Text>\r\n ))}\r\n </>\r\n )}\r\n </View>\r\n </Page>\r\n </Document>\r\n );\r\n};\r\n\r\nexport default ResumePDF;\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\Toast.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ui\\badge.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ui\\button.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ui\\card.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ui\\dialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ui\\input.tsx","messages":[{"ruleId":"@typescript-eslint/no-empty-object-type","severity":1,"message":"An interface declaring no members is equivalent to its supertype.","line":4,"column":18,"nodeType":"Identifier","messageId":"noEmptyInterfaceWithSuper","endLine":4,"endColumn":28,"suggestions":[{"messageId":"replaceEmptyInterfaceWithSuper","fix":{"range":[75,154],"text":"type InputProps = React.InputHTMLAttributes<HTMLInputElement>"},"desc":"Replace empty interface with a type alias."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import * as React from \"react\"\r\nimport { cn } from \"@/lib/utils\"\r\n\r\nexport interface InputProps\r\n extends React.InputHTMLAttributes<HTMLInputElement> { }\r\n\r\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\r\n ({ className, type, ...props }, ref) => {\r\n return (\r\n <input\r\n type={type}\r\n className={cn(\r\n \"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\r\n className\r\n )}\r\n ref={ref}\r\n {...props}\r\n />\r\n )\r\n }\r\n)\r\nInput.displayName = \"Input\"\r\n\r\nexport { Input }\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ui\\label.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ui\\progress.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ui\\scroll-area.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ui\\separator.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\components\\ui\\textarea.tsx","messages":[{"ruleId":"@typescript-eslint/no-empty-object-type","severity":1,"message":"An interface declaring no members is equivalent to its supertype.","line":5,"column":18,"nodeType":"Identifier","messageId":"noEmptyInterfaceWithSuper","endLine":5,"endColumn":31,"suggestions":[{"messageId":"replaceEmptyInterfaceWithSuper","fix":{"range":[77,165],"text":"type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>"},"desc":"Replace empty interface with a type alias."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import * as React from \"react\"\r\n\r\nimport { cn } from \"@/lib/utils\"\r\n\r\nexport interface TextareaProps\r\n extends React.TextareaHTMLAttributes<HTMLTextAreaElement> { }\r\n\r\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\r\n ({ className, ...props }, ref) => {\r\n return (\r\n <textarea\r\n className={cn(\r\n \"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\r\n className\r\n )}\r\n ref={ref}\r\n {...props}\r\n />\r\n )\r\n }\r\n)\r\nTextarea.displayName = \"Textarea\"\r\n\r\nexport { Textarea }\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\lib\\docx-generator.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'HeadingLevel' is defined but never used.","line":1,"column":48,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":60,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"HeadingLevel"},"fix":{"range":[45,59],"text":""},"desc":"Remove unused variable \"HeadingLevel\"."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":13,"column":42,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":13,"endColumn":45,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[537,540],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[537,540],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":15,"column":48,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":15,"endColumn":51,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[703,706],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[703,706],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":168,"column":38,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":168,"endColumn":41,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[6416,6419],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[6416,6419],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":189,"column":41,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":189,"endColumn":44,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[7267,7270],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[7267,7270],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":243,"column":36,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":243,"endColumn":39,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[9372,9375],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[9372,9375],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { Document, Packer, Paragraph, TextRun, HeadingLevel, AlignmentType, Table, TableRow, TableCell, WidthType, BorderStyle, ShadingType, convertInchesToTwip, ExternalHyperlink } from \"docx\";\r\n\r\n// Premium color scheme matching PDF\r\nconst colors = {\r\n primary: \"0f172a\", // Deep navy\r\n secondary: \"1e40af\", // Royal blue\r\n accent: \"0ea5e9\", // Sky blue\r\n text: \"1e293b\", // Dark slate\r\n textLight: \"475569\", // Medium slate\r\n white: \"ffffff\",\r\n};\r\n\r\nexport const generateDocx = async (data: any) => {\r\n // Helper for created text - VERY compact sizes (half-points: 14 = 7pt, 16 = 8pt, 18 = 9pt, 20 = 10pt)\r\n const createText = (text: string, options: any = {}) => new TextRun({\r\n text,\r\n font: \"Arial\",\r\n size: options.size || 16, // Default to 8pt\r\n bold: options.bold || false,\r\n color: options.color || colors.text,\r\n ...options,\r\n });\r\n\r\n // Helper for section headers in sidebar - compact\r\n const sidebarSectionHeader = (text: string) => new Paragraph({\r\n children: [createText(text, { bold: true, color: colors.accent, size: 14 })], // 7pt header\r\n spacing: { before: 60, after: 30 }, // Reduced spacing\r\n border: {\r\n bottom: { style: BorderStyle.SINGLE, size: 4, color: colors.accent },\r\n },\r\n });\r\n\r\n // Helper for section headers in main - compact\r\n const mainSectionHeader = (text: string) => new Paragraph({\r\n children: [createText(text, { bold: true, color: colors.secondary, size: 16 })], // 8pt header\r\n spacing: { before: 80, after: 40 }, // Reduced spacing\r\n border: {\r\n bottom: { style: BorderStyle.SINGLE, size: 6, color: colors.secondary },\r\n },\r\n });\r\n\r\n // Build sidebar content - compact\r\n const sidebarContent: Paragraph[] = [\r\n // Name\r\n new Paragraph({\r\n children: [createText(data.fullName?.toUpperCase() || \"YOUR NAME\", {\r\n bold: true,\r\n color: colors.white,\r\n size: 20, // 10pt Name\r\n })],\r\n spacing: { after: 20 },\r\n }),\r\n // Title\r\n new Paragraph({\r\n children: [createText(data.experience?.[0]?.role?.toUpperCase() || \"PROFESSIONAL\", {\r\n bold: true,\r\n color: colors.accent,\r\n size: 12, // 6pt Title\r\n })],\r\n spacing: { after: 60 },\r\n }),\r\n\r\n // Contact Section\r\n sidebarSectionHeader(\"CONTACT\"),\r\n ];\r\n\r\n // Add contact items with clickable links\r\n if (data.contactInfo?.email) {\r\n sidebarContent.push(\r\n new Paragraph({\r\n children: [createText(\"Email\", { color: \"94a3b8\", size: 12 })], // 6pt Label\r\n spacing: { before: 30 },\r\n }),\r\n new Paragraph({\r\n children: [\r\n new ExternalHyperlink({\r\n children: [createText(data.contactInfo.email, { color: colors.accent, size: 14 })], // 7pt Value\r\n link: `mailto:${data.contactInfo.email}`,\r\n }),\r\n ],\r\n spacing: { after: 40 },\r\n })\r\n );\r\n }\r\n\r\n if (data.contactInfo?.phone) {\r\n sidebarContent.push(\r\n new Paragraph({\r\n children: [createText(\"Phone\", { color: \"94a3b8\", size: 12 })],\r\n }),\r\n new Paragraph({\r\n children: [\r\n new ExternalHyperlink({\r\n children: [createText(data.contactInfo.phone, { color: colors.accent, size: 14 })],\r\n link: `tel:${data.contactInfo.phone}`,\r\n }),\r\n ],\r\n spacing: { after: 40 },\r\n })\r\n );\r\n }\r\n\r\n if (data.contactInfo?.linkedin) {\r\n const linkedinUrl = data.contactInfo.linkedin.startsWith('http')\r\n ? data.contactInfo.linkedin\r\n : `https://${data.contactInfo.linkedin}`;\r\n sidebarContent.push(\r\n new Paragraph({\r\n children: [createText(\"LinkedIn\", { color: \"94a3b8\", size: 12 })],\r\n }),\r\n new Paragraph({\r\n children: [\r\n new ExternalHyperlink({\r\n children: [createText(data.contactInfo.linkedin.replace('https://', '').replace('www.', ''), { color: colors.accent, size: 14, underline: {} })],\r\n link: linkedinUrl,\r\n }),\r\n ],\r\n spacing: { after: 40 },\r\n })\r\n );\r\n }\r\n\r\n if (data.contactInfo?.website) {\r\n const websiteUrl = data.contactInfo.website.startsWith('http')\r\n ? data.contactInfo.website\r\n : `https://${data.contactInfo.website}`;\r\n sidebarContent.push(\r\n new Paragraph({\r\n children: [createText(\"Portfolio\", { color: \"94a3b8\", size: 12 })],\r\n }),\r\n new Paragraph({\r\n children: [\r\n new ExternalHyperlink({\r\n children: [createText(data.contactInfo.website.replace('https://', '').replace('www.', ''), { color: colors.accent, size: 14, underline: {} })],\r\n link: websiteUrl,\r\n }),\r\n ],\r\n spacing: { after: 40 },\r\n })\r\n );\r\n }\r\n\r\n if (data.contactInfo?.location) {\r\n sidebarContent.push(\r\n new Paragraph({\r\n children: [createText(\"Location\", { color: \"94a3b8\", size: 12 })],\r\n }),\r\n new Paragraph({\r\n children: [createText(data.contactInfo.location, { color: colors.white, size: 14 })],\r\n spacing: { after: 40 },\r\n })\r\n );\r\n }\r\n\r\n // Skills Section\r\n sidebarContent.push(sidebarSectionHeader(\"SKILLS\"));\r\n\r\n const skillsText = data.skills?.map((skill: string) => `ΓÇó ${skill}`).join('\\n') || '';\r\n sidebarContent.push(new Paragraph({\r\n children: [createText(skillsText, { color: colors.white, size: 13 })], // 6.5pt Skills\r\n spacing: { after: 80 },\r\n }));\r\n\r\n // Education Section\r\n if (data.education && data.education.length > 0) {\r\n sidebarContent.push(sidebarSectionHeader(\"EDUCATION\"));\r\n\r\n data.education.forEach((edu: any) => {\r\n sidebarContent.push(\r\n new Paragraph({\r\n children: [createText(edu.degree, { bold: true, color: colors.white, size: 14 })],\r\n spacing: { before: 40 },\r\n }),\r\n new Paragraph({\r\n children: [createText(edu.institution, { color: colors.accent, size: 13 })],\r\n }),\r\n new Paragraph({\r\n children: [createText(`${edu.date}${edu.gpa ? ` ΓÇó GPA: ${edu.gpa}` : ''}`, { color: \"94a3b8\", size: 12 })],\r\n spacing: { after: 60 },\r\n })\r\n );\r\n });\r\n }\r\n\r\n // Projects Section\r\n if (data.projects && data.projects.length > 0) {\r\n sidebarContent.push(sidebarSectionHeader(\"PROJECTS\"));\r\n\r\n data.projects.forEach((project: any) => {\r\n sidebarContent.push(\r\n new Paragraph({\r\n children: [createText(project.name, { bold: true, color: colors.white, size: 14 })],\r\n spacing: { before: 40 },\r\n }),\r\n new Paragraph({\r\n children: [createText(project.description?.substring(0, 100) + (project.description?.length > 100 ? '...' : ''), { color: \"cbd5e1\", size: 12 })],\r\n }),\r\n );\r\n if (project.techStack) {\r\n sidebarContent.push(new Paragraph({\r\n children: [createText(project.techStack.join(' ΓÇó '), { color: colors.accent, size: 11 })],\r\n spacing: { after: 60 },\r\n }));\r\n }\r\n });\r\n }\r\n\r\n // Certifications Section\r\n if (data.certifications && data.certifications.length > 0) {\r\n sidebarContent.push(sidebarSectionHeader(\"CERTIFICATIONS\"));\r\n data.certifications.forEach((cert: string) => {\r\n sidebarContent.push(new Paragraph({\r\n children: [createText(`ΓÇó ${cert}`, { color: colors.white, size: 13 })],\r\n }));\r\n });\r\n }\r\n\r\n // Languages Section\r\n if (data.languages && data.languages.length > 0) {\r\n sidebarContent.push(sidebarSectionHeader(\"LANGUAGES\"));\r\n data.languages.forEach((lang: string) => {\r\n sidebarContent.push(new Paragraph({\r\n children: [createText(`ΓÇó ${lang}`, { color: colors.white, size: 13 })],\r\n }));\r\n });\r\n }\r\n\r\n // Build main content\r\n const mainContent: Paragraph[] = [\r\n // Professional Summary\r\n mainSectionHeader(\"PROFESSIONAL SUMMARY\"),\r\n new Paragraph({\r\n children: [createText(data.professionalSummary || '', { size: 18 })], // 9pt Summary\r\n spacing: { after: 100 },\r\n alignment: AlignmentType.JUSTIFIED,\r\n }),\r\n\r\n // Experience\r\n mainSectionHeader(\"PROFESSIONAL EXPERIENCE\"),\r\n ];\r\n\r\n // Add experience entries\r\n data.experience?.forEach((exp: any) => {\r\n mainContent.push(\r\n new Paragraph({\r\n children: [createText(exp.role, { bold: true, color: colors.primary, size: 18 })], // 9pt Role\r\n spacing: { before: 80 },\r\n }),\r\n new Paragraph({\r\n children: [createText(exp.company, { bold: true, color: colors.secondary, size: 16 })], // 8pt Company\r\n }),\r\n new Paragraph({\r\n children: [createText(`${exp.date}${exp.location ? ` ΓÇó ${exp.location}` : ''}`, { color: colors.textLight, size: 14 })], // 7pt Date\r\n spacing: { after: 30 },\r\n }),\r\n );\r\n\r\n exp.description?.forEach((bullet: string) => {\r\n mainContent.push(new Paragraph({\r\n children: [\r\n createText(\"Γû╕ \", { bold: true, color: colors.secondary, size: 16 }),\r\n createText(bullet, { size: 16 }), // 8pt Body\r\n ],\r\n spacing: { after: 20, before: 10 },\r\n indent: { left: convertInchesToTwip(0.15) },\r\n }));\r\n });\r\n });\r\n\r\n // Add achievements if available\r\n if (data.achievements && data.achievements.length > 0) {\r\n mainContent.push(mainSectionHeader(\"KEY ACHIEVEMENTS\"));\r\n data.achievements.forEach((achievement: string) => {\r\n mainContent.push(new Paragraph({\r\n children: [\r\n createText(\"Γÿà \", { bold: true, color: colors.secondary, size: 16 }),\r\n createText(achievement, { size: 16 }), // 8pt Body\r\n ],\r\n spacing: { after: 20 },\r\n indent: { left: convertInchesToTwip(0.15) },\r\n }));\r\n });\r\n }\r\n\r\n // Create the two-column table\r\n const doc = new Document({\r\n sections: [{\r\n properties: {\r\n page: {\r\n margin: {\r\n top: convertInchesToTwip(0.3), // Reduced margins\r\n right: convertInchesToTwip(0.3),\r\n bottom: convertInchesToTwip(0.3),\r\n left: convertInchesToTwip(0.3),\r\n },\r\n },\r\n },\r\n children: [\r\n new Table({\r\n width: { size: 100, type: WidthType.PERCENTAGE },\r\n borders: {\r\n top: { style: BorderStyle.NONE },\r\n bottom: { style: BorderStyle.NONE },\r\n left: { style: BorderStyle.NONE },\r\n right: { style: BorderStyle.NONE },\r\n insideHorizontal: { style: BorderStyle.NONE },\r\n insideVertical: { style: BorderStyle.NONE },\r\n },\r\n rows: [\r\n new TableRow({\r\n children: [\r\n // Sidebar cell (32%)\r\n new TableCell({\r\n width: { size: 32, type: WidthType.PERCENTAGE },\r\n shading: {\r\n fill: colors.primary,\r\n type: ShadingType.SOLID,\r\n color: colors.primary,\r\n },\r\n margins: {\r\n top: convertInchesToTwip(0.2),\r\n bottom: convertInchesToTwip(0.2),\r\n left: convertInchesToTwip(0.15),\r\n right: convertInchesToTwip(0.15),\r\n },\r\n children: sidebarContent,\r\n }),\r\n // Main content cell (68%)\r\n new TableCell({\r\n width: { size: 68, type: WidthType.PERCENTAGE },\r\n margins: {\r\n top: convertInchesToTwip(0.2),\r\n bottom: convertInchesToTwip(0.2),\r\n left: convertInchesToTwip(0.25),\r\n right: convertInchesToTwip(0.15),\r\n },\r\n children: mainContent,\r\n }),\r\n ],\r\n }),\r\n ],\r\n }),\r\n ],\r\n }],\r\n });\r\n\r\n return await Packer.toBlob(doc);\r\n};\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\lib\\gemini.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\lib\\prisma.ts","messages":[{"ruleId":"@typescript-eslint/ban-ts-comment","severity":1,"message":"Use \"@ts-expect-error\" instead of \"@ts-ignore\", as \"@ts-ignore\" will do nothing if the following line is error-free.","line":6,"column":1,"nodeType":"Line","messageId":"tsIgnoreInsteadOfExpectError","endLine":6,"endColumn":14,"suggestions":[{"messageId":"replaceTsIgnoreWithTsExpectError","fix":{"range":[187,200],"text":"// @ts-expect-error"},"desc":"Replace \"@ts-ignore\" with \"@ts-expect-error\"."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { PrismaClient } from '@prisma/client';\r\n\r\n// Use a global variable to store the Prisma Client instance\r\n// to prevent multiple instances in development (Next.js hot reloading)\r\n\r\n// @ts-ignore\r\nconst globalForPrisma = global as unknown as { prisma: PrismaClient };\r\n\r\nexport const prisma =\r\n globalForPrisma.prisma ||\r\n new PrismaClient({\r\n log: ['warn', 'error'],\r\n });\r\n\r\nif (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\lib\\utils.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\ANIRUDDHA\\Desktop\\Projects\\smart-resume-analyzer\\src\\middleware.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]}]