4 Commits

Author SHA1 Message Date
Mohak Jaiswal
3a1deddb8c Fix deadline handling and prevent invalid job application modal access 2026-01-03 19:45:34 +05:30
Mohak Jaiswal
441ae8169b Fix deadline handling and prevent invalid job application modal access 2026-01-03 19:33:00 +05:30
Mohak Jaiswal
3bcb5b851d Fix deadline handling and prevent invalid job application modal access 2026-01-03 19:29:52 +05:30
1648a56680 script update 2025-11-02 15:49:58 +05:30
6 changed files with 52 additions and 23 deletions

View File

@@ -5,8 +5,8 @@
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "dotenv -e ../../.env -- next dev --turbopack -p 9001", "dev": "dotenv -e ../../.env -- next dev --turbopack -p 9001",
"build": "dotenv -e ../../.env -- next build", "build": "dotenv -e ../../.env -- next build && cp -r .next/static .next/standalone/apps/admin/.next/static && cp -r public .next/standalone/apps/admin/public",
"start": "dotenv -e ../../.env -- next start -p 9001", "start": "PORT=9001 dotenv -e ../../.env -- node .next/standalone/apps/admin/server.js",
"lint": "next lint", "lint": "next lint",
"lint:fix": "next lint --fix", "lint:fix": "next lint --fix",
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"

View File

@@ -40,6 +40,9 @@ export default function JobApplicationModal({ job, studentId, resumes, isApplied
const [isPending, startTransition] = useTransition(); const [isPending, startTransition] = useTransition();
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null); const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
const deadline = new Date(job.applicationDeadline);
const isDeadlinePassed = new Date() > deadline;
const handleApply = async () => { const handleApply = async () => {
if (!selectedResume) { if (!selectedResume) {
setMessage({ type: 'error', text: 'Please select a resume' }); setMessage({ type: 'error', text: 'Please select a resume' });
@@ -63,12 +66,14 @@ export default function JobApplicationModal({ job, studentId, resumes, isApplied
setMessage({ type: 'error', text: result.error || 'Failed to submit application' }); setMessage({ type: 'error', text: result.error || 'Failed to submit application' });
} }
} catch (error) { } catch (error) {
setMessage({ type: 'error', text: 'An error occurred while submitting your application' }); setMessage({
type: 'error',
text: error instanceof Error ? error.message : 'An error occurred while submitting your application'
});
} }
}); });
}; };
const isDeadlinePassed = new Date() > new Date(job.applicationDeadline as any);
const cannotApplyReason = isApplied const cannotApplyReason = isApplied
? 'You have already applied to this job' ? 'You have already applied to this job'
: resumes.length === 0 : resumes.length === 0
@@ -78,21 +83,33 @@ export default function JobApplicationModal({ job, studentId, resumes, isApplied
: null; : null;
return ( return (
<Dialog open={isOpen} onOpenChange={setIsOpen}> <Dialog open={isOpen} onOpenChange={(open) => !isPending && setIsOpen(open)}>
{!cannotApplyReason && (
<DialogTrigger asChild> <DialogTrigger asChild>
<div className="flex flex-col items-start"> <div className="flex flex-col items-start">
<Button <Button
size="sm" size="sm"
className="bg-blue-600 hover:bg-blue-700" className="bg-blue-600 hover:bg-blue-700"
disabled={Boolean(cannotApplyReason)} >
Apply Now
</Button>
</div>
</DialogTrigger>
)}
{cannotApplyReason && (
<div className="flex flex-col items-start">
<Button
size="sm"
className="bg-blue-600 hover:bg-blue-700"
disabled
> >
{isApplied ? 'Applied' : 'Apply Now'} {isApplied ? 'Applied' : 'Apply Now'}
</Button> </Button>
{cannotApplyReason && (
<span className="mt-1 text-xs text-red-600">{cannotApplyReason}</span> <span className="mt-1 text-xs text-red-600">{cannotApplyReason}</span>
)}
</div> </div>
</DialogTrigger> )}
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto"> <DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader> <DialogHeader>
<DialogTitle className="flex items-center gap-2"> <DialogTitle className="flex items-center gap-2">
@@ -127,7 +144,7 @@ export default function JobApplicationModal({ job, studentId, resumes, isApplied
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Calendar className="w-4 h-4" /> <Calendar className="w-4 h-4" />
<span>Deadline: {job.applicationDeadline.toLocaleDateString()}</span> <span>Deadline: {deadline.toLocaleDateString()}</span>
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Star className="w-4 h-4" /> <Star className="w-4 h-4" />
@@ -162,6 +179,18 @@ export default function JobApplicationModal({ job, studentId, resumes, isApplied
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
{selectedResume && (
<a
href={resumes.find(r => r.id.toString() === selectedResume)?.fileUrl}
target="_blank"
rel="noopener noreferrer"
className="text-sm text-blue-600 underline mt-2 inline-block"
>
Preview selected resume
</a>
)}
{resumes.length === 0 && ( {resumes.length === 0 && (
<p className="text-sm text-red-600 mt-1"> <p className="text-sm text-red-600 mt-1">
No resumes found. Please upload a resume first. No resumes found. Please upload a resume first.
@@ -171,8 +200,7 @@ export default function JobApplicationModal({ job, studentId, resumes, isApplied
{/* Message Display */} {/* Message Display */}
{message && ( {message && (
<div className={`p-3 rounded-lg ${ <div className={`p-3 rounded-lg ${message.type === 'success'
message.type === 'success'
? 'bg-green-100 text-green-700 border border-green-200' ? 'bg-green-100 text-green-700 border border-green-200'
: 'bg-red-100 text-red-700 border border-red-200' : 'bg-red-100 text-red-700 border border-red-200'
}`}> }`}>

View File

@@ -5,8 +5,8 @@
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "dotenv -e ../../.env -- next dev --turbopack -p 9000", "dev": "dotenv -e ../../.env -- next dev --turbopack -p 9000",
"build": "dotenv -e ../../.env -- next build", "build": "dotenv -e ../../.env -- next build && cp -r .next/static .next/standalone/apps/student/.next/static && cp -r public .next/standalone/apps/student/public",
"start": "dotenv -e ../../.env -- next start -p 9000", "start": "PORT=9000 dotenv -e ../../.env -- node .next/standalone/apps/student/server.js",
"lint": "next lint", "lint": "next lint",
"lint:fix": "next lint --fix", "lint:fix": "next lint --fix",
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"

1
nextplacement Submodule

Submodule nextplacement added at 1648a56680

View File

@@ -34,6 +34,6 @@
] ]
}, },
"dependencies": { "dependencies": {
"nodemailer": "^7.0.7" "nodemailer": "^7.0.6"
} }
} }

10
pnpm-lock.yaml generated
View File

@@ -9,8 +9,8 @@ importers:
.: .:
dependencies: dependencies:
nodemailer: nodemailer:
specifier: ^7.0.7 specifier: ^7.0.6
version: 7.0.7 version: 7.0.6
devDependencies: devDependencies:
'@eslint/js': '@eslint/js':
specifier: ^9.32.0 specifier: ^9.32.0
@@ -3269,8 +3269,8 @@ packages:
resolution: {integrity: sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==} resolution: {integrity: sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
nodemailer@7.0.7: nodemailer@7.0.6:
resolution: {integrity: sha512-jGOaRznodf62TVzdyhKt/f1Q/c3kYynk8629sgJHpRzGZj01ezbgMMWJSAjHADcwTKxco3B68/R+KHJY2T5BaA==} resolution: {integrity: sha512-F44uVzgwo49xboqbFgBGkRaiMgtoBrBEWCVincJPK9+S9Adkzt/wXCLKbf7dxucmxfTI5gHGB+bEmdyzN6QKjw==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
npm-run-path@4.0.1: npm-run-path@4.0.1:
@@ -7271,7 +7271,7 @@ snapshots:
nodemailer@6.10.1: {} nodemailer@6.10.1: {}
nodemailer@7.0.7: {} nodemailer@7.0.6: {}
npm-run-path@4.0.1: npm-run-path@4.0.1:
dependencies: dependencies: