Skip to content

Commit 8350aef

Browse files
Merge pull request #21 from junaiddshaukat/feat/added-a-carrers-page
Feat: Added a Carrers Page
2 parents ecaf7b2 + 98f4903 commit 8350aef

10 files changed

Lines changed: 1042 additions & 36 deletions

File tree

app/admin/dashboard/jobs/page.tsx

Lines changed: 522 additions & 0 deletions
Large diffs are not rendered by default.

app/admin/dashboard/layout.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import React, { useState } from 'react';
44
import Link from 'next/link';
55
import { usePathname } from 'next/navigation';
6-
import { LogOut, Menu, X, LayoutDashboard, Users, Users2, CalendarDays, Network, PenToolIcon, Globe, BookOpen, Tag } from 'lucide-react';
6+
import { LogOut, Menu, X, LayoutDashboard, Users, Users2, CalendarDays, Network, Globe, BookOpen, Tag, Briefcase } from 'lucide-react';
77
import { Button } from '@/components/ui/button';
88

99
const menuItems = [
@@ -13,6 +13,7 @@ const menuItems = [
1313
{ title: 'Sessions', href: '/admin/dashboard/sessions', icon: <CalendarDays className="w-5 h-5" /> },
1414
{ title: 'Mentorship', href: '/admin/dashboard/mentorship', icon: <Network className="w-5 h-5" /> },
1515
{ title: 'Network', href: '/admin/dashboard/add-mentorship', icon: <Globe className="w-5 h-5" /> },
16+
{ title: 'Jobs', href: '/admin/dashboard/jobs', icon: <Briefcase className="w-5 h-5" /> },
1617
{ title: 'Tags', href: '/admin/dashboard/tags', icon: <Tag className="w-5 h-5" /> },
1718
{ title: 'MindMaster', href: '/admin/dashboard/mindmaster', icon: <BookOpen className="w-5 h-5" /> },
1819
{ title: 'Resources', href: '/admin/dashboard/resources', icon: <BookOpen className="w-5 h-5" /> },

app/api/admin/jobs/route.ts

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import { NextResponse } from 'next/server';
2+
import connectDB from '@/lib/db';
3+
import { Job } from '@/models/Job';
4+
import { cookies } from 'next/headers';
5+
import { verify } from 'jsonwebtoken';
6+
import { ActivityLog } from '@/models/ActivityLog';
7+
8+
const JWT_SECRET = process.env.JWT_SECRET;
9+
10+
async function checkAdmin(): Promise<string | null> {
11+
const cookieStore = await cookies();
12+
const token = cookieStore.get('admin-token')?.value;
13+
if (!token || !JWT_SECRET) return null;
14+
try {
15+
const decoded: any = verify(token, JWT_SECRET);
16+
return decoded.username || 'Unknown';
17+
} catch {
18+
return null;
19+
}
20+
}
21+
22+
export async function GET() {
23+
try {
24+
const adminUsername = await checkAdmin();
25+
if (!adminUsername) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
26+
27+
await connectDB();
28+
const jobs = await Job.find().sort({ createdAt: -1 });
29+
return NextResponse.json(jobs);
30+
} catch (error) {
31+
console.error('GET /api/admin/jobs', error);
32+
return NextResponse.json({ error: 'Failed to fetch jobs' }, { status: 500 });
33+
}
34+
}
35+
36+
export async function POST(request: Request) {
37+
try {
38+
const adminUsername = await checkAdmin();
39+
if (!adminUsername) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
40+
41+
await connectDB();
42+
const data = await request.json();
43+
44+
if (!data.title || !data.company || !data.description) {
45+
return NextResponse.json(
46+
{ error: 'title, company, and description are required' },
47+
{ status: 400 }
48+
);
49+
}
50+
51+
const job = new Job({
52+
title: String(data.title).trim(),
53+
company: String(data.company).trim(),
54+
location: String(data.location || '').trim(),
55+
workplaceType: String(data.workplaceType || 'Remote').trim(),
56+
employmentType: String(data.employmentType || 'Full-time').trim(),
57+
audience: String(data.audience || 'Both').trim(),
58+
description: String(data.description).trim(),
59+
requirements: String(data.requirements || '').trim(),
60+
applyUrl: String(data.applyUrl || '').trim(),
61+
applyEmail: String(data.applyEmail || '').trim(),
62+
deadline: data.deadline ? new Date(data.deadline) : undefined,
63+
tags: Array.isArray(data.tags) ? data.tags.map((t: any) => String(t).trim()).filter(Boolean) : [],
64+
isActive: data.isActive !== false,
65+
lastModifiedBy: adminUsername,
66+
});
67+
68+
await job.save();
69+
70+
await ActivityLog.create({
71+
entityType: 'Job',
72+
entityId: job._id.toString(),
73+
action: 'add',
74+
adminUsername,
75+
details: { title: job.title, company: job.company },
76+
});
77+
78+
return NextResponse.json(job, { status: 201 });
79+
} catch (error: any) {
80+
console.error('POST /api/admin/jobs', error);
81+
return NextResponse.json({ error: error?.message || 'Failed to create job' }, { status: 500 });
82+
}
83+
}
84+
85+
export async function PUT(request: Request) {
86+
try {
87+
const adminUsername = await checkAdmin();
88+
if (!adminUsername) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
89+
90+
await connectDB();
91+
const { searchParams } = new URL(request.url);
92+
const id = searchParams.get('id');
93+
if (!id) return NextResponse.json({ error: 'Job ID is required' }, { status: 400 });
94+
95+
const data = await request.json();
96+
const update: any = {
97+
lastModifiedBy: adminUsername,
98+
};
99+
100+
if (data.title !== undefined) update.title = String(data.title).trim();
101+
if (data.company !== undefined) update.company = String(data.company).trim();
102+
if (data.location !== undefined) update.location = String(data.location || '').trim();
103+
if (data.workplaceType !== undefined) update.workplaceType = String(data.workplaceType || '').trim();
104+
if (data.employmentType !== undefined) update.employmentType = String(data.employmentType || '').trim();
105+
if (data.audience !== undefined) update.audience = String(data.audience || '').trim();
106+
if (data.description !== undefined) update.description = String(data.description || '').trim();
107+
if (data.requirements !== undefined) update.requirements = String(data.requirements || '').trim();
108+
if (data.applyUrl !== undefined) update.applyUrl = String(data.applyUrl || '').trim();
109+
if (data.applyEmail !== undefined) update.applyEmail = String(data.applyEmail || '').trim();
110+
if (data.deadline !== undefined) update.deadline = data.deadline ? new Date(data.deadline) : undefined;
111+
if (data.tags !== undefined) {
112+
update.tags = Array.isArray(data.tags) ? data.tags.map((t: any) => String(t).trim()).filter(Boolean) : [];
113+
}
114+
if (data.isActive !== undefined) update.isActive = !!data.isActive;
115+
116+
const job = await Job.findByIdAndUpdate(id, update, { new: true, runValidators: true });
117+
if (!job) return NextResponse.json({ error: 'Job not found' }, { status: 404 });
118+
119+
await ActivityLog.create({
120+
entityType: 'Job',
121+
entityId: job._id.toString(),
122+
action: 'edit',
123+
adminUsername,
124+
details: { title: job.title, company: job.company },
125+
});
126+
127+
return NextResponse.json(job);
128+
} catch (error: any) {
129+
console.error('PUT /api/admin/jobs', error);
130+
return NextResponse.json({ error: error?.message || 'Failed to update job' }, { status: 500 });
131+
}
132+
}
133+
134+
export async function DELETE(request: Request) {
135+
try {
136+
const adminUsername = await checkAdmin();
137+
if (!adminUsername) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
138+
139+
await connectDB();
140+
const { searchParams } = new URL(request.url);
141+
const id = searchParams.get('id');
142+
if (!id) return NextResponse.json({ error: 'Job ID is required' }, { status: 400 });
143+
144+
const job = await Job.findByIdAndDelete(id);
145+
if (!job) return NextResponse.json({ error: 'Job not found' }, { status: 404 });
146+
147+
await ActivityLog.create({
148+
entityType: 'Job',
149+
entityId: job._id.toString(),
150+
action: 'delete',
151+
adminUsername,
152+
details: { title: job.title, company: job.company },
153+
});
154+
155+
return NextResponse.json({ message: 'Job deleted successfully' });
156+
} catch (error) {
157+
console.error('DELETE /api/admin/jobs', error);
158+
return NextResponse.json({ error: 'Failed to delete job' }, { status: 500 });
159+
}
160+
}
161+
162+

app/api/jobs/route.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { NextResponse } from 'next/server';
2+
import connectDB from '@/lib/db';
3+
import { Job } from '@/models/Job';
4+
5+
// Public endpoint: only returns active jobs
6+
export async function GET() {
7+
try {
8+
await connectDB();
9+
const jobs = await Job.find({ isActive: true }).sort({ createdAt: -1 });
10+
return NextResponse.json(jobs);
11+
} catch (error) {
12+
console.error('GET /api/jobs', error);
13+
return NextResponse.json({ error: 'Failed to fetch jobs' }, { status: 500 });
14+
}
15+
}
16+
17+

0 commit comments

Comments
 (0)