| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Resume Job Matcher | AI-Powered Career Matching</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.min.js" integrity="sha512-ml/QKfG3+Yes6TwOzQb7aCNtJF4PUyha6R3w8pSTo/VJSywl7ZreYvvtUso7fKevpsI+pYVVwnu82YO0q3V6eg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> |
| <script> |
| tailwind.config = { |
| theme: { |
| extend: { |
| colors: { |
| primary: { |
| 50: '#f0f9ff', |
| 100: '#e0f2fe', |
| 200: '#bae6fd', |
| 300: '#7dd3fc', |
| 400: '#38bdf8', |
| 500: '#0ea5e9', |
| 600: '#0284c7', |
| 700: '#0369a1', |
| 800: '#075985', |
| 900: '#0c4a6e', |
| }, |
| dark: '#1e293b', |
| light: '#f8fafc' |
| }, |
| fontFamily: { |
| 'sans': ['Inter', 'ui-sans-serif', 'system-ui'], |
| }, |
| boxShadow: { |
| 'soft': '0 15px 30px -5px rgba(0, 0, 0, 0.05)', |
| 'soft-lg': '0 20px 40px -5px rgba(0, 0, 0, 0.08)', |
| 'glow': '0 0 15px rgba(59, 130, 246, 0.2)', |
| } |
| } |
| } |
| } |
| </script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> |
| <style> |
| .file-upload { |
| position: relative; |
| overflow: hidden; |
| transition: all 0.3s ease; |
| } |
| .file-upload:hover { |
| transform: translateY(-2px); |
| } |
| .file-upload input { |
| position: absolute; |
| top: 0; |
| right: 0; |
| margin: 0; |
| padding: 0; |
| font-size: 20px; |
| cursor: pointer; |
| opacity: 0; |
| filter: alpha(opacity=0); |
| } |
| .progress-bar { |
| transition: width 0.5s ease-in-out; |
| } |
| .job-card { |
| transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); |
| background: linear-gradient(to bottom right, white 50%, #f8fafc); |
| } |
| .job-card:hover { |
| transform: translateY(-5px); |
| box-shadow: var(--tw-shadow-soft-lg), 0 0 0 1px rgba(59, 130, 246, 0.1); |
| } |
| .feature-card:hover { |
| transform: translateY(-3px); |
| box-shadow: var(--tw-shadow-soft-lg), 0 0 0 1px rgba(59, 130, 246, 0.1); |
| } |
| #resumePreview { |
| max-height: 500px; |
| overflow-y: auto; |
| } |
| .tabs button.active { |
| border-bottom: 2px solid #0ea5e9; |
| color: #0ea5e9; |
| font-weight: 600; |
| } |
| .match-badge { |
| background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%); |
| color: white; |
| } |
| .skill-pill { |
| transition: all 0.2s ease; |
| } |
| .skill-pill:hover { |
| transform: scale(1.05); |
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); |
| } |
| .blinking-cursor { |
| display: inline-block; |
| width: 2px; |
| height: 15px; |
| background: #0ea5e9; |
| margin-left: 5px; |
| animation: blink 1s infinite; |
| } |
| @keyframes blink { |
| 0%, 100% { opacity: 1; } |
| 50% { opacity: 0; } |
| } |
| .floating-animation { |
| animation: float 6s ease-in-out infinite; |
| } |
| @keyframes float { |
| 0%, 100% { transform: translateY(0); } |
| 50% { transform: translateY(-10px); } |
| } |
| ::-webkit-scrollbar { |
| width: 8px; |
| height: 8px; |
| } |
| ::-webkit-scrollbar-track { |
| background: #f1f1f1; |
| border-radius: 10px; |
| } |
| ::-webkit-scrollbar-thumb { |
| background: #c1c1c1; |
| border-radius: 10px; |
| } |
| ::-webkit-scrollbar-thumb:hover { |
| background: #a1a1a1; |
| } |
| .modal { |
| transition: opacity 0.3s ease, visibility 0.3s ease; |
| } |
| .auth-form { |
| transition: transform 0.3s ease, opacity 0.3s ease; |
| } |
| </style> |
| </head> |
| <body class="font-sans bg-gray-50 antialiased"> |
| |
| <div id="loginModal" class="modal fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> |
| <div class="auth-form bg-white rounded-xl shadow-2xl w-full max-w-md p-8 transform translate-y-4 opacity-0"> |
| <div class="flex justify-between items-center mb-6"> |
| <h2 class="text-2xl font-bold text-dark">Welcome Back</h2> |
| <button id="closeLoginModal" class="text-gray-500 hover:text-gray-700"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <form id="loginForm"> |
| <div class="mb-4"> |
| <label for="loginEmail" class="block text-gray-700 text-sm font-medium mb-2">Email</label> |
| <input type="email" id="loginEmail" required class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-300 focus:border-primary-300"> |
| </div> |
| <div class="mb-6"> |
| <label for="loginPassword" class="block text-gray-700 text-sm font-medium mb-2">Password</label> |
| <input type="password" id="loginPassword" required class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-300 focus:border-primary-300"> |
| </div> |
| <button type="submit" class="w-full bg-primary-600 hover:bg-primary-700 text-white font-medium py-2.5 rounded-lg transition mb-4"> |
| Log In |
| </button> |
| <p class="text-center text-gray-600 text-sm"> |
| Don't have an account? |
| <button type="button" id="showSignupForm" class="text-primary-600 font-medium hover:underline">Sign up</button> |
| </p> |
| </form> |
| </div> |
| </div> |
|
|
| |
| <div id="signupModal" class="modal fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> |
| <div class="auth-form bg-white rounded-xl shadow-2xl w-full max-w-md p-8 transform translate-y-4 opacity-0"> |
| <div class="flex justify-between items-center mb-6"> |
| <h2 class="text-2xl font-bold text-dark">Create Account</h2> |
| <button id="closeSignupModal" class="text-gray-500 hover:text-gray-700"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <form id="signupForm"> |
| <div class="mb-4"> |
| <label for="signupName" class="block text-gray-700 text-sm font-medium mb-2">Full Name</label> |
| <input type="text" id="signupName" required class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-300 focus:border-primary-300"> |
| </div> |
| <div class="mb-4"> |
| <label for="signupEmail" class="block text-gray-700 text-sm font-medium mb-2">Email</label> |
| <input type="email" id="signupEmail" required class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-300 focus:border-primary-300"> |
| </div> |
| <div class="mb-6"> |
| <label for="signupPassword" class="block text-gray-700 text-sm font-medium mb-2">Password</label> |
| <input type="password" id="signupPassword" required minlength="6" class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-300 focus:border-primary-300"> |
| </div> |
| <button type="submit" class="w-full bg-primary-600 hover:bg-primary-700 text-white font-medium py-2.5 rounded-lg transition mb-4"> |
| Sign Up |
| </button> |
| <p class="text-center text-gray-600 text-sm"> |
| Already have an account? |
| <button type="button" id="showLoginForm" class="text-primary-600 font-medium hover:underline">Log in</button> |
| </p> |
| </form> |
| </div> |
| </div> |
|
|
| |
| <div class="bg-gradient-to-r from-primary-700 to-primary-600 text-white"> |
| <div class="container mx-auto px-4 py-3"> |
| <div class="flex justify-between items-center"> |
| <div class="flex items-center space-x-4"> |
| <i class="fas fa-bolt text-yellow-300 text-xl"></i> |
| <span class="font-semibold">Resume Matcher PRO</span> |
| </div> |
| <nav class="hidden md:flex space-x-6"> |
| <a href="#" class="hover:text-yellow-200 transition">Home</a> |
| <a href="#" class="hover:text-yellow-200 transition">How It Works</a> |
| <a href="#" class="hover:text-yellow-200 transition">Pricing</a> |
| <a href="#" class="hover:text-yellow-200 transition">Contact</a> |
| </nav> |
| <div class="flex items-center space-x-3" id="authButtons"> |
| <button id="loginButton" class="hidden md:block px-3 py-1 text-sm rounded hover:bg-primary-700 transition"> |
| <i class="fas fa-user mr-1"></i> Login |
| </button> |
| <button id="signupButton" class="hidden md:block bg-yellow-400 hover:bg-yellow-300 text-primary-900 px-4 py-1.5 rounded-md font-medium transition"> |
| Sign Up Free |
| </button> |
| <button class="md:hidden text-xl"> |
| <i class="fas fa-bars"></i> |
| </button> |
| </div> |
| <div class="hidden items-center space-x-3" id="userProfile"> |
| <div class="flex items-center space-x-2"> |
| <img id="userAvatar" src="https://randomuser.me/api/portraits/men/32.jpg" alt="User" class="w-8 h-8 rounded-full border-2 border-white"> |
| <span id="userName" class="text-sm">John Doe</span> |
| </div> |
| <button id="logoutButton" class="text-sm px-3 py-1 rounded hover:bg-primary-700 transition"> |
| <i class="fas fa-sign-out-alt"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="container mx-auto px-4 py-12"> |
| <div class="max-w-6xl mx-auto"> |
| <div class="text-center mb-16"> |
| <div class="inline-block bg-primary-50 text-primary-600 px-4 py-1.5 rounded-full text-sm font-medium mb-4"> |
| <i class="fas fa-bolt mr-1"></i> POWERFUL JOB MATCHING TECHNOLOGY |
| </div> |
| <h1 class="text-5xl font-bold text-dark mb-4">Find Your Perfect <span class="text-primary-600">Career Match</span></h1> |
| <p class="text-xl text-gray-600 max-w-3xl mx-auto leading-relaxed"> |
| Upload your resume and let our AI analyze your skills to match you with the best job opportunities right now. |
| <span class="blinking-cursor"></span> |
| </p> |
| </div> |
|
|
| |
| <div id="uploadSection" class="hidden bg-white rounded-xl shadow-soft overflow-hidden mb-16 border border-gray-100"> |
| <div class="md:flex"> |
| <div class="md:w-2/3 p-8"> |
| <h2 class="text-3xl font-bold text-dark mb-2">Upload Your Resume</h2> |
| <p class="text-gray-600 mb-6">Get started in seconds. Simply upload your resume (we support DOCX, PDF, TXT formats).</p> |
| |
| <div class="file-upload bg-gradient-to-br from-primary-50 to-white border-2 border-dashed border-primary-300 rounded-xl p-8 text-center shadow-soft"> |
| <div class="floating-animation inline-block mb-4"> |
| <i class="fas fa-cloud-upload-alt text-primary-400 text-6xl"></i> |
| </div> |
| <h3 class="text-xl font-medium text-dark mb-2">Drag & Drop your resume here</h3> |
| <p class="text-gray-500 mb-6">or</p> |
| <button class="relative overflow-hidden bg-gradient-to-r from-primary-600 to-primary-500 hover:from-primary-700 hover:to-primary-600 text-white font-medium py-3 px-8 rounded-lg transition-all shadow-md hover:shadow-glow"> |
| Browse Files |
| <i class="fas fa-search ml-2"></i> |
| </button> |
| <input type="file" id="resumeUpload" accept=".pdf,.doc,.docx,.txt" class="w-full h-full"> |
| <p class="text-sm text-gray-500 mt-6">Supported formats: PDF, DOC, DOCX, TXT (Max 5MB)</p> |
| </div> |
|
|
| <div id="loadingContainer" class="hidden mt-8"> |
| <div class="flex items-center justify-center space-x-4"> |
| <div class="w-10 h-10 border-4 border-primary-200 border-t-primary-500 rounded-full animate-spin"></div> |
| <div class="text-left"> |
| <p class="font-medium text-gray-700">Analyzing your resume...</p> |
| <p class="text-sm text-gray-500">Extracting skills, experience, and qualifications</p> |
| </div> |
| </div> |
| <div class="w-full bg-gray-200 rounded-full h-2.5 mt-4"> |
| <div id="progressBar" class="progress-bar bg-gradient-to-r from-primary-500 to-primary-400 h-2.5 rounded-full" style="width: 0%"></div> |
| </div> |
| <p id="statusText" class="text-sm text-gray-500 mt-2 text-center">Initializing analysis...</p> |
| </div> |
| </div> |
| |
| <div class="md:w-1/3 bg-gradient-to-b from-primary-500 to-primary-600 p-8 flex flex-col"> |
| <div class="flex-1"> |
| <h3 class="text-white text-xl font-semibold mb-4">Why Upload Your Resume?</h3> |
| <ul class="space-y-4"> |
| <li class="flex items-start"> |
| <div class="flex-shrink-0 h-6 w-6 rounded-full bg-white bg-opacity-20 flex items-center justify-center text-white mr-3 mt-0.5"> |
| <i class="fas fa-check text-xs"></i> |
| </div> |
| <span class="text-white text-opacity-90">Get matched with current open jobs that fit your skills</span> |
| </li> |
| <li class="flex items-start"> |
| <div class="flex-shrink-0 h-6 w-6 rounded-full bg-white bg_opacity-20 flex items-center justify-center text-white mr-3 mt-0.5"> |
| <i class="fas fa-check text-xs"></i> |
| </div> |
| <span class="text-white text_opacity-90">Discover salaries 20-30% higher than your current</span> |
| </li> |
| <li class="flex items-start"> |
| <div class="flex-shrink-0 h-6 w-6 rounded-full bg-white bg_opacity-20 flex items-center justify-center text-white mr-3 mt-0.5"> |
| <i class="fas fa-check text-xs"></i> |
| </div> |
| <span class="text-white text_opacity-90">Receive personalized career development advice</span> |
| </li> |
| <li class="flex items-start"> |
| <div class="flex-shrink-0 h-6 w-6 rounded-full bg-white bg_opacity-20 flex items-center justify-center text-white mr-3 mt-0.5"> |
| <i class="fas fa-check text-xs"></i> |
| </div> |
| <span class="text-white text_opacity-90">Save your profile for future job matches</span> |
| </li> |
| </ul> |
| </div> |
| <div class="pt-6 border-t border-white border-opacity-20"> |
| <div class="flex items-center"> |
| <img src="https://randomuser.me/api/portraits/women/43.jpg" alt="User" class="w-10 h-10 rounded-full mr-3 border-2 border-white"> |
| <div> |
| <p class="text-white text-opacity-90 font-medium">"Found my dream job in 2 weeks!"</p> |
| <p class="text-white text-opacity-70 text-sm">Sarah K., Data Scientist</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="loginCTASection" class="bg-white rounded-xl shadow-soft overflow-hidden mb-16 border border-gray-100 p-8 text-center"> |
| <div class="max-w-2xl mx-auto"> |
| <i class="fas fa-lock text-primary-500 text-5xl mb-6"></i> |
| <h2 class="text-2xl font-bold text-dark mb-4">Ready to Find Your Perfect Job Match?</h2> |
| <p class="text-gray-600 mb-6">Sign up or log in to upload your resume and discover jobs that match your unique skills and experience.</p> |
| <div class="flex justify-center space-x-4"> |
| <button id="loginButtonCTA" class="bg-white border-2 border-primary-500 text-primary-600 hover:bg-primary-50 font-medium px-6 py-2 rounded-md transition"> |
| <i class="fas fa-user mr-2"></i> Log In |
| </button> |
| <button id="signupButtonCTA" class="bg-gradient-to-r from-primary-600 to-primary-500 hover:from-primary-700 hover:to-primary-600 text-white font-medium px-6 py-2 rounded-md transition shadow-md hover:shadow-glow"> |
| <i class="fas fa-user-plus mr-2"></i> Sign Up Free |
| </button> |
| </div> |
| <p class="text-sm text-gray-500 mt-4">Your data is secure and will never be shared without your permission.</p> |
| </div> |
| </div> |
|
|
| |
| <div id="resultsContainer" class="hidden"> |
| <div id="matchScore" class="bg-gradient-to-r from-primary-500 to-primary-600 rounded-xl p-6 mb-8 shadow-soft relative overflow-hidden"> |
| <div class="absolute top-0 right-0 opacity-20"> |
| <svg width="160" height="160" xmlns="http://www.w3.org/2000/svg"> |
| <path fill="white" d="M80 0c44.1 0 80 35.9 80 80s-35.9 80-80 80S0 124.1 0 80 35.9 0 80 0zm0 20c-33.1 0-60 26.9-60 60s26.9 60 60 60 60-26.9 60-60-26.9-60-60-60zm0 20c22.1 0 40 17.9 40 40s-17.9 40-40 40-40-17.9-40-40 17.9-40 40-40z"/> |
| </svg> |
| </div> |
| <div class="relative z-10"> |
| <div class="flex flex-col md:flex-row md:items-center justify-between"> |
| <div> |
| <h2 class="text-2xl font-bold text-white mb-1">Excellent Job Matches Found!</h2> |
| <p class="text-white text-opacity-80">Based on real-time job postings matching your resume</p> |
| </div> |
| <div class="inline-flex items-center px-4 py-2 bg-white bg-opacity-20 rounded-full text-white font-bold mt-4 md:mt-0"> |
| <span class="text-2xl mr-2">⭐</span> |
| <span id="matchPercentage">91%</span> MATCH |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="flex border-b mb-8 tabs"> |
| <button class="mr-6 py-3 px-1 font-medium text-gray-500 hover:text-primary-600 transition relative active" id="tabJobs"> |
| Job Matches |
| <div class="absolute bottom-0 left-0 w-full h-0.5 bg-primary-600 rounded-full"></div> |
| </button> |
| <button class="mr-6 py-3 px-1 font-medium text-gray-500 hover:text-primary-600 transition relative" id="tabSkills"> |
| Skill Analysis |
| <div class="absolute bottom-0 left-0 w-full h-0.5 bg-transparent rounded-full"></div> |
| </button> |
| <button class="py-3 px-1 font-medium text-gray-500 hover:text-primary-600 transition relative" id="tabResume"> |
| Resume Preview |
| <div class="absolute bottom-0 left-0 w-full h-0.5 bg-transparent rounded-full"></div> |
| </button> |
| </div> |
|
|
| <div id="jobsTab" class="tab-content"> |
| <div class="flex justify-between items-center mb-6"> |
| <h3 class="text-xl font-semibold text-dark">Recommended for You</h3> |
| <div class="flex items-center space-x-2"> |
| <span class="text-sm text-gray-500">Sort by:</span> |
| <select id="sortJobs" class="bg-gray-100 border-0 text-sm rounded-md px-3 py-1.5 focus:ring-2 focus:ring-primary-300"> |
| <option value="match">Best Match</option> |
| <option value="salary">Highest Salary</option> |
| <option value="recent">Most Recent</option> |
| <option value="location">Location</option> |
| </select> |
| </div> |
| </div> |
|
|
| <div id="jobsLoading" class="hidden flex items-center justify-center py-16"> |
| <div class="w-10 h-10 border-4 border-primary-200 border-t-primary-500 rounded-full animate-spin mr-4"></div> |
| <span class="text-gray-600">Searching for matching jobs...</span> |
| </div> |
|
|
| <div id="jobsList" class="space-y-4"> |
| |
| </div> |
|
|
| <div class="mt-8 text-center" id="loadMoreContainer"> |
| <button id="loadMoreJobs" class="bg-white border border-gray-300 hover:border-primary-400 text-primary-600 font-medium px-6 py-2 rounded-md transition"> |
| <i class="fas fa-sync-alt mr-2"></i> Load More Jobs |
| </button> |
| </div> |
| </div> |
|
|
| <div id="skillsTab" class="tab-content hidden"> |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8"> |
| <div class="lg:col-span-2 bg-white p-6 rounded-xl shadow-soft border border-gray-100"> |
| <h3 class="text-lg font-semibold text-dark mb-4">Your Skills Radar</h3> |
| <div class="h-64 flex items-center justify-center"> |
| <div class="text-center"> |
| <i class="fas fa-chart-radar text-primary-500 text-5xl mb-4"></i> |
| <p class="text-gray-500">Radar chart visualization would appear here showing your skill strengths</p> |
| </div> |
| </div> |
| </div> |
| <div class="bg-white p-6 rounded-xl shadow-soft border border-gray-100"> |
| <h3 class="text-lg font-semibold text-dark mb-4">Top Job Categories</h3> |
| <div class="space-y-3" id="jobCategories"> |
| |
| </div> |
| </div> |
| </div> |
|
|
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
| <div class="bg-white p-6 rounded-xl shadow-soft border border-gray-100"> |
| <div class="flex justify-between items-center mb-4"> |
| <h3 class="text-lg font-semibold text-dark">Your Top Skills</h3> |
| <span class="text-xs bg-primary-100 text-primary-700 px-2 py-1 rounded-full">Strength</span> |
| </div> |
| <div id="topSkills" class="space-y-4"> |
| |
| </div> |
| </div> |
| <div class="bg-white p-6 rounded-xl shadow-soft border border-gray-100"> |
| <div class="flex justify-between items-center mb-4"> |
| <h3 class="text-lg font-semibold text-dark">Suggested Skills to Learn</h3> |
| <span class="text-xs bg-yellow-100 text-yellow-700 px-2 py-1 rounded-full">Opportunity</span> |
| </div> |
| <div id="suggestedSkills" class="space-y-4"> |
| |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <div id="resumeTab" class="tab-content hidden"> |
| <div class="flex justify-between items-center mb-6"> |
| <h3 class="text-xl font-semibold text-dark">Resume Preview</h3> |
| <div class="flex space-x-2"> |
| <button class="bg-white border border-gray-300 hover:border-primary-400 text-gray-700 font-medium px-4 py-1.5 rounded-md text-sm transition"> |
| <i class="fas fa-download mr-2"></i> Download |
| </button> |
| <button class="bg-primary-600 hover:bg-primary-700 text-white font-medium px-4 py-1.5 rounded-md text-sm transition"> |
| <i class="fas fa-edit mr-2"></i> Edit Resume |
| </button> |
| </div> |
| </div> |
| |
| <div class="bg-gray-50 border border-gray-200 rounded-xl p-5"> |
| <div id="resumePreview" class="p-6 bg-white rounded-lg shadow-inner overflow-auto"> |
| |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="bg-dark text-white py-12"> |
| <div class="container mx-auto px-4"> |
| <div class="max-w-6xl mx-auto"> |
| <div class="grid md:grid-cols-4 gap-8"> |
| <div class="md:col-span-2"> |
| <h3 class="text-xl font-bold mb-4">Resume Matcher</h3> |
| <p class="text-gray-400">Using AI-powered technology to connect talented professionals with their perfect career matches since 2023.</p> |
| <div class="flex space-x-4 mt-6"> |
| <a href="#" class="text-gray-400 hover:text-white transition"> |
| <i class="fab fa-twitter text-xl"></i> |
| </a> |
| <a href="#" class="text-gray-400 hover:text-white transition"> |
| <i class="fab fa-linkedin text-xl"></i> |
| </a> |
| <a href="#" class="text-gray-400 hover:text-white transition"> |
| <i class="fab fa-facebook text-xl"></i> |
| </a> |
| <a href="#" class="text-gray-400 hover:text-white transition"> |
| <i class="fab fa-instagram text-xl"></i> |
| </a> |
| </div> |
| </div> |
| <div> |
| <h4 class="font-semibold mb-4 text-gray-300">Company</h4> |
| <ul class="space-y-2"> |
| <li><a href="#" class="text-gray-400 hover:text-white transition">About Us</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Careers</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Blog</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Press</a></li> |
| </ul> |
| </div> |
| <div> |
| <h4 class="font-semibold mb-4 text-gray-300">Resources</h4> |
| <ul class="space-y-2"> |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Help Center</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Privacy Policy</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Terms of Service</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Resume Tips</a></li> |
| </ul> |
| </div> |
| </div> |
| <div class="border-t border-gray-800 mt-10 pt-6 flex flex-col md:flex-row justify-between items-center"> |
| <p class="text-gray-500 text-sm">© 2023 Resume Matcher. All rights reserved.</p> |
| <div class="flex space-x-6 mt-4 md:mt-0"> |
| <a href="#" class="text-gray-500 hover:text-white text-sm transition">Privacy Policy</a> |
| <a href="#" class="text-gray-500 hover:text-white text-sm transition">Terms of Service</a> |
| <a href="#" class="text-gray-500 hover:text-white text-sm transition">Cookies</a> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| |
| pdfjsLib = window['pdfjs-dist/build/pdf']; |
| pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.worker.min.js'; |
| |
| |
| let users = JSON.parse(localStorage.getItem('resumeMatcherUsers')) || []; |
| let currentUser = JSON.parse(localStorage.getItem('resumeMatcherCurrentUser')) || null; |
| let userResumes = JSON.parse(localStorage.getItem('resumeMatcherUserResumes')) || {}; |
| |
| |
| const loginModal = document.getElementById('loginModal'); |
| const loginForm = document.getElementById('loginForm'); |
| const closeLoginModal = document.getElementById('closeLoginModal'); |
| const loginButton = document.getElementById('loginButton'); |
| const loginButtonCTA = document.getElementById('loginButtonCTA'); |
| |
| const signupModal = document.getElementById('signupModal'); |
| const signupForm = document.getElementById('signupForm'); |
| const closeSignupModal = document.getElementById('closeSignupModal'); |
| const signupButton = document.getElementById('signupButton'); |
| const signupButtonCTA = document.getElementById('signupButtonCTA'); |
| |
| const showLoginForm = document.getElementById('showLoginForm'); |
| const showSignupForm = document.getElementById('showSignupForm'); |
| |
| const authButtons = document.getElementById('authButtons'); |
| const userProfile = document.getElementById('userProfile'); |
| const userName = document.getElementById('userName'); |
| const userAvatar = document.getElementById('userAvatar'); |
| const logoutButton = document.getElementById('logoutButton'); |
| |
| const loginCTASection = document.getElementById('loginCTASection'); |
| const uploadSection = document.getElementById('uploadSection'); |
| |
| const resultsContainer = document.getElementById('resultsContainer'); |
| const jobsList = document.getElementById('jobsList'); |
| const loadMoreJobs = document.getElementById('loadMoreJobs'); |
| const sortJobs = document.getElementById('sortJobs'); |
| |
| |
| const fileUpload = document.getElementById('resumeUpload'); |
| const loadingContainer = document.getElementById('loadingContainer'); |
| const progressBar = document.getElementById('progressBar'); |
| const statusText = document.getElementById('statusText'); |
| const resumePreview = document.getElementById('resumePreview'); |
| const matchPercentage = document.getElementById('matchPercentage'); |
| |
| |
| checkAuthState(); |
| |
| |
| loginButton.addEventListener('click', () => showModal(loginModal)); |
| loginButtonCTA.addEventListener('click', () => showModal(loginModal)); |
| closeLoginModal.addEventListener('click', () => hideModal(loginModal)); |
| |
| signupButton.addEventListener('click', () => showModal(signupModal)); |
| signupButtonCTA.addEventListener('click', () => showModal(signupModal)); |
| closeSignupModal.addEventListener('click', () => hideModal(signupModal)); |
| |
| showLoginForm.addEventListener('click', () => { |
| hideModal(signupModal); |
| setTimeout(() => showModal(loginModal), 300); |
| }); |
| |
| showSignupForm.addEventListener('click', () => { |
| hideModal(loginModal); |
| setTimeout(() => showModal(signupModal), 300); |
| }); |
| |
| |
| loginForm.addEventListener('submit', function(e) { |
| e.preventDefault(); |
| const email = document.getElementById('loginEmail').value; |
| const password = document.getElementById('loginPassword').value; |
| |
| const user = users.find(u => u.email === email && u.password === password); |
| |
| if (user) { |
| currentUser = { |
| id: user.id, |
| name: user.name, |
| email: user.email, |
| avatar: user.avatar |
| }; |
| |
| localStorage.setItem('resumeMatcherCurrentUser', JSON.stringify(currentUser)); |
| hideModal(loginModal); |
| checkAuthState(); |
| showToast('Logged in successfully!', 'success'); |
| } else { |
| showToast('Invalid email or password', 'error'); |
| } |
| }); |
| |
| |
| signupForm.addEventListener('submit', function(e) { |
| e.preventDefault(); |
| const name = document.getElementById('signupName').value; |
| const email = document.getElementById('signupEmail').value; |
| const password = document.getElementById('signupPassword').value; |
| |
| |
| if (users.some(u => u.email === email)) { |
| showToast('Email already registered', 'error'); |
| return; |
| } |
| |
| |
| const newUser = { |
| id: Date.now().toString(), |
| name: name, |
| email: email, |
| password: password, |
| avatar: `https://randomuser.me/api/portraits/${Math.random() > 0.5 ? 'men' : 'women'}/${Math.floor(Math.random() * 100)}.jpg` |
| }; |
| |
| users.push(newUser); |
| currentUser = { |
| id: newUser.id, |
| name: newUser.name, |
| email: newUser.email, |
| avatar: newUser.avatar |
| }; |
| |
| localStorage.setItem('resumeMatcherUsers', JSON.stringify(users)); |
| localStorage.setItem('resumeMatcherCurrentUser', JSON.stringify(currentUser)); |
| |
| hideModal(signupModal); |
| checkAuthState(); |
| showToast('Account created successfully!', 'success'); |
| }); |
| |
| |
| logoutButton.addEventListener('click', function() { |
| currentUser = null; |
| localStorage.removeItem('resumeMatcherCurrentUser'); |
| checkAuthState(); |
| resultsContainer.classList.add('hidden'); |
| showToast('Logged out successfully', 'success'); |
| }); |
| |
| |
| fileUpload.addEventListener('change', function(e) { |
| if (this.files && this.files[0]) { |
| const file = this.files[0]; |
| if (file.size > 5 * 1024 * 1024) { |
| showToast('File size exceeds 5MB limit. Please choose a smaller file.', 'error'); |
| return; |
| } |
| |
| loadingContainer.classList.remove('hidden'); |
| resultsContainer.classList.add('hidden'); |
| |
| |
| let progress = 0; |
| const statusMessages = [ |
| "Initializing analysis...", |
| "Extracting text content...", |
| "Identifying skills and keywords...", |
| "Analyzing work experience...", |
| "Matching with job database...", |
| "Calculating best matches..." |
| ]; |
| |
| const interval = setInterval(() => { |
| progress += Math.random() * 15; |
| if (progress > 100) progress = 100; |
| progressBar.style.width = progress + '%'; |
| |
| if (progress >= 20) statusText.textContent = statusMessages[1]; |
| if (progress >= 35) statusText.textContent = statusMessages[2]; |
| if (progress >= 50) statusText.textContent = statusMessages[3]; |
| if (progress >= 70) statusText.textContent = statusMessages[4]; |
| if (progress >= 85) statusText.textContent = statusMessages[5]; |
| |
| if (progress === 100) { |
| clearInterval(interval); |
| setTimeout(() => { |
| loadingContainer.classList.add('hidden'); |
| extractResumeData(file); |
| }, 500); |
| } |
| }, 300); |
| } |
| }); |
| |
| |
| const dropArea = document.querySelector('.file-upload'); |
| |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { |
| dropArea.addEventListener(eventName, preventDefaults, false); |
| }); |
| |
| function preventDefaults(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| } |
| |
| ['dragenter', 'dragover'].forEach(eventName => { |
| dropArea.addEventListener(eventName, highlight, false); |
| }); |
| |
| ['dragleave', 'drop'].forEach(eventName => { |
| dropArea.addEventListener(eventName, unhighlight, false); |
| }); |
| |
| function highlight() { |
| dropArea.classList.add('border-primary-500', 'bg-blue-100'); |
| dropArea.classList.remove('border-primary-300'); |
| } |
| |
| function unhighlight() { |
| dropArea.classList.remove('border-primary-500', 'bg-blue-100'); |
| dropArea.classList.add('border-primary-300'); |
| } |
| |
| dropArea.addEventListener('drop', handleDrop, false); |
| |
| function handleDrop(e) { |
| const dt = e.dataTransfer; |
| const files = dt.files; |
| if (files.length > 0) { |
| fileUpload.files = files; |
| const event = new Event('change'); |
| fileUpload.dispatchEvent(event); |
| } |
| } |
| |
| |
| const tabJobs = document.getElementById('tabJobs'); |
| const tabSkills = document.getElementById('tabSkills'); |
| const tabResume = document.getElementById('tabResume'); |
| const jobsTab = document.getElementById('jobsTab'); |
| const skillsTab = document.getElementById('skillsTab'); |
| const resumeTab = document.getElementById('resumeTab'); |
| |
| tabJobs.addEventListener('click', () => { |
| setActiveTab(tabJobs, jobsTab); |
| }); |
| |
| tabSkills.addEventListener('click', () => { |
| setActiveTab(tabSkills, skillsTab); |
| }); |
| |
| tabResume.addEventListener('click', () => { |
| setActiveTab(tabResume, resumeTab); |
| }); |
| |
| |
| sortJobs.addEventListener('change', function() { |
| if (currentUser && userResumes[currentUser.id]) { |
| const resumeData = userResumes[currentUser.id]; |
| renderJobs(resumeData.matchedJobs, this.value); |
| } |
| }); |
| |
| |
| loadMoreJobs.addEventListener('click', function() { |
| |
| if (currentUser && userResumes[currentUser.id]) { |
| const resumeData = userResumes[currentUser.id]; |
| const newJobs = generateMoreJobs(resumeData.resumeContent.includes('developer') || |
| resumeData.resumeContent.includes('engineer')); |
| resumeData.matchedJobs = [...resumeData.matchedJobs, ...newJobs]; |
| userResumes[currentUser.id] = resumeData; |
| localStorage.setItem('resumeMatcherUserResumes', JSON.stringify(userResumes)); |
| |
| renderJobs(resumeData.matchedJobs, sortJobs.value); |
| showToast('Loaded more job matches', 'success'); |
| } |
| }); |
| |
| |
| function checkAuthState() { |
| if (currentUser) { |
| authButtons.classList.add('hidden'); |
| userProfile.classList.remove('hidden'); |
| userName.textContent = currentUser.name; |
| userAvatar.src = currentUser.avatar; |
| |
| loginCTASection.classList.add('hidden'); |
| uploadSection.classList.remove('hidden'); |
| |
| |
| if (userResumes[currentUser.id]) { |
| displayResults(userResumes[currentUser.id]); |
| } |
| } else { |
| authButtons.classList.remove('hidden'); |
| userProfile.classList.add('hidden'); |
| |
| loginCTASection.classList.remove('hidden'); |
| uploadSection.classList.add('hidden'); |
| } |
| } |
| |
| function showModal(modal) { |
| const form = modal.querySelector('.auth-form'); |
| modal.classList.remove('hidden'); |
| setTimeout(() => { |
| form.classList.remove('translate-y-4'); |
| form.classList.remove('opacity-0'); |
| }, 10); |
| } |
| |
| function hideModal(modal) { |
| const form = modal.querySelector('.auth-form'); |
| form.classList.add('translate-y-4'); |
| form.classList.add('opacity-0'); |
| setTimeout(() => { |
| modal.classList.add('hidden'); |
| modal.querySelector('form').reset(); |
| }, 300); |
| } |
| |
| function showToast(message, type) { |
| const toast = document.createElement('div'); |
| toast.className = `fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-md text-white font-medium flex items-center |
| ${type === 'success' ? 'bg-green-500' : 'bg-red-500'}`; |
| toast.innerHTML = ` |
| <i class="fas ${type === 'success' ? 'fa-check-circle' : 'fa-exclamation-circle'} mr-2"></i> |
| ${message} |
| `; |
| document.body.appendChild(toast); |
| |
| setTimeout(() => { |
| toast.classList.add('opacity-0', 'translate-y-2'); |
| setTimeout(() => toast.remove(), 300); |
| }, 3000); |
| } |
| |
| function setActiveTab(tabElement, contentElement) { |
| |
| document.querySelectorAll('.tabs button').forEach(tab => { |
| tab.classList.remove('active'); |
| tab.querySelector('div').classList.add('bg-transparent'); |
| tab.querySelector('div').classList.remove('bg-primary-600'); |
| }); |
| |
| |
| document.querySelectorAll('.tab-content').forEach(content => { |
| content.classList.add('hidden'); |
| }); |
| |
| |
| tabElement.classList.add('active'); |
| tabElement.querySelector('div').classList.remove('bg-transparent'); |
| tabElement.querySelector('div').classList.add('bg-primary-600'); |
| |
| |
| contentElement.classList.remove('hidden'); |
| } |
| |
| function extractResumeData(file) { |
| const reader = new FileReader(); |
| let resumeContent = ''; |
| |
| if (file.type === 'application/pdf') { |
| reader.onload = function(e) { |
| const typedArray = new Uint8Array(e.target.result); |
| |
| |
| pdfjsLib.getDocument(typedArray).promise.then(function(pdf) { |
| let pagesContent = []; |
| for (let i = 1; i <= pdf.numPages; i++) { |
| pdf.getPage(i).then(function(page) { |
| page.getTextContent().then(function(textContent) { |
| const pageText = textContent.items.map(item => item.str).join(' '); |
| pagesContent.push(pageText); |
| |
| if (pagesContent.length === pdf.numPages) { |
| resumeContent = pagesContent.join('\n'); |
| processResume(resumeContent, file.name); |
| } |
| }); |
| }); |
| } |
| }).catch(function(error) { |
| console.error('PDF processing error:', error); |
| showToast('Error processing PDF. Please try another file.', 'error'); |
| loadingContainer.classList.add('hidden'); |
| }); |
| }; |
| |
| reader.readAsArrayBuffer(file); |
| } else { |
| |
| reader.onload = function(e) { |
| resumeContent = e.target.result; |
| processResume(resumeContent, file.name); |
| }; |
| |
| if (file.type.includes('text') || file.name.endsWith('.txt')) { |
| reader.readAsText(file); |
| } else if (file.type.includes('msword') || file.name.endsWith('.docx')) { |
| |
| reader.readAsText(file); |
| } else { |
| reader.readAsText(file); |
| } |
| } |
| } |
| |
| function processResume(resumeContent, fileName) { |
| |
| const commonSkills = [ |
| 'JavaScript', 'React', 'Node.js', 'Python', 'Java', 'SQL', 'AWS', 'Docker', |
| 'Leadership', 'Project Management', 'Communication', 'Teamwork', 'Analytical Skills', |
| 'HTML', 'CSS', 'Git', 'REST API', 'Machine Learning', 'Data Analysis' |
| ]; |
| |
| const extractedSkills = commonSkills.filter(skill => { |
| const regex = new RegExp('\\b' + skill.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + '\\b', 'i'); |
| return regex.test(resumeContent); |
| }).slice(0, 12); |
| |
| const requiredFields = ['skills', 'experience', 'education']; |
| const completenessScore = requiredFields.filter(field => |
| new RegExp(field, 'i').test(resumeContent) |
| ).length / requiredFields.length * 100; |
| |
| |
| const isTechnical = /developer|engineer|programmer|software|web|frontend|backend|full.?stack/i.test(resumeContent); |
| |
| |
| const matchedJobs = fetchMatchingJobs(isTechnical, extractedSkills, 5); |
| |
| |
| const resumeData = { |
| fileName: fileName, |
| resumeContent: resumeContent, |
| extractedSkills: extractedSkills, |
| matchedJobs: matchedJobs, |
| completenessScore: Math.round(completenessScore), |
| uploadedAt: new Date().toISOString() |
| }; |
| |
| |
| if (currentUser) { |
| userResumes[currentUser.id] = resumeData; |
| localStorage.setItem('resumeMatcherUserResumes', JSON.stringify(userResumes)); |
| } |
| |
| displayResults(resumeData); |
| } |
| |
| function fetchMatchingJobs(isTechnical, skills, count) { |
| |
| |
| const allJobs = generateMockJobs(isTechnical); |
| |
| |
| return allJobs.map(job => { |
| const matchedSkills = job.skills.filter(skill => |
| skills.some(s => s.toLowerCase() === skill.toLowerCase()) |
| ); |
| |
| const matchScore = Math.min(Math.round((matchedSkills.length / job.skills.length) * 100), 100); |
| |
| return { |
| ...job, |
| matchScore: matchScore, |
| matchedSkills: matchedSkills |
| }; |
| }) |
| .sort((a, b) => b.matchScore - a.matchScore) |
| .slice(0, count); |
| } |
| |
| function displayResults(resumeData) { |
| |
| matchPercentage.textContent = resumeData.matchedJobs.length > 0 ? |
| Math.max(...resumeData.matchedJobs.map(job => job.matchScore)) + '%' : '0%'; |
| |
| |
| renderResumePreview(resumeData); |
| |
| |
| renderJobs(resumeData.matchedJobs, sortJobs.value); |
| |
| |
| renderSkillsAnalysis(resumeData); |
| |
| |
| resultsContainer.classList.remove('hidden'); |
| setActiveTab(tabJobs, jobsTab); |
| } |
| |
| function renderResumePreview(resumeData) { |
| resumePreview.innerHTML = ''; |
| |
| |
| const preview = document.createElement('div'); |
| preview.className = 'resume'; |
| |
| |
| let highlightedContent = resumeData.resumeContent; |
| resumeData.extractedSkills.forEach(skill => { |
| const regex = new RegExp('\\b' + skill.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + '\\b', 'gi'); |
| highlightedContent = highlightedContent.replace(regex, |
| match => `<span class="bg-yellow-100 text-yellow-800 px-1 rounded">${match}</span>` |
| ); |
| }); |
| |
| preview.innerHTML = ` |
| <div class="mb-4 flex justify-between items-center"> |
| <h2 class="text-xl font-bold">Resume Preview: ${resumeData.fileName}</h2> |
| <span class="text-sm bg-gray-100 text-gray-600 px-2 py-1 rounded"> |
| ${resumeData.completenessScore}% Complete |
| </span> |
| </div> |
| <div class="bg-gray-50 p-4 rounded-lg border border-gray-200"> |
| <pre class="whitespace-pre-wrap font-sans text-gray-700">${highlightedContent}</pre> |
| </div> |
| `; |
| resumePreview.appendChild(preview); |
| } |
| |
| function renderJobs(jobs, sortBy = 'match') { |
| jobsList.innerHTML = ''; |
| |
| if (jobs.length === 0) { |
| jobsList.innerHTML = ` |
| <div class="text-center py-12"> |
| <i class="fas fa-search text-4xl text-gray-400 mb-4"></i> |
| <h3 class="text-lg font-medium text-gray-600 mb-2">No matching jobs found</h3> |
| <p class="text-gray-500">Try adjusting your skills or searching for different positions.</p> |
| </div> |
| `; |
| return; |
| } |
| |
| |
| let sortedJobs = [...jobs]; |
| switch (sortBy) { |
| case 'salary': |
| sortedJobs.sort((a, b) => { |
| const aSalary = parseInt(a.salary.replace(/[^0-9]/g, '')); |
| const bSalary = parseInt(b.salary.replace(/[^0-9]/g, '')); |
| return bSalary - aSalary; |
| }); |
| break; |
| case 'recent': |
| sortedJobs.sort((a, b) => new Date(b.postedAt) - new Date(a.postedAt)); |
| break; |
| case 'location': |
| |
| sortedJobs.sort((a, b) => a.location.localeCompare(b.location)); |
| break; |
| default: |
| sortedJobs.sort((a, b) => b.matchScore - a.matchScore); |
| } |
| |
| sortedJobs.forEach(job => { |
| const jobCard = document.createElement('div'); |
| jobCard.className = 'job-card bg-white rounded-xl shadow-soft border border-gray-100 overflow-hidden'; |
| jobCard.innerHTML = ` |
| <div class="p-6"> |
| <div class="flex items-start justify-between mb-4"> |
| <div class="flex items-start"> |
| <div class="w-12 h-12 rounded-lg ${job.logo} flex items-center justify-center text-white mr-4 mt-0.5"> |
| ${job.company.charAt(0)} |
| </div> |
| <div> |
| <h3 class="text-xl font-bold text-dark">${job.title}</h3> |
| <p class="text-gray-600">${job.company} • ${job.companyType}</p> |
| </div> |
| </div> |
| <div class="flex flex-col items-end"> |
| <span class="match-badge text-xs font-bold px-2.5 py-1 rounded-full mb-2"> |
| ${job.matchScore}% Match |
| </span> |
| <span class="text-xs text-gray-500">${formatRelativeTime(job.postedAt)}</span> |
| </div> |
| </div> |
| |
| <p class="text-gray-700 mb-4">${job.description}</p> |
| |
| <div class="flex items-center text-sm text-gray-600 mb-4"> |
| <span class="mr-4"><i class="fas fa-map-marker-alt mr-1"></i> ${job.location}</span> |
| <span class="mr-4"><i class="fas fa-clock mr-1"></i> ${job.type}</span> |
| <span><i class="fas fa-dollar-sign mr-1"></i> ${job.salary}</span> |
| </div> |
| |
| <div class="mb-4"> |
| <p class="text-sm text-gray-600 mb-2">Matched Skills:</p> |
| <div class="flex flex-wrap gap-2"> |
| ${job.matchedSkills.map(skill => ` |
| <span class="skill-pill bg-primary-100 text-primary-700 text-xs font-medium px-3 py-1 rounded-full"> |
| ${skill} |
| </span> |
| `).join('')} |
| </div> |
| </div> |
| |
| <div class="flex justify-between items-center"> |
| <div class="text-sm"> |
| <span class="text-gray-500">${Math.floor(Math.random() * 50) + 10} applicants</span> |
| ${job.urgent ? `<span class="ml-2 bg-red-100 text-red-800 px-2 py-0.5 rounded-full text-xs">Urgent hiring</span>` : ''} |
| </div> |
| <div class="flex items-center space-x-2"> |
| <button class="border border-primary-500 text-primary-600 hover:bg-primary-50 text-sm font-medium px-3 py-1 rounded-md transition"> |
| <i class="far fa-bookmark mr-1"></i> Save |
| </button> |
| <button class="bg-primary-600 hover:bg-primary-700 text-white text-sm font-medium px-4 py-1.5 rounded-md transition ${job.easyApply ? 'shadow-md hover:shadow-glow' : 'bg-opacity-80 hover:bg-opacity-100'}"> |
| ${job.easyApply ? '<i class="fas fa-bolt mr-"></i> Easy Apply' : 'Apply Now'} |
| </button> |
| </div> |
| </div> |
| </div> |
| `; |
| jobsList.appendChild(jobCard); |
| }); |
| } |
| |
| function renderSkillsAnalysis(resumeData) { |
| topSkills.innerHTML = ''; |
| suggestedSkills.innerHTML = ''; |
| |
| |
| resumeData.extractedSkills.forEach(skill => { |
| const skillItem = document.createElement('div'); |
| skillItem.className = 'skill-item'; |
| |
| |
| const level = Math.min(Math.floor(Math.random() * 30) + 70, 100); |
| |
| skillItem.innerHTML = ` |
| <div class="flex justify-between items-center mb-1.5"> |
| <span class="font-medium text-gray-800">${skill}</span> |
| <span class="text-sm text-gray-500">${level}% proficiency</span> |
| </div> |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> |
| <div class="progress-bar bg-gradient-to-r from-primary-500 to-primary-400 h-2.5 rounded-full" style="width: ${level}%"></div> |
| </div> |
| `; |
| topSkills.appendChild(skillItem); |
| }); |
| |
| |
| const allSkills = [ |
| 'React', 'Node.js', 'TypeScript', 'GraphQL', 'Docker', |
| 'AWS', 'Kubernetes', 'Python', 'Machine Learning', |
| 'Leadership', 'Public Speaking', 'Project Management', 'Agile' |
| ]; |
| |
| const suggested = allSkills.filter(skill => |
| !resumeData.extractedSkills.includes(skill) |
| ).slice(0, 5).map(skill => ({ |
| skill: skill, |
| reason: getSkillReason(skill) |
| })); |
| |
| suggested.forEach(item => { |
| const skillItem = document.createElement('div'); |
| skillItem.className = 'skill-item bg-blue-50 p-4 rounded-lg hover:bg-blue-100 transition cursor-pointer'; |
| skillItem.innerHTML = ` |
| <div class="flex items-start"> |
| <div class="flex-shrink-0 h-5 w-5 rounded-full bg-blue-200 text-blue-700 flex items-center justify-center mr-3 mt-0.5"> |
| <i class="fas fa-lightbulb text-xs"></i> |
| </div> |
| <div> |
| <h4 class="font-medium text-dark mb-1">${item.skill}</h4> |
| <p class="text-sm text-gray-600">${item.reason}</p> |
| </div> |
| </div> |
| `; |
| suggestedSkills.appendChild(skillItem); |
| }); |
| |
| |
| const categories = [ |
| { name: 'Software Development', match: 78 + Math.floor(Math.random() * 20) }, |
| { name: 'Product Management', match: 45 + Math.floor(Math.random() * 30) }, |
| { name: 'Data Science', match: 35 + Math.floor(Math.random() * 40) }, |
| { name: 'DevOps Engineering', match: 60 + Math.floor(Math.random() * 25) } |
| ].sort((a, b) => b.match - a.match); |
| |
| const jobCategories = document.getElementById('jobCategories'); |
| jobCategories.innerHTML = ''; |
| |
| categories.forEach(cat => { |
| const categoryItem = document.createElement('div'); |
| categoryItem.className = 'mb-4'; |
| categoryItem.innerHTML = ` |
| <div class="flex justify-between text-sm mb-1"> |
| <span>${cat.name}</span> |
| <span>${cat.match}% match</span> |
| </div> |
| <div class="w-full bg-gray-200 rounded-full h-2"> |
| <div class="bg-${cat.match > 70 ? 'primary' : cat.match > 50 ? 'yellow' : 'orange'}-500 h-2 rounded-full" style="width: ${cat.match}%"></div> |
| </div> |
| `; |
| jobCategories.appendChild(categoryItem); |
| }); |
| } |
| |
| function getSkillReason(skill) { |
| const reasons = { |
| 'React': 'Most in-demand frontend framework with thousands of jobs available', |
| 'Node.js': 'Essential for full-stack JavaScript developers - used by major companies', |
| 'TypeScript': 'Growing adoption in enterprise applications - improves code quality', |
| 'GraphQL': 'Modern API technology becoming standard for new projects', |
| 'Docker': 'Key containerization technology used in DevOps workflows', |
| 'AWS': 'Dominant cloud platform with many certification opportunities', |
| 'Kubernetes': 'Critical for scaling cloud-native applications', |
| 'Python': 'Versatile language used in web development, data science, and automation', |
| 'Machine Learning': 'High-paying field with increasing demand across industries', |
| 'Leadership': 'Essential for career advancement and management roles', |
| 'Public Speaking': 'Improves communication and professional presence', |
| 'Project Management': 'Valuable skill for leading teams and initiatives', |
| 'Agile': 'Standard methodology for software development teams' |
| }; |
| |
| return reasons[skill] || 'High-demand skill that could expand your career opportunities'; |
| } |
| |
| function generateMockJobs(isTechnical) { |
| if (isTechnical) { |
| return [ |
| { |
| id: 'job1', |
| title: "Senior Full Stack Developer (React/Node)", |
| company: "TechHub Inc.", |
| companyType: "Series B Startup", |
| location: "San Francisco, CA (Remote OK)", |
| salary: "$130,000 - $160,000", |
| description: "Lead development of innovative web applications using React and Node.js. Ideal for developers with 5+ years of full-stack experience who enjoy working in fast-paced environments.", |
| skills: ["JavaScript", "React", "Node.js", "TypeScript", "AWS", "CI/CD"], |
| postedAt: new Date(Date.now() - 86400000 * 2).toISOString(), |
| type: "Full-time", |
| logo: "bg-gradient-to-r from-indigo-500 to-purple-600", |
| easyApply: true, |
| urgent: true |
| }, |
| { |
| id: 'job2', |
| title: "Frontend Engineer - React Specialist", |
| company: "Digital Creations", |
| companyType: "Enterprise", |
| location: "New York, NY", |
| salary: "$120,000 - $150,000", |
| description: "Build beautiful, responsive user interfaces using React and modern frontend tooling. Strong focus on performance optimization and accessibility.", |
| skills: ["JavaScript", "React", "Redux", "CSS", "Performance", "Accessibility"], |
| postedAt: new Date(Date.now() - 86400000).toISOString(), |
| type: "Full-time", |
| logo: "bg-gradient-to-r from-blue-500 to-blue-700", |
| easyApply: false, |
| urgent: false |
| }, |
| { |
| id: 'job3', |
| title: "Backend API Developer (Node.js)", |
| company: "Cloud Systems", |
| companyType: "Product Company", |
| location: "Remote (US only)", |
| salary: "$115,000 - $140,000", |
| description: "Develop scalable microservices and RESTful APIs with Node.js. Experience with database design and cloud architecture required.", |
| skills: ["Node.js", "Express", "REST API", "MongoDB", "AWS"], |
| postedAt: new Date(Date.now() - 86400000 * 4).toISOString(), |
| type: "Full-time", |
| logo: "bg-gradient-to-r from-green-500 to-emerald-600", |
| easyApply: true, |
| urgent: false |
| }, |
| { |
| id: 'job4', |
| title: "DevOps Engineer (AWS, Kubernetes)", |
| company: "System Solutions", |
| companyType: "Consulting Firm", |
| location: "Chicago, IL", |
| salary: "$125,000 - $155,000", |
| description: "Implement CI/CD pipelines and cloud infrastructure for enterprise clients. Strong experience with containerization and infrastructure as code required.", |
| skills: ["AWS", "Docker", "Kubernetes", "CI/CD", "Terraform"], |
| postedAt: new Date(Date.now() - 86400000 * 3).toISOString(), |
| type: "Full-time", |
| logo: "bg-gradient-to-r from-purple-500 to-indigo-500", |
| easyApply: false, |
| urgent: true |
| } |
| ]; |
| } else { |
| return [ |
| { |
| id: 'job5', |
| title: "Senior Project Manager", |
| company: "Enterprise Solutions", |
| companyType: "Consulting Firm", |
| location: "Boston, MA", |
| salary: "$95,000 - $125,000", |
| description: "Manage complex projects across multiple departments. PMP certification preferred with 5+ years of experience leading enterprise initiatives.", |
| skills: ["Project Management", "Leadership", "Agile", "Risk Management"], |
| postedAt: new Date(Date.now() - 86400000 * 2).toISOString(), |
| type: "Full-time", |
| logo: "bg-gradient-to-r from-blue-600 to-blue-800", |
| easyApply: true, |
| urgent: false |
| }, |
| { |
| id: 'job6', |
| title: "Product Manager", |
| company: "Digital Platform Co", |
| companyType: "Tech Startup", |
| location: "Austin, TX", |
| salary: "$105,000 - $135,000", |
| description: "Lead product development from conception to launch. Work closely with engineering and design teams to deliver exceptional user experiences.", |
| skills: ["Product Management", "Agile", "User Research", "Roadmapping"], |
| postedAt: new Date(Date.now() - 86400000).toISOString(), |
| type: "Full-time", |
| logo: "bg-gradient-to-r from-indigo-400 to-violet-500", |
| easyApply: false, |
| urgent: true |
| } |
| ]; |
| } |
| } |
| |
| function generateMoreJobs(isTechnical) { |
| if (isTechnical) { |
| return [ |
| { |
| id: 'job7-' + Date.now(), |
| title: "JavaScript Architect", |
| company: "Framework Labs", |
| companyType: "Tech Company", |
| location: "Remote (Global)", |
| salary: "$140,000 - $180,000", |
| description: "Design and implement architectural solutions for large-scale JavaScript applications. Expert knowledge of design patterns and performance optimization required.", |
| skills: ["JavaScript", "Architecture", "Performance", "Design Patterns", "Mentoring"], |
| postedAt: new Date(Date.now() - 86400000 * 5).toISOString(), |
| type: "Full-time", |
| logo: "bg-gradient-to-r from-orange-500 to-red-500", |
| easyApply: false, |
| urgent: false |
| }, |
| { |
| id: 'job8-' + Date.now(), |
| title: "Full Stack Developer (TypeScript)", |
| company: "ByteDance Systems", |
| companyType: "Product Company", |
| location: "Seattle, WA", |
| salary: "$125,000 - $150,000", |
| description: "Develop full-stack applications with TypeScript, React, and Node.js. Experience with automated testing and documentation required.", |
| skills: ["TypeScript", "React", "Node.js", "Testing", "Documentation"], |
| postedAt: new Date(Date.now() - 86400000 * 6).toISOString(), |
| type: "Full-time", |
| logo: "bg-gradient-to-r from-teal-500 to-cyan-500", |
| easyApply: true, |
| urgent: false |
| } |
| ]; |
| } else { |
| return [ |
| { |
| id: 'job9-' + Date.now(), |
| title: "Technical Product Owner", |
| company: "Agile Enterprises", |
| companyType: "Consulting Firm", |
| location: "Denver, CO", |
| salary: "$110,000 - $140,000", |
| description: "Bridge the gap between business and technology as a product owner for digital platforms. Technical background preferred but not required.", |
| skills: ["Product Ownership", "Agile", "Technical", "Roadmapping"], |
| postedAt: new Date(Date.now() - 86400000 * 4).toISOString(), |
| type: "Full-time", |
| logo: "bg-gradient-to-r from-pink-500 to-rose-500", |
| easyApply: false, |
| urgent: false |
| } |
| ]; |
| } |
| } |
| |
| function formatRelativeTime(dateString) { |
| const date = new Date(dateString); |
| const now = new Date(); |
| const diffInMs = now - date; |
| const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24)); |
| |
| if (diffInDays === 0) { |
| return "Today"; |
| } else if (diffInDays === 1) { |
| return "Yesterday"; |
| } else if (diffInDays < 7) { |
| return `${diffInDays} days ago`; |
| } else if (diffInDays < 30) { |
| const weeks = Math.floor(diffInDays / 7); |
| return `${weeks} week${weeks > 1 ? 's' : ''} ago`; |
| } else { |
| return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); |
| } |
| } |
| }); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=SamOp224/sam-spqce" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body> |
| </html> |