-
Notifications
You must be signed in to change notification settings - Fork 0
Create page.tsx #5
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,118 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// app/(chat)/generate/page.tsx | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'use client'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
export default function GenerateJD() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const [formData, setFormData] = useState({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
title: '', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
industry: '', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
experience: '', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
details: '' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const [result, setResult] = useState(''); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const [loading, setLoading] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const handleSubmit = async (e: React.FormEvent) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
e.preventDefault(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setLoading(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const response = await fetch('/api/chat', { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
method: 'POST', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
headers: { 'Content-Type': 'application/json' }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
body: JSON.stringify({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type: 'generate_jd', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
content: formData | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const data = await response.json(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setResult(data.generatedJD); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.error('Error:', error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+31
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Improve error handling for better user experience Currently, errors are only logged to the console. Users won't know if something goes wrong with their request. Add an error state and display a user-friendly error message when an API request fails: const [formData, setFormData] = useState({...});
const [result, setResult] = useState('');
const [loading, setLoading] = useState(false);
+ const [error, setError] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
+ setError('');
try {
const response = await fetch('/api/chat', {
// ...
});
+ if (!response.ok) {
+ throw new Error(`Error: ${response.status}`);
+ }
const data = await response.json();
setResult(data.generatedJD);
} catch (error) {
console.error('Error:', error);
+ setError('Failed to generate job description. Please try again.');
}
setLoading(false);
}; Then add this to the JSX to display the error: {error && (
<div className="mt-4 p-3 bg-red-100 text-red-700 rounded">
{error}
</div>
)} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Improvement: Add error handling UI to show users when something goes wrong. Currently, errors are only logged to the console but not displayed to the user. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setLoading(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="max-w-4xl mx-auto p-6"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<h1 className="text-2xl font-bold mb-6">Generate Job Description</h1> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<form onSubmit={handleSubmit} className="space-y-6"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: Consider adding form validation beyond the required attribute. For example, validate the minimum length of inputs or add more specific validation rules for each field. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<label className="block font-medium mb-1">Job Title</label> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<input | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type="text" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
value={formData.title} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Accessibility: Add |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className="w-full p-2 border rounded" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
placeholder="e.g., Senior Software Engineer" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+44
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix accessibility issue: associate label with input element The form label is not properly associated with its input element, which causes accessibility issues for screen readers and other assistive technologies. - <label className="block font-medium mb-1">Job Title</label>
+ <label htmlFor="job-title" className="block font-medium mb-1">Job Title</label>
<input
+ id="job-title"
type="text"
value={formData.title}
onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}
className="w-full p-2 border rounded"
placeholder="e.g., Senior Software Engineer"
required
/> 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Biome (1.9.4)[error] 44-44: A form label must be associated with an input. Consider adding a (lint/a11y/noLabelWithoutControl) 🪛 GitHub Actions: Lint[error] 44-44: Accessibility lint error (lint/a11y/noLabelWithoutControl): A form label must be associated with an input. Consider adding a |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<label className="block font-medium mb-1">Industry</label> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<select | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
value={formData.industry} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onChange={(e) => setFormData(prev => ({ ...prev, industry: e.target.value }))} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className="w-full p-2 border rounded" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<option value="">Select Industry</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<option value="Technology">Technology</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<option value="Finance">Finance</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<option value="Healthcare">Healthcare</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<option value="Manufacturing">Manufacturing</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<option value="Retail">Retail</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</select> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+56
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix accessibility issue: associate label with select element The industry label is not properly associated with its select element, affecting accessibility. - <label className="block font-medium mb-1">Industry</label>
+ <label htmlFor="industry" className="block font-medium mb-1">Industry</label>
<select
+ id="industry"
value={formData.industry}
onChange={(e) => setFormData(prev => ({ ...prev, industry: e.target.value }))}
className="w-full p-2 border rounded"
required
> 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Biome (1.9.4)[error] 56-56: A form label must be associated with an input. Consider adding a (lint/a11y/noLabelWithoutControl) 🪛 GitHub Actions: Lint[error] 56-56: Accessibility lint error (lint/a11y/noLabelWithoutControl): A form label must be associated with an input. Consider adding a |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<label className="block font-medium mb-1">Experience Level</label> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<select | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
value={formData.experience} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onChange={(e) => setFormData(prev => ({ ...prev, experience: e.target.value }))} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className="w-full p-2 border rounded" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<option value="">Select Experience Level</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<option value="Entry Level">Entry Level (0-2 years)</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<option value="Mid Level">Mid Level (3-5 years)</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<option value="Senior Level">Senior Level (5+ years)</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<option value="Lead">Lead (7+ years)</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</select> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+73
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix accessibility issue: associate label with select element The experience level label is not properly associated with its select element, affecting accessibility. - <label className="block font-medium mb-1">Experience Level</label>
+ <label htmlFor="experience" className="block font-medium mb-1">Experience Level</label>
<select
+ id="experience"
value={formData.experience}
onChange={(e) => setFormData(prev => ({ ...prev, experience: e.target.value }))}
className="w-full p-2 border rounded"
required
> 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Biome (1.9.4)[error] 73-73: A form label must be associated with an input. Consider adding a (lint/a11y/noLabelWithoutControl) 🪛 GitHub Actions: Lint[error] 73-73: Accessibility lint error (lint/a11y/noLabelWithoutControl): A form label must be associated with an input. Consider adding a |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<label className="block font-medium mb-1">Job Details</label> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<textarea | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
value={formData.details} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onChange={(e) => setFormData(prev => ({ ...prev, details: e.target.value }))} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className="w-full h-32 p-2 border rounded" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
placeholder="Describe the role, responsibilities, and key requirements..." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
required | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+89
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix accessibility issue: associate label with textarea element The job details label is not properly associated with its textarea element, affecting accessibility. - <label className="block font-medium mb-1">Job Details</label>
+ <label htmlFor="job-details" className="block font-medium mb-1">Job Details</label>
<textarea
+ id="job-details"
value={formData.details}
onChange={(e) => setFormData(prev => ({ ...prev, details: e.target.value }))}
className="w-full h-32 p-2 border rounded"
placeholder="Describe the role, responsibilities, and key requirements..."
required
/> 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Biome (1.9.4)[error] 89-89: A form label must be associated with an input. Consider adding a (lint/a11y/noLabelWithoutControl) 🪛 GitHub Actions: Lint[error] 89-89: Accessibility lint error (lint/a11y/noLabelWithoutControl): A form label must be associated with an input. Consider adding a |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: The button's disabled state doesn't check if the |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type="submit" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
disabled={loading || !formData.title || !formData.industry || !formData.details} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enhancement: Consider adding a loading indicator or skeleton UI while the job description is being generated to improve user experience. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className="w-full py-2 bg-purple-600 text-white rounded hover:bg-purple-700 disabled:bg-purple-300" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{loading ? 'Generating...' : 'Generate JD'} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</form> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{result && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="mt-6 p-4 bg-gray-50 rounded-lg"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<h2 className="text-lg font-medium mb-2">Generated Job Description:</h2> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="prose max-w-none"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Best Practice: Consider adding a copy button for the generated job description to improve user experience. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div dangerouslySetInnerHTML={{ __html: result }} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainSecurity concern with using dangerouslySetInnerHTML Using Ensure that the API properly sanitizes the HTML before returning it, or consider using a library like DOMPurify on the client-side to sanitize the HTML: - <div dangerouslySetInnerHTML={{ __html: result }} />
+ <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(result) }} /> This would require adding DOMPurify to your project: If it's not installed, you'll need to:
🏁 Script executed: #!/bin/bash
# Command to check if DOMPurify is already installed
npm list dompurify Length of output: 69 🏁 Script executed: #!/bin/bash
# Check if dompurify is listed in package.json dependencies or devDependencies
grep -n '"dompurify"' package.json || echo "No dompurify entry found in package.json"
# Search for any imports or usages of DOMPurify across the repository
rg -n "dompurify" . Length of output: 147 Sanitize HTML before rendering Using Please:
npm install dompurify @types/dompurify
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Warning There is an error in the Gemini Code Assist config file for this repository at There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Security Concern: Using |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</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.
Optimization: The form state could be optimized by using a reducer pattern or a form library like React Hook Form or Formik for more complex forms with validation.