diff --git a/app/admin/dashboard/jobs/page.tsx b/app/admin/dashboard/jobs/page.tsx index afbbe4b..e36d687 100644 --- a/app/admin/dashboard/jobs/page.tsx +++ b/app/admin/dashboard/jobs/page.tsx @@ -157,7 +157,6 @@ export default function JobsAdminPage() { if (!formData.title.trim()) return 'Title is required'; if (!formData.company.trim()) return 'Company is required'; if (!formData.description.trim()) return 'Description is required'; - if (!formData.applyUrl.trim() && !formData.applyEmail.trim()) return 'Provide apply URL or apply email'; return ''; }; @@ -242,7 +241,7 @@ export default function JobsAdminPage() { {editingId ? 'Edit Job' : 'Create Job'} - Fill in the job details. At least one apply method is required. + Fill in the job details. Apply URL/Email are optional.
diff --git a/app/carrers/page.tsx b/app/carrers/page.tsx index f8885b2..cb56747 100644 --- a/app/carrers/page.tsx +++ b/app/carrers/page.tsx @@ -37,8 +37,12 @@ export default function CarrersPage() { const [loading, setLoading] = useState(true); const [search, setSearch] = useState(''); const [audience, setAudience] = useState<'all' | 'Students' | 'Professionals' | 'Both'>('all'); + const [workplace, setWorkplace] = useState<'all' | 'Remote' | 'Hybrid' | 'Onsite'>('all'); + const [employment, setEmployment] = useState<'all' | 'Internship' | 'Full-time' | 'Part-time' | 'Contract'>('all'); const [applyOpen, setApplyOpen] = useState(false); const [selectedJob, setSelectedJob] = useState(null); + const [detailsOpen, setDetailsOpen] = useState(false); + const [detailsJob, setDetailsJob] = useState(null); useEffect(() => { fetch('/api/jobs') @@ -51,6 +55,8 @@ export default function CarrersPage() { const q = search.trim().toLowerCase(); return jobs.filter((j) => { if (audience !== 'all' && (j.audience || 'Both') !== audience) return false; + if (workplace !== 'all' && (j.workplaceType || 'Remote') !== workplace) return false; + if (employment !== 'all' && (j.employmentType || 'Full-time') !== employment) return false; if (!q) return true; return ( j.title?.toLowerCase().includes(q) || @@ -59,13 +65,18 @@ export default function CarrersPage() { (j.tags || []).join(' ').toLowerCase().includes(q) ); }); - }, [jobs, search, audience]); + }, [jobs, search, audience, workplace, employment]); const openApply = (job: JobDoc) => { setSelectedJob(job); setApplyOpen(true); }; + const openDetails = (job: JobDoc) => { + setDetailsJob(job); + setDetailsOpen(true); + }; + const copyToClipboard = async (text: string) => { try { await navigator.clipboard.writeText(text); @@ -79,7 +90,7 @@ export default function CarrersPage() {

Careers

- Hand-picked opportunities for students and professionals from our community. + Hand-picked opportunities for students and professionals across Dev Weekends and Dev Weekends partner companies.

@@ -101,6 +112,29 @@ export default function CarrersPage() { Both + +
+
+ + +
); @@ -170,6 +209,73 @@ export default function CarrersPage() { )}
+ { + setDetailsOpen(open); + if (!open) setDetailsJob(null); + }} + > + + + Job Details + + {detailsJob ? ( + <> + {detailsJob.title} @ {detailsJob.company} + + ) : ( + 'Job details' + )} + + + +
+
+
+ Company:{' '} + {detailsJob?.company || ''} +
+
+ Location:{' '} + {detailsJob?.location || 'Not specified'} +
+
+ Deadline:{' '} + + {detailsJob?.deadline ? new Date(detailsJob.deadline).toLocaleDateString() : 'Not specified'} + +
+
+ +
+
Description
+
{detailsJob?.description || ''}
+
+ +
+
Requirements
+
+ {detailsJob?.requirements?.trim() ? detailsJob.requirements : 'Not provided'} +
+
+ + {Array.isArray(detailsJob?.tags) && detailsJob!.tags!.length > 0 ? ( +
+
Tags
+
+ {detailsJob!.tags!.map((t, idx) => ( + + {t} + + ))} +
+
+ ) : null} +
+
+
+ { diff --git a/models/Job.ts b/models/Job.ts index 533870f..45deb4a 100644 --- a/models/Job.ts +++ b/models/Job.ts @@ -26,19 +26,6 @@ const jobSchema = new Schema( { timestamps: true } ); -// Basic safety: don't allow jobs without any apply method -jobSchema.pre('validate', function (next: (err?: any) => void) { - // @ts-expect-error - mongoose doc typing - const applyUrl = (this.applyUrl || '').toString().trim(); - // @ts-expect-error - mongoose doc typing - const applyEmail = (this.applyEmail || '').toString().trim(); - if (!applyUrl && !applyEmail) { - // @ts-expect-error - mongoose doc typing - this.invalidate('applyUrl', 'Either applyUrl or applyEmail is required'); - } - next(); -}); - export const Job = models.Job || mongoose.model('Job', jobSchema);