sam-spqce / index.html
SamOp224's picture
Add 1 files
5b4c232 verified
<!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">
<!-- Login Modal -->
<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>
<!-- Signup Modal -->
<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>
<!-- Header -->
<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>
<!-- Upload Section (Visible when logged in) -->
<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>
<!-- Login CTA (Visible when not logged in) -->
<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>
<!-- Results Section -->
<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">
<!-- Job cards will be inserted here -->
</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">
<!-- Categories will be inserted here -->
</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">
<!-- Top skills will be inserted here -->
</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">
<!-- Suggested skills will be inserted here -->
</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">
<!-- Resume content will be inserted here -->
</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() {
// Initialize PDF.js
pdfjsLib = window['pdfjs-dist/build/pdf'];
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.worker.min.js';
// User storage (simulated)
let users = JSON.parse(localStorage.getItem('resumeMatcherUsers')) || [];
let currentUser = JSON.parse(localStorage.getItem('resumeMatcherCurrentUser')) || null;
let userResumes = JSON.parse(localStorage.getItem('resumeMatcherUserResumes')) || {};
// UI Elements
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');
// Resume upload elements
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');
// Set initial UI state based on auth
checkAuthState();
// Auth Modal Controls
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);
});
// Login Form
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');
}
});
// Signup Form
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;
// Check if user already exists
if (users.some(u => u.email === email)) {
showToast('Email already registered', 'error');
return;
}
// Create new user
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');
});
// Logout Function
logoutButton.addEventListener('click', function() {
currentUser = null;
localStorage.removeItem('resumeMatcherCurrentUser');
checkAuthState();
resultsContainer.classList.add('hidden');
showToast('Logged out successfully', 'success');
});
// File upload handling
fileUpload.addEventListener('change', function(e) {
if (this.files && this.files[0]) {
const file = this.files[0];
if (file.size > 5 * 1024 * 1024) { // 5MB limit
showToast('File size exceeds 5MB limit. Please choose a smaller file.', 'error');
return;
}
loadingContainer.classList.remove('hidden');
resultsContainer.classList.add('hidden');
// Simulate processing
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);
}
});
// Drag and drop functionality
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);
}
}
// Tab handling
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);
});
// Sort jobs
sortJobs.addEventListener('change', function() {
if (currentUser && userResumes[currentUser.id]) {
const resumeData = userResumes[currentUser.id];
renderJobs(resumeData.matchedJobs, this.value);
}
});
// Load more jobs
loadMoreJobs.addEventListener('click', function() {
// In a real app, this would fetch more jobs from an API
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');
}
});
// Helper Functions
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');
// Load user's previous resume if exists
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) {
// Reset all tabs
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');
});
// Hide all content
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.add('hidden');
});
// Activate selected tab
tabElement.classList.add('active');
tabElement.querySelector('div').classList.remove('bg-transparent');
tabElement.querySelector('div').classList.add('bg-primary-600');
// Show selected content
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);
// Extract text from PDF
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 {
// For text-based files
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')) {
// Note: Full DOCX parsing would require additional libraries
reader.readAsText(file);
} else {
reader.readAsText(file);
}
}
}
function processResume(resumeContent, fileName) {
// Extract skills using regex patterns (simplified version)
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;
// Check for common job titles to determine profile type
const isTechnical = /developer|engineer|programmer|software|web|frontend|backend|full.?stack/i.test(resumeContent);
// Generate matched jobs
const matchedJobs = fetchMatchingJobs(isTechnical, extractedSkills, 5);
// Create resume data for storage
const resumeData = {
fileName: fileName,
resumeContent: resumeContent,
extractedSkills: extractedSkills,
matchedJobs: matchedJobs,
completenessScore: Math.round(completenessScore),
uploadedAt: new Date().toISOString()
};
// Save to user data
if (currentUser) {
userResumes[currentUser.id] = resumeData;
localStorage.setItem('resumeMatcherUserResumes', JSON.stringify(userResumes));
}
displayResults(resumeData);
}
function fetchMatchingJobs(isTechnical, skills, count) {
// In a real app, this would call an API to find matching jobs
// For demo, we'll use mock data filtered by skills
const allJobs = generateMockJobs(isTechnical);
// Simple matching algorithm based on skills
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) {
// Set match percentage
matchPercentage.textContent = resumeData.matchedJobs.length > 0 ?
Math.max(...resumeData.matchedJobs.map(job => job.matchScore)) + '%' : '0%';
// Render resume preview
renderResumePreview(resumeData);
// Render matched jobs
renderJobs(resumeData.matchedJobs, sortJobs.value);
// Render skills analysis
renderSkillsAnalysis(resumeData);
// Show results
resultsContainer.classList.remove('hidden');
setActiveTab(tabJobs, jobsTab);
}
function renderResumePreview(resumeData) {
resumePreview.innerHTML = '';
// Simple preview - in a real app, would properly parse and format
const preview = document.createElement('div');
preview.className = 'resume';
// Highlight skills
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;
}
// Sort jobs
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':
// Simplified location sorting
sortedJobs.sort((a, b) => a.location.localeCompare(b.location));
break;
default: // match score
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 = '';
// Render extracted skills
resumeData.extractedSkills.forEach(skill => {
const skillItem = document.createElement('div');
skillItem.className = 'skill-item';
// Random skill level for demo
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);
});
// Render suggested skills
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);
});
// Render job categories
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(), // 2 days ago
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(), // 1 day ago
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(), // 4 days ago
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(), // 3 days ago
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(), // 2 days ago
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(), // 1 day ago
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(), // 5 days ago
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(), // 6 days ago
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(), // 4 days ago
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>