-
-
Notifications
You must be signed in to change notification settings - Fork 266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Currency System with Rewards for Resume Rating #926
Conversation
Warning Rate limit exceeded@GarimaSingh0109 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 20 minutes and 48 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. WalkthroughThe changes in this pull request introduce a new CSS stylesheet, HTML structure, and JavaScript functionality for the Changes
Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 31
🧹 Outside diff range and nitpick comments (4)
RateMyResume.js (1)
1-80
: Consider implementing a more robust architecture.
The current implementation would benefit from:
- Proper separation of concerns (UI, business logic, data management)
- Type checking (consider TypeScript)
- Error boundaries for better error handling
- State management solution for larger scale
- Unit tests for critical functionality
This will improve maintainability and scalability as the application grows.
RateMyResume.css (1)
1-246
: Consider implementing a CSS architecture pattern.
While the styles are functional, implementing a CSS architecture pattern like BEM (Block Element Modifier) would improve maintainability and scalability. This would be particularly beneficial for the SkillCoins reward system as it grows.
Additionally, consider:
- Creating a design system with reusable variables for colors, spacing, and typography
- Implementing a mobile-first approach
- Adding CSS comments to document complex selectors and calculations
Would you like help setting up a CSS architecture pattern for this project?
resume.html (1)
Line range hint 129-161
: Improve ID naming conventions for form fields.
The "PH" suffix in IDs (institutePH, coursePH, jobrolePH) is not a common naming convention and might be confusing. Consider using more descriptive names.
Apply these changes:
- <input type="text" id="institutePH" placeholder="Enter institute name">
- <input type="text" id="coursePH" placeholder="Enter course name">
- <input type="text" id="jobrolePH" placeholder="Enter job position">
+ <input type="text" id="institute-name" placeholder="Enter institute name">
+ <input type="text" id="course-name" placeholder="Enter course name">
+ <input type="text" id="job-position" placeholder="Enter job position">
Resume.js (1)
91-99
: Add input validation for project links
In the project entries, the project link input field accepts URLs. Consider adding type="url"
to the input field to ensure that users enter valid URLs.
Apply this diff to enhance input validation:
<label>Link (Optional):</label>
- <input type="url" placeholder="Enter project link">
+ <input type="url" placeholder="Enter project link">
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (2)
RateMyResumeData/mohitresume.pdf
is excluded by!**/*.pdf
images/hero.jpg
is excluded by!**/*.jpg
📒 Files selected for processing (7)
- RateMyResume.css (1 hunks)
- RateMyResume.html (1 hunks)
- RateMyResume.js (1 hunks)
- Resume.css (4 hunks)
- Resume.js (11 hunks)
- resume.html (7 hunks)
- resume2.js (2 hunks)
🔇 Additional comments (7)
RateMyResume.html (2)
108-110
: Verify SkillCoins reward implementation.
The submit buttons for ratings need to be connected to the SkillCoins reward system as mentioned in the PR objectives.
Let's verify the reward system implementation:
#!/bin/bash
# Check for SkillCoins reward implementation
echo "Checking SkillCoins reward system..."
rg -A 10 "SkillCoins|reward|submit.*rating" --type js
# Check for event handlers on submit buttons
rg -A 5 "submitbtn.*click|submit.*handler" --type js
Also applies to: 126-128, 144-146
76-91
: Verify security measures for form submission.
The resume upload form needs additional security measures:
- CSRF protection
- File size and type validation
- Secure form submission handling
Let's verify the security implementation:
resume.html (1)
20-20
: Verify the existence of RateMyResume.html page.
The navigation link for the new SkillCoins currency system has been added. Let's verify the target page exists.
✅ Verification successful
Link to RateMyResume.html is properly implemented
The verification confirms that:
- RateMyResume.html exists in the codebase
- It contains references to SkillCoins functionality
- It's properly linked from resume.html
- The file is self-referential (contains internal links)
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if RateMyResume.html exists
if [ -f "RateMyResume.html" ]; then
echo "RateMyResume.html exists"
else
echo "Warning: RateMyResume.html not found"
fi
Length of output: 89
Script:
#!/bin/bash
# Let's verify if RateMyResume.html has the necessary structure for the SkillCoins system
rg -l "SkillCoins" RateMyResume.html
# Also check if it's properly linked in other files
rg -l "RateMyResume.html" --type html
Length of output: 122
resume2.js (3)
18-18
: LGTM: Selector update aligns with HTML changes.
The selector update maintains consistency with the corresponding HTML changes.
Line range hint 1-340
: Missing implementation of SkillCoins system.
The PR objectives mention implementing a SkillCoins currency system for resume rating, but this implementation is missing from the current changes. Please add the necessary code to:
- Track SkillCoins balance
- Update balance when users rate resumes
- Implement premium feature access using SkillCoins
Let's check for any existing SkillCoins implementation:
#!/bin/bash
# Search for any SkillCoins-related code
rg -i "skillcoin|currency|reward|balance" --type=js
239-339
: Consider architectural improvements for autocomplete implementation.
The current implementation has several areas for improvement:
- Large data arrays should be moved to separate configuration files
- jQuery dependency should be verified
- Input validation is missing
- Performance optimization for large datasets
Consider these architectural improvements:
- Move data to separate files:
// config/autocomplete-data.js
export const cities = [...];
export const collegesAndUniversities = [...];
- Add error handling and validation:
$(document).ready(function () {
+ // Verify jQuery UI is available
+ if (!$.fn.autocomplete) {
+ console.error('jQuery UI autocomplete is required');
+ return;
+ }
+
+ // Common autocomplete configuration
+ const commonConfig = {
+ minLength: 1,
+ response: function(event, ui) {
+ if (ui.content.length === 0) {
+ // Handle no results
+ }
+ }
+ };
+
$("#location").autocomplete({
+ ...commonConfig,
source: cities,
- minLength: 1
});
- Consider implementing lazy loading or server-side filtering for large datasets to improve performance.
Let's verify the jQuery UI dependency:
Resume.js (1)
142-145
: Ensure consistent usage of 'skillsinput' element ID
The element ID for the skills input field is skillsinput
. Please verify that this ID matches the corresponding element in the HTML file to prevent any discrepancies.
Run the following script to verify the element ID usage:
Also applies to: 202-202, 224-224, 235-235
✅ Verification successful
Element ID 'skillsinput' is consistently used across files
The verification shows that the 'skillsinput' ID is consistently used across the codebase:
- Defined in
resume.html
as<input type="text" id="skillsinput">
- Referenced correctly in
Resume.js
usinggetElementById('skillsinput')
- Also referenced in
resume2.js
usingquerySelector('#skillsinput')
The ID matches between HTML and JavaScript files, confirming there are no discrepancies in its usage.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify that 'skillsinput' ID is consistently used in JavaScript and HTML files.
# Test: Search for 'skillsinput' in JavaScript files.
rg 'document\.getElementById\(["\']skillsinput["\']\)' *.js
# Test: Search for elements with ID 'skillsinput' in HTML files.
rg 'id=["\']skillsinput["\']' *.html
Length of output: 93
Script:
#!/bin/bash
# Let's fix the syntax and search more thoroughly
# Search for 'skillsinput' in all files
rg "skillsinput" --type-add 'web:*.{html,js,jsx,ts,tsx}' -t web
# Also search for input elements with id attribute in HTML-like files
rg 'id=["\']\w+["\']' --type-add 'web:*.{html,js,jsx,ts,tsx}' -t web
Length of output: 565
const myratinghead = document.querySelector('.myrating'); | ||
const ratingboardhead = document.querySelector('.ratingboardhead'); | ||
const rating = document.querySelector('.rating'); | ||
const ratingboard = document.querySelector('.ratingboard'); | ||
ratingboard.style.display = 'none'; | ||
|
||
ratingboardhead.addEventListener('click',()=>{ | ||
rating.style.display = 'none'; | ||
ratingboard.style.display = ''; | ||
ratingboardhead.style.backgroundColor = 'gray'; | ||
myratinghead.style.backgroundColor = 'blue'; | ||
}) | ||
|
||
myratinghead.addEventListener('click',()=>{ | ||
rating.style.display = ''; | ||
ratingboard.style.display = 'none'; | ||
myratinghead.style.backgroundColor = 'gray'; | ||
ratingboardhead.style.backgroundColor = 'blue'; | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve rating board toggle implementation.
The current implementation mixes styling concerns with JavaScript and uses hard-coded colors. Consider using CSS classes for better maintainability.
Here's a more maintainable approach:
-const myratinghead = document.querySelector('.myrating');
-const ratingboardhead = document.querySelector('.ratingboardhead');
-const rating = document.querySelector('.rating');
-const ratingboard = document.querySelector('.ratingboard');
-ratingboard.style.display = 'none';
+const sections = {
+ myRating: {
+ header: document.querySelector('.myrating'),
+ content: document.querySelector('.rating')
+ },
+ ratingBoard: {
+ header: document.querySelector('.ratingboardhead'),
+ content: document.querySelector('.ratingboard')
+ }
+};
+function toggleSection(activeSection) {
+ Object.entries(sections).forEach(([key, section]) => {
+ const isActive = key === activeSection;
+ section.content.classList.toggle('hidden', !isActive);
+ section.header.classList.toggle('active', isActive);
+ });
+}
+// Initialize state
+toggleSection('myRating');
+sections.ratingBoard.header.addEventListener('click', () => toggleSection('ratingBoard'));
+sections.myRating.header.addEventListener('click', () => toggleSection('myRating'));
Add these CSS classes:
.hidden {
display: none;
}
.active {
background-color: gray;
}
.myrating:not(.active),
.ratingboardhead:not(.active) {
background-color: blue;
}
document.getElementById('resumeForm').addEventListener('submit', function(event) { | ||
event.preventDefault(); // Prevents form submission to a backend | ||
|
||
const username = document.getElementById('username').value; | ||
const jobRole = document.getElementById('jobRole').value; | ||
const resume = document.getElementById('resume').files[0]; | ||
|
||
if (resume) { | ||
alert(`Your resume has been successfully uploaded and will appear on the Rating Board within 8 hours. Thank you!\nUsername: ${username}\nJob Role: ${jobRole}\nResume: ${resume.name}`); | ||
} else { | ||
alert('Please select a PDF file.'); | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add proper form validation and secure data handling.
Several improvements are needed for the form submission:
- Add PDF file type validation
- Implement proper error handling
- Avoid displaying sensitive information in alerts
- Add actual form submission logic
Here's the suggested implementation:
document.getElementById('resumeForm').addEventListener('submit', function(event) {
event.preventDefault(); // Prevents form submission to a backend
const username = document.getElementById('username').value;
const jobRole = document.getElementById('jobRole').value;
const resume = document.getElementById('resume').files[0];
+ // Validate required fields
+ if (!username.trim() || !jobRole.trim()) {
+ alert('Please fill in all required fields.');
+ return;
+ }
+
+ // Validate PDF file
+ if (!resume) {
+ alert('Please select a PDF file.');
+ return;
+ }
+
+ if (resume.type !== 'application/pdf') {
+ alert('Please upload a PDF file only.');
+ return;
+ }
+
+ try {
+ // TODO: Implement actual form submission
+ // const formData = new FormData();
+ // formData.append('username', username);
+ // formData.append('jobRole', jobRole);
+ // formData.append('resume', resume);
+
alert('Your resume has been successfully uploaded and will appear on the Rating Board within 8 hours. Thank you!');
- } else {
- alert('Please select a PDF file.');
+ } catch (error) {
+ console.error('Error submitting form:', error);
+ alert('An error occurred while uploading your resume. Please try again.');
}
});
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
document.getElementById('resumeForm').addEventListener('submit', function(event) { | |
event.preventDefault(); // Prevents form submission to a backend | |
const username = document.getElementById('username').value; | |
const jobRole = document.getElementById('jobRole').value; | |
const resume = document.getElementById('resume').files[0]; | |
if (resume) { | |
alert(`Your resume has been successfully uploaded and will appear on the Rating Board within 8 hours. Thank you!\nUsername: ${username}\nJob Role: ${jobRole}\nResume: ${resume.name}`); | |
} else { | |
alert('Please select a PDF file.'); | |
} | |
}); | |
document.getElementById('resumeForm').addEventListener('submit', function(event) { | |
event.preventDefault(); // Prevents form submission to a backend | |
const username = document.getElementById('username').value; | |
const jobRole = document.getElementById('jobRole').value; | |
const resume = document.getElementById('resume').files[0]; | |
// Validate required fields | |
if (!username.trim() || !jobRole.trim()) { | |
alert('Please fill in all required fields.'); | |
return; | |
} | |
// Validate PDF file | |
if (!resume) { | |
alert('Please select a PDF file.'); | |
return; | |
} | |
if (resume.type !== 'application/pdf') { | |
alert('Please upload a PDF file only.'); | |
return; | |
} | |
try { | |
// TODO: Implement actual form submission | |
// const formData = new FormData(); | |
// formData.append('username', username); | |
// formData.append('jobRole', jobRole); | |
// formData.append('resume', resume); | |
alert('Your resume has been successfully uploaded and will appear on the Rating Board within 8 hours. Thank you!'); | |
} catch (error) { | |
console.error('Error submitting form:', error); | |
alert('An error occurred while uploading your resume. Please try again.'); | |
} | |
}); |
// JavaScript to handle star rating functionality | ||
const cards = document.querySelectorAll('.card'); | ||
cards.forEach(card => { | ||
const stars = card.querySelectorAll('.star'); | ||
let numstar = 0; | ||
|
||
// Loop through each star and add event listeners | ||
stars.forEach(star => { | ||
star.addEventListener('click', () => { | ||
// Set the rating based on the data-rating attribute | ||
numstar = parseInt(star.getAttribute('data-rating')); | ||
|
||
// Update star icons based on the selected rating | ||
stars.forEach((s, index) => { | ||
const starIcon = s.querySelector('i'); | ||
if (index < numstar) { | ||
starIcon.classList.remove('fa-regular'); // Remove empty star | ||
starIcon.classList.add('fa-solid'); // Add filled star | ||
} else { | ||
starIcon.classList.remove('fa-solid'); // Remove filled star | ||
starIcon.classList.add('fa-regular'); // Add empty star | ||
} | ||
}); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enhance star rating system with accessibility and validation.
The current implementation lacks accessibility features and proper user feedback.
Here's an improved implementation:
-// JavaScript to handle star rating functionality
const cards = document.querySelectorAll('.card');
cards.forEach(card => {
const stars = card.querySelectorAll('.star');
- let numstar = 0;
+ let currentRating = 0;
+
+ // Add ARIA attributes for accessibility
+ const ratingContainer = card.querySelector('.rating-stars');
+ ratingContainer.setAttribute('role', 'radiogroup');
+ ratingContainer.setAttribute('aria-label', 'Rate this resume');
// Loop through each star and add event listeners
stars.forEach(star => {
+ // Add accessibility attributes
+ star.setAttribute('role', 'radio');
+ star.setAttribute('tabindex', '0');
+
star.addEventListener('click', () => {
- // Set the rating based on the data-rating attribute
- numstar = parseInt(star.getAttribute('data-rating'));
+ updateRating(parseInt(star.getAttribute('data-rating')));
+ });
- // Update star icons based on the selected rating
- stars.forEach((s, index) => {
- const starIcon = s.querySelector('i');
- if (index < numstar) {
- starIcon.classList.remove('fa-regular'); // Remove empty star
- starIcon.classList.add('fa-solid'); // Add filled star
- } else {
- starIcon.classList.remove('fa-solid'); // Remove filled star
- starIcon.classList.add('fa-regular'); // Add empty star
- }
- });
+ // Add keyboard support
+ star.addEventListener('keypress', (e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ updateRating(parseInt(star.getAttribute('data-rating')));
+ }
});
});
+
+ function updateRating(newRating) {
+ if (newRating < 1 || newRating > 5) {
+ console.error('Invalid rating value');
+ return;
+ }
+
+ currentRating = newRating;
+
+ // Update visual state
+ stars.forEach((s, index) => {
+ const starIcon = s.querySelector('i');
+ const isFilled = index < currentRating;
+ starIcon.classList.toggle('fa-regular', !isFilled);
+ starIcon.classList.toggle('fa-solid', isFilled);
+
+ // Update accessibility attributes
+ s.setAttribute('aria-checked', index === currentRating - 1);
+ });
+
+ // Provide user feedback
+ const feedback = card.querySelector('.rating-feedback') ||
+ card.appendChild(document.createElement('div'));
+ feedback.className = 'rating-feedback';
+ feedback.textContent = `You rated this resume ${currentRating} stars`;
+ feedback.setAttribute('role', 'alert');
+ }
});
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// JavaScript to handle star rating functionality | |
const cards = document.querySelectorAll('.card'); | |
cards.forEach(card => { | |
const stars = card.querySelectorAll('.star'); | |
let numstar = 0; | |
// Loop through each star and add event listeners | |
stars.forEach(star => { | |
star.addEventListener('click', () => { | |
// Set the rating based on the data-rating attribute | |
numstar = parseInt(star.getAttribute('data-rating')); | |
// Update star icons based on the selected rating | |
stars.forEach((s, index) => { | |
const starIcon = s.querySelector('i'); | |
if (index < numstar) { | |
starIcon.classList.remove('fa-regular'); // Remove empty star | |
starIcon.classList.add('fa-solid'); // Add filled star | |
} else { | |
starIcon.classList.remove('fa-solid'); // Remove filled star | |
starIcon.classList.add('fa-regular'); // Add empty star | |
} | |
}); | |
}); | |
}); | |
}); | |
const cards = document.querySelectorAll('.card'); | |
cards.forEach(card => { | |
const stars = card.querySelectorAll('.star'); | |
let currentRating = 0; | |
// Add ARIA attributes for accessibility | |
const ratingContainer = card.querySelector('.rating-stars'); | |
ratingContainer.setAttribute('role', 'radiogroup'); | |
ratingContainer.setAttribute('aria-label', 'Rate this resume'); | |
// Loop through each star and add event listeners | |
stars.forEach(star => { | |
// Add accessibility attributes | |
star.setAttribute('role', 'radio'); | |
star.setAttribute('tabindex', '0'); | |
star.addEventListener('click', () => { | |
updateRating(parseInt(star.getAttribute('data-rating'))); | |
}); | |
// Add keyboard support | |
star.addEventListener('keypress', (e) => { | |
if (e.key === 'Enter' || e.key === ' ') { | |
e.preventDefault(); | |
updateRating(parseInt(star.getAttribute('data-rating'))); | |
} | |
}); | |
}); | |
function updateRating(newRating) { | |
if (newRating < 1 || newRating > 5) { | |
console.error('Invalid rating value'); | |
return; | |
} | |
currentRating = newRating; | |
// Update visual state | |
stars.forEach((s, index) => { | |
const starIcon = s.querySelector('i'); | |
const isFilled = index < currentRating; | |
starIcon.classList.toggle('fa-regular', !isFilled); | |
starIcon.classList.toggle('fa-solid', isFilled); | |
// Update accessibility attributes | |
s.setAttribute('aria-checked', index === currentRating - 1); | |
}); | |
// Provide user feedback | |
const feedback = card.querySelector('.rating-feedback') || | |
card.appendChild(document.createElement('div')); | |
feedback.className = 'rating-feedback'; | |
feedback.textContent = `You rated this resume ${currentRating} stars`; | |
feedback.setAttribute('role', 'alert'); | |
} | |
}); |
let skillcoins = 0; | ||
const coinsdata = document.querySelector('#SkillCoins'); | ||
|
||
const updateCoins = () => { | ||
coinsdata.textContent = skillcoins; | ||
}; | ||
updateCoins(); | ||
|
||
const submitButtons = document.querySelectorAll('.submitbtn'); | ||
submitButtons.forEach(button => { | ||
button.addEventListener('click', () => { | ||
if(button.disabled != true){ | ||
skillcoins += 5; | ||
updateCoins(); | ||
button.disabled = true; // Disable the button after it’s clicked | ||
} | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve SkillCoins management with persistence and validation.
The current implementation has several potential issues:
- SkillCoins state is not persisted
- No validation for coin updates
- Potential race conditions in concurrent updates
- Limited user feedback
Here's a more robust implementation:
-let skillcoins = 0;
-const coinsdata = document.querySelector('#SkillCoins');
+class SkillCoinsManager {
+ constructor() {
+ this.coinsDisplay = document.querySelector('#SkillCoins');
+ this.loadCoins();
+ }
+
+ loadCoins() {
+ try {
+ this.coins = parseInt(localStorage.getItem('skillCoins')) || 0;
+ this.updateDisplay();
+ } catch (error) {
+ console.error('Error loading SkillCoins:', error);
+ this.coins = 0;
+ }
+ }
+
+ async addCoins(amount) {
+ if (!Number.isInteger(amount) || amount <= 0) {
+ throw new Error('Invalid coin amount');
+ }
+
+ try {
+ this.coins += amount;
+ await this.saveCoins();
+ this.updateDisplay();
+ return true;
+ } catch (error) {
+ console.error('Error adding coins:', error);
+ return false;
+ }
+ }
+
+ async saveCoins() {
+ localStorage.setItem('skillCoins', this.coins.toString());
+ }
+
+ updateDisplay() {
+ this.coinsDisplay.textContent = this.coins;
+ }
+}
+const skillCoinsManager = new SkillCoinsManager();
const submitButtons = document.querySelectorAll('.submitbtn');
submitButtons.forEach(button => {
- button.addEventListener('click', () => {
+ button.addEventListener('click', async () => {
if(button.disabled != true){
- skillcoins += 5;
- updateCoins();
button.disabled = true; // Disable the button after it's clicked
+ try {
+ const success = await skillCoinsManager.addCoins(5);
+ if (success) {
+ const feedback = document.createElement('div');
+ feedback.textContent = 'You earned 5 SkillCoins!';
+ feedback.className = 'coins-feedback';
+ button.parentNode.appendChild(feedback);
+ setTimeout(() => feedback.remove(), 3000);
+ } else {
+ button.disabled = false; // Re-enable on failure
+ }
+ } catch (error) {
+ console.error('Error processing SkillCoins:', error);
+ button.disabled = false;
+ }
}
});
});
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
let skillcoins = 0; | |
const coinsdata = document.querySelector('#SkillCoins'); | |
const updateCoins = () => { | |
coinsdata.textContent = skillcoins; | |
}; | |
updateCoins(); | |
const submitButtons = document.querySelectorAll('.submitbtn'); | |
submitButtons.forEach(button => { | |
button.addEventListener('click', () => { | |
if(button.disabled != true){ | |
skillcoins += 5; | |
updateCoins(); | |
button.disabled = true; // Disable the button after it’s clicked | |
} | |
}); | |
}); | |
class SkillCoinsManager { | |
constructor() { | |
this.coinsDisplay = document.querySelector('#SkillCoins'); | |
this.loadCoins(); | |
} | |
loadCoins() { | |
try { | |
this.coins = parseInt(localStorage.getItem('skillCoins')) || 0; | |
this.updateDisplay(); | |
} catch (error) { | |
console.error('Error loading SkillCoins:', error); | |
this.coins = 0; | |
} | |
} | |
async addCoins(amount) { | |
if (!Number.isInteger(amount) || amount <= 0) { | |
throw new Error('Invalid coin amount'); | |
} | |
try { | |
this.coins += amount; | |
await this.saveCoins(); | |
this.updateDisplay(); | |
return true; | |
} catch (error) { | |
console.error('Error adding coins:', error); | |
return false; | |
} | |
} | |
async saveCoins() { | |
localStorage.setItem('skillCoins', this.coins.toString()); | |
} | |
updateDisplay() { | |
this.coinsDisplay.textContent = this.coins; | |
} | |
} | |
const skillCoinsManager = new SkillCoinsManager(); | |
const submitButtons = document.querySelectorAll('.submitbtn'); | |
submitButtons.forEach(button => { | |
button.addEventListener('click', async () => { | |
if(button.disabled != true){ | |
button.disabled = true; // Disable the button after it's clicked | |
try { | |
const success = await skillCoinsManager.addCoins(5); | |
if (success) { | |
const feedback = document.createElement('div'); | |
feedback.textContent = 'You earned 5 SkillCoins!'; | |
feedback.className = 'coins-feedback'; | |
button.parentNode.appendChild(feedback); | |
setTimeout(() => feedback.remove(), 3000); | |
} else { | |
button.disabled = false; // Re-enable on failure | |
} | |
} catch (error) { | |
console.error('Error processing SkillCoins:', error); | |
button.disabled = false; | |
} | |
} | |
}); | |
}); |
.hero-content p { | ||
font-size: 1.2em; | ||
color: #555; | ||
margin-bottom: 25px; | ||
line-height: 1.6; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove duplicate style definitions.
The .hero-content p
selector has conflicting style definitions in two places. This can lead to unexpected behavior.
Merge the styles into a single definition:
.hero-content p {
font-size: 1.2em;
color: #555;
margin-bottom: 25px;
line-height: 1.6;
font-weight: 500;
}
Also applies to: 59-63
<div class="panel2"> | ||
<h1>This feature is Currently in Working.</h1> | ||
<p>Till then you can use our Manual feature.</p> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve the work-in-progress message.
The current message contains grammatical errors and lacks clarity about which feature is in development.
Apply these changes:
- <h1>This feature is Currently in Working.</h1>
- <p>Till then you can use our Manual feature.</p>
+ <h1>This feature is currently under development</h1>
+ <p>In the meantime, you can use our manual resume builder.</p>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
<div class="panel2"> | |
<h1>This feature is Currently in Working.</h1> | |
<p>Till then you can use our Manual feature.</p> | |
</div> | |
<div class="panel2"> | |
<h1>This feature is currently under development</h1> | |
<p>In the meantime, you can use our manual resume builder.</p> | |
</div> |
<!-- hero section start --> | ||
<div class="herosection"> | ||
<div class="herocontent"> | ||
<h1>Build Your Perfect Resume in Just 3 Simple Steps!</h1> | ||
<p>Effortlessly create a professional resume that stands out!</p> | ||
</div> | ||
<div class="herostep"> | ||
<div class="one heros"> | ||
<i class="fa-solid fa-keyboard"></i> | ||
<h2>Fill Your Info /<br>Upload LinkedIn Profile</h2> | ||
</div> | ||
<i class="fa-solid fa-chevron-right"></i> | ||
<div class="two heros"> | ||
<i class="fa-solid fa-clipboard-list"></i> | ||
<h2>Choose Your Template</h2> | ||
</div> | ||
<i class="fa-solid fa-chevron-right"></i> | ||
<div class="three heros"> | ||
<i class="fa-solid fa-download"></i> | ||
<h2>Review and Download</h2> | ||
</div> | ||
</div> | ||
<div class="herobtn"> | ||
<div class="button" id="manulbtn">Build Resume Manually</div> | ||
<div class="button" id="autobtn">Build Resume Automatically</div> | ||
</div> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix typo and enhance accessibility in hero section.
The hero section is well-structured, but there are a few improvements needed:
- The button ID "manulbtn" has a typo (should be "manualbtn")
- Interactive elements should be buttons for better accessibility
Apply these changes:
- <div class="button" id="manulbtn">Build Resume Manually</div>
- <div class="button" id="autobtn">Build Resume Automatically</div>
+ <button class="button" id="manualbtn">Build Resume Manually</button>
+ <button class="button" id="autobtn">Build Resume Automatically</button>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
<!-- hero section start --> | |
<div class="herosection"> | |
<div class="herocontent"> | |
<h1>Build Your Perfect Resume in Just 3 Simple Steps!</h1> | |
<p>Effortlessly create a professional resume that stands out!</p> | |
</div> | |
<div class="herostep"> | |
<div class="one heros"> | |
<i class="fa-solid fa-keyboard"></i> | |
<h2>Fill Your Info /<br>Upload LinkedIn Profile</h2> | |
</div> | |
<i class="fa-solid fa-chevron-right"></i> | |
<div class="two heros"> | |
<i class="fa-solid fa-clipboard-list"></i> | |
<h2>Choose Your Template</h2> | |
</div> | |
<i class="fa-solid fa-chevron-right"></i> | |
<div class="three heros"> | |
<i class="fa-solid fa-download"></i> | |
<h2>Review and Download</h2> | |
</div> | |
</div> | |
<div class="herobtn"> | |
<div class="button" id="manulbtn">Build Resume Manually</div> | |
<div class="button" id="autobtn">Build Resume Automatically</div> | |
</div> | |
</div> | |
<!-- hero section start --> | |
<div class="herosection"> | |
<div class="herocontent"> | |
<h1>Build Your Perfect Resume in Just 3 Simple Steps!</h1> | |
<p>Effortlessly create a professional resume that stands out!</p> | |
</div> | |
<div class="herostep"> | |
<div class="one heros"> | |
<i class="fa-solid fa-keyboard"></i> | |
<h2>Fill Your Info /<br>Upload LinkedIn Profile</h2> | |
</div> | |
<i class="fa-solid fa-chevron-right"></i> | |
<div class="two heros"> | |
<i class="fa-solid fa-clipboard-list"></i> | |
<h2>Choose Your Template</h2> | |
</div> | |
<i class="fa-solid fa-chevron-right"></i> | |
<div class="three heros"> | |
<i class="fa-solid fa-download"></i> | |
<h2>Review and Download</h2> | |
</div> | |
</div> | |
<div class="herobtn"> | |
<button class="button" id="manualbtn">Build Resume Manually</button> | |
<button class="button" id="autobtn">Build Resume Automatically</button> | |
</div> | |
</div> |
const panel1 = document.querySelector('.panel1'); | ||
const panel2 = document.querySelector('.panel2'); | ||
const autobtn = document.querySelector('#autobtn'); | ||
const manualbtn = document.querySelector('#manulbtn'); | ||
panel2.style.display = 'none'; | ||
|
||
autobtn.addEventListener('click',()=>{ | ||
panel1.style.display = 'none'; | ||
panel2.style.display = ''; | ||
autobtn.style.backgroundColor = 'rgb(74, 74, 74)'; | ||
manualbtn.style.backgroundColor = 'blue'; | ||
}) | ||
|
||
manualbtn.addEventListener('click',()=>{ | ||
panel1.style.display = ''; | ||
panel2.style.display = 'none'; | ||
autobtn.style.backgroundColor = 'blue'; | ||
manualbtn.style.backgroundColor = 'rgb(74, 74, 74)'; | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider improvements to panel toggle implementation.
The panel toggle functionality could be enhanced in several ways:
- Move color values to CSS variables for better maintainability
- Add ARIA attributes for accessibility
- Consider using a more robust state management approach
Here's a suggested improvement:
const panel1 = document.querySelector('.panel1');
const panel2 = document.querySelector('.panel2');
const autobtn = document.querySelector('#autobtn');
const manualbtn = document.querySelector('#manulbtn');
panel2.style.display = 'none';
+
+// Define button states
+const BUTTON_STATES = {
+ active: {
+ backgroundColor: 'var(--active-btn-color, rgb(74, 74, 74))',
+ ariaPressed: 'true'
+ },
+ inactive: {
+ backgroundColor: 'var(--inactive-btn-color, blue)',
+ ariaPressed: 'false'
+ }
+};
+
+// Initialize ARIA attributes
+autobtn.setAttribute('aria-controls', 'panel2');
+manualbtn.setAttribute('aria-controls', 'panel1');
autobtn.addEventListener('click',()=>{
panel1.style.display = 'none';
panel2.style.display = '';
- autobtn.style.backgroundColor = 'rgb(74, 74, 74)';
- manualbtn.style.backgroundColor = 'blue';
+ Object.assign(autobtn.style, BUTTON_STATES.active);
+ Object.assign(manualbtn.style, BUTTON_STATES.inactive);
})
manualbtn.addEventListener('click',()=>{
panel1.style.display = '';
panel2.style.display = 'none';
- autobtn.style.backgroundColor = 'blue';
- manualbtn.style.backgroundColor = 'rgb(74, 74, 74)';
+ Object.assign(autobtn.style, BUTTON_STATES.inactive);
+ Object.assign(manualbtn.style, BUTTON_STATES.active);
})
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const panel1 = document.querySelector('.panel1'); | |
const panel2 = document.querySelector('.panel2'); | |
const autobtn = document.querySelector('#autobtn'); | |
const manualbtn = document.querySelector('#manulbtn'); | |
panel2.style.display = 'none'; | |
autobtn.addEventListener('click',()=>{ | |
panel1.style.display = 'none'; | |
panel2.style.display = ''; | |
autobtn.style.backgroundColor = 'rgb(74, 74, 74)'; | |
manualbtn.style.backgroundColor = 'blue'; | |
}) | |
manualbtn.addEventListener('click',()=>{ | |
panel1.style.display = ''; | |
panel2.style.display = 'none'; | |
autobtn.style.backgroundColor = 'blue'; | |
manualbtn.style.backgroundColor = 'rgb(74, 74, 74)'; | |
}) | |
const panel1 = document.querySelector('.panel1'); | |
const panel2 = document.querySelector('.panel2'); | |
const autobtn = document.querySelector('#autobtn'); | |
const manualbtn = document.querySelector('#manulbtn'); | |
panel2.style.display = 'none'; | |
// Define button states | |
const BUTTON_STATES = { | |
active: { | |
backgroundColor: 'var(--active-btn-color, rgb(74, 74, 74))', | |
ariaPressed: 'true' | |
}, | |
inactive: { | |
backgroundColor: 'var(--inactive-btn-color, blue)', | |
ariaPressed: 'false' | |
} | |
}; | |
// Initialize ARIA attributes | |
autobtn.setAttribute('aria-controls', 'panel2'); | |
manualbtn.setAttribute('aria-controls', 'panel1'); | |
autobtn.addEventListener('click',()=>{ | |
panel1.style.display = 'none'; | |
panel2.style.display = ''; | |
Object.assign(autobtn.style, BUTTON_STATES.active); | |
Object.assign(manualbtn.style, BUTTON_STATES.inactive); | |
}) | |
manualbtn.addEventListener('click',()=>{ | |
panel1.style.display = ''; | |
panel2.style.display = 'none'; | |
Object.assign(autobtn.style, BUTTON_STATES.inactive); | |
Object.assign(manualbtn.style, BUTTON_STATES.active); | |
}) |
Resume.js
Outdated
@@ -10,7 +10,7 @@ const nextstep1 = document.querySelector('#next-step'); | |||
const nextstep2 = document.querySelector('#next-step-2'); | |||
const prevstep2 = document.querySelector('#prev-step-2'); | |||
const prevstep3 = document.querySelector('#prev-step-3'); | |||
|
|||
let selecttemp = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix inconsistent usage of selecttemp
and selectedTemplate
variables
The variables selecttemp
and selectedTemplate
are both used to track the selected template, but they are used inconsistently throughout the code. This could lead to unexpected behavior or bugs. It's recommended to use a single consistent variable to avoid confusion.
Apply this diff to unify the variable usage to selectedTemplate
:
- let selecttemp = 0;
+ let selectedTemplate = '';
...
// Update the event listeners for template cards:
basicCard.addEventListener('click', () => {
basicCard.style.backgroundColor = '#ADD8E6';
classicCard.style.backgroundColor = '#f4f4f4';
modernCard.style.backgroundColor = '#f4f4f4';
- selecttemp = 1;
+ selectTemplate('basic');
});
classicCard.addEventListener('click', () => {
basicCard.style.backgroundColor = '#f4f4f4';
classicCard.style.backgroundColor = '#ADD8E6';
modernCard.style.backgroundColor = '#f4f4f4';
- selecttemp = 2;
+ selectTemplate('classic');
});
modernCard.addEventListener('click', () => {
basicCard.style.backgroundColor = '#f4f4f4';
classicCard.style.backgroundColor = '#f4f4f4';
modernCard.style.backgroundColor = '#ADD8E6';
- selecttemp = 3;
+ selectTemplate('modern');
});
...
// In `generateResumeHTML`, use the `template` parameter instead of `selecttemp`:
function generateResumeHTML(name, profile, email, contact, location, skills, educationEntries, experienceEntries, projectEntries, template) {
- if (selecttemp === 1){
+ if (template === 'basic'){
- } else if (selecttemp === 2){
+ } else if (template === 'classic'){
- } else if (selecttemp === 3){
+ } else if (template === 'modern'){
...
}
Also applies to: 164-176, 208-208, 298-386
Resume.js
Outdated
<h2>Achievements and Certifications</h2> | ||
<div id="achievements"> | ||
<!-- Achievements data will be filled here --> | ||
</div> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove unused 'Achievements and Certifications' section
The "Achievements and Certifications" section is included in the resume templates but no data is being collected or populated into this section. Since the function collectAchiveData
has been removed, this section should either be updated to collect achievements data or removed to avoid confusion.
Apply this diff to remove the unused section from the templates:
For the classic template:
...
- <h2>Achievements and Certifications</h2>
- <div id="achievements">
- <!-- Achievements data will be filled here -->
- </div>
...
For the modern template:
...
- <h2>Achievements and Certifications</h2>
- <div id="achievements">
- <!-- Achievements data will be filled here -->
- </div>
...
Alternatively, if achievements are intended to be included, consider reintroducing the data collection and display logic for achievements.
Also applies to: 409-413
Description:
This PR implements the SkillCoins currency system and integrates currency rewards for users who rate other users' resumes, addressing Issue #925.
Changes Made:
SkillCoins Counter:
Rewards for Rating:
Premium Feature Integration:
Testing:
Screenshot:
Closes Issue: #925
Summary by CodeRabbit
Release Notes
New Features
Improvements
Bug Fixes
These updates significantly enhance the user interface and functionality of the resume rating and building experience.