diff --git a/apps/student/app/(main)/applications/page.tsx b/apps/student/app/(main)/applications/page.tsx
index a00c785..ea43f27 100644
--- a/apps/student/app/(main)/applications/page.tsx
+++ b/apps/student/app/(main)/applications/page.tsx
@@ -16,54 +16,7 @@ import {
Download,
Share2
} from "lucide-react"
-
-// Mock data for applications - in real app this would come from database
-const mockApplications = [
- {
- id: 1,
- jobTitle: "Software Engineer Intern",
- company: "TechCorp Solutions",
- status: "pending",
- appliedDate: "2024-01-15",
- deadline: "2024-02-15",
- location: "San Francisco, CA",
- salary: "$25/hour",
- resume: "Resume_v2.pdf"
- },
- {
- id: 2,
- jobTitle: "Data Analyst",
- company: "DataFlow Inc",
- status: "reviewed",
- appliedDate: "2024-01-10",
- deadline: "2024-02-10",
- location: "New York, NY",
- salary: "$30/hour",
- resume: "Resume_v2.pdf"
- },
- {
- id: 3,
- jobTitle: "Frontend Developer",
- company: "WebSolutions",
- status: "accepted",
- appliedDate: "2024-01-05",
- deadline: "2024-02-05",
- location: "Remote",
- salary: "$28/hour",
- resume: "Resume_v2.pdf"
- },
- {
- id: 4,
- jobTitle: "Product Manager Intern",
- company: "InnovateTech",
- status: "rejected",
- appliedDate: "2024-01-01",
- deadline: "2024-02-01",
- location: "Seattle, WA",
- salary: "$32/hour",
- resume: "Resume_v2.pdf"
- }
-]
+import { getStudentApplications } from "../actions"
const getStatusConfig = (status: string) => {
switch (status) {
@@ -100,15 +53,73 @@ const getStatusConfig = (status: string) => {
}
}
-export default function ApplicationsPage() {
- const statusConfig = getStatusConfig('pending')
- const StatusIcon = statusConfig.icon
+export default async function ApplicationsPage() {
+ // Get real applications data - using student ID 1 for demo
+ const { success, applications, error } = await getStudentApplications(1);
+
+ // Fallback to mock data if database query fails
+ const mockApplications = [
+ {
+ id: 1,
+ job: {
+ title: "Software Engineer Intern",
+ company: { name: "TechCorp Solutions" },
+ location: "San Francisco, CA",
+ salary: "$25/hour",
+ applicationDeadline: new Date("2024-02-15")
+ },
+ resume: { title: "Resume_v2.pdf" },
+ status: "pending",
+ createdAt: new Date("2024-01-15")
+ },
+ {
+ id: 2,
+ job: {
+ title: "Data Analyst",
+ company: { name: "DataFlow Inc" },
+ location: "New York, NY",
+ salary: "$30/hour",
+ applicationDeadline: new Date("2024-02-10")
+ },
+ resume: { title: "Resume_v2.pdf" },
+ status: "reviewed",
+ createdAt: new Date("2024-01-10")
+ },
+ {
+ id: 3,
+ job: {
+ title: "Frontend Developer",
+ company: { name: "WebSolutions" },
+ location: "Remote",
+ salary: "$28/hour",
+ applicationDeadline: new Date("2024-02-05")
+ },
+ resume: { title: "Resume_v2.pdf" },
+ status: "accepted",
+ createdAt: new Date("2024-01-05")
+ },
+ {
+ id: 4,
+ job: {
+ title: "Product Manager Intern",
+ company: { name: "InnovateTech" },
+ location: "Seattle, WA",
+ salary: "$32/hour",
+ applicationDeadline: new Date("2024-02-01")
+ },
+ resume: { title: "Resume_v2.pdf" },
+ status: "rejected",
+ createdAt: new Date("2024-01-01")
+ }
+ ];
+
+ const allApplications = success && applications ? applications : mockApplications;
// Calculate stats
- const totalApplications = mockApplications.length
- const pendingApplications = mockApplications.filter(app => app.status === 'pending').length
- const acceptedApplications = mockApplications.filter(app => app.status === 'accepted').length
- const rejectedApplications = mockApplications.filter(app => app.status === 'rejected').length
+ const totalApplications = allApplications.length;
+ const pendingApplications = allApplications.filter(app => app.status === 'pending').length;
+ const acceptedApplications = allApplications.filter(app => app.status === 'accepted').length;
+ const rejectedApplications = allApplications.filter(app => app.status === 'rejected').length;
return (
@@ -117,6 +128,12 @@ export default function ApplicationsPage() {
My Applications
Track your job applications and their status
+ {!success && error && (
+
+
+ Using demo data: {error}
+
+ )}
{/* Stats Cards */}
@@ -180,9 +197,9 @@ export default function ApplicationsPage() {
- {mockApplications.map((application) => {
- const appStatusConfig = getStatusConfig(application.status)
- const AppStatusIcon = appStatusConfig.icon
+ {allApplications.map((application) => {
+ const appStatusConfig = getStatusConfig(application.status);
+ const AppStatusIcon = appStatusConfig.icon;
return (
@@ -193,20 +210,20 @@ export default function ApplicationsPage() {
-
{application.jobTitle}
-
{application.company}
+
{application.job.title}
+
{application.job.company.name}
- Applied: {application.appliedDate}
+ Applied: {application.createdAt.toLocaleDateString()}
- {application.location}
+ {application.job.location}
- {application.salary}
+ {application.job.salary}
@@ -228,11 +245,11 @@ export default function ApplicationsPage() {
- Resume: {application.resume}
+ Resume: {application.resume.title}
- Deadline: {application.deadline}
+ Deadline: {application.job.applicationDeadline.toLocaleDateString()}
@@ -253,7 +270,7 @@ export default function ApplicationsPage() {
{/* Empty State */}
- {mockApplications.length === 0 && (
+ {allApplications.length === 0 && (
diff --git a/apps/student/app/(main)/jobs/page.tsx b/apps/student/app/(main)/jobs/page.tsx
index 7b364ab..31436db 100644
--- a/apps/student/app/(main)/jobs/page.tsx
+++ b/apps/student/app/(main)/jobs/page.tsx
@@ -1,3 +1,6 @@
+'use client';
+
+import { useState, useEffect } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from "@workspace/ui/components/card"
import { Button } from "@workspace/ui/components/button"
import { Badge } from "@workspace/ui/components/badge"
@@ -19,33 +22,41 @@ import {
Users,
Star,
CheckCircle,
- ExternalLink
+ ExternalLink,
+ Loader2,
+ AlertCircle
} from "lucide-react"
-import { db, jobs, companies } from "@workspace/db"
-import { eq } from "drizzle-orm"
-import JobApplicationModal from "../../components/job-application-modal"
+import { getAvailableJobs } from "../actions"
+import JobApplicationModal from "../../../components/job-application-modal"
-async function getJobsData() {
- try {
- const availableJobs = await db.query.jobs.findMany({
- where: eq(jobs.active, true),
- with: {
- company: true
- }
- });
-
- return availableJobs;
- } catch (error) {
- console.error("Error fetching jobs:", error);
- return [];
- }
+interface Job {
+ id: number;
+ title: string;
+ company: {
+ name: string;
+ email: string;
+ };
+ location: string;
+ salary: string;
+ description: string;
+ applicationDeadline: Date;
+ minCGPA: number;
+ active: boolean;
+ link?: string;
}
-export default async function JobsPage() {
- const jobs = await getJobsData();
+export default function JobsPage() {
+ const [jobs, setJobs] = useState([]);
+ const [filteredJobs, setFilteredJobs] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [searchTerm, setSearchTerm] = useState('');
+ const [locationFilter, setLocationFilter] = useState('all');
+ const [jobTypeFilter, setJobTypeFilter] = useState('all');
+ const [showLoadMore, setShowLoadMore] = useState(false);
// Mock data for demonstration
- const mockJobs = [
+ const mockJobs: Job[] = [
{
id: 1,
title: "Software Engineer Intern",
@@ -138,7 +149,94 @@ export default async function JobsPage() {
}
];
- const allJobs = [...jobs, ...mockJobs];
+ useEffect(() => {
+ loadJobs();
+ }, []);
+
+ const loadJobs = async () => {
+ try {
+ const result = await getAvailableJobs();
+ if (result.success && result.jobs) {
+ setJobs(result.jobs as any);
+ setFilteredJobs(result.jobs as any);
+ } else {
+ setJobs(mockJobs);
+ setFilteredJobs(mockJobs);
+ setError(result.error || 'Using demo data');
+ }
+ } catch (err) {
+ setJobs(mockJobs);
+ setFilteredJobs(mockJobs);
+ setError('Failed to load jobs, using demo data');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ filterJobs();
+ }, [jobs, searchTerm, locationFilter, jobTypeFilter]);
+
+ const filterJobs = () => {
+ let filtered = [...jobs];
+
+ // Search filter
+ if (searchTerm) {
+ filtered = filtered.filter(job =>
+ job.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ job.company.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ job.description.toLowerCase().includes(searchTerm.toLowerCase())
+ );
+ }
+
+ // Location filter
+ if (locationFilter && locationFilter !== 'all') {
+ filtered = filtered.filter(job =>
+ job.location.toLowerCase().includes(locationFilter.toLowerCase())
+ );
+ }
+
+ // Job type filter (simplified - could be enhanced with job type field)
+ if (jobTypeFilter && jobTypeFilter !== 'all') {
+ filtered = filtered.filter(job =>
+ job.title.toLowerCase().includes(jobTypeFilter.toLowerCase())
+ );
+ }
+
+ setFilteredJobs(filtered);
+ setShowLoadMore(filtered.length > 6);
+ };
+
+ const handleSearch = (value: string) => {
+ setSearchTerm(value);
+ };
+
+ const handleLocationFilter = (value: string) => {
+ setLocationFilter(value);
+ };
+
+ const handleJobTypeFilter = (value: string) => {
+ setJobTypeFilter(value);
+ };
+
+ const clearFilters = () => {
+ setSearchTerm('');
+ setLocationFilter('all');
+ setJobTypeFilter('all');
+ };
+
+ const displayedJobs = filteredJobs.slice(0, showLoadMore ? 6 : filteredJobs.length);
+
+ if (isLoading) {
+ return (
+
+ );
+ }
return (
@@ -147,6 +245,12 @@ export default async function JobsPage() {
Browse Jobs
Find the perfect opportunity that matches your skills and aspirations
+ {error && (
+
+ )}
{/* Search and Filter Section */}
@@ -159,38 +263,53 @@ export default async function JobsPage() {
handleSearch(e.target.value)}
/>
-
+
+ All Locations
Remote
- San Francisco
- New York
+ San Francisco
+ New York
Seattle
Austin
- Los Angeles
+ Los Angeles
-
+
- Internship
- Full Time
- Part Time
- Contract
+ All Types
+ Internship
+ Engineering
+ Analyst
+ Design
+ Management
+ {(searchTerm || (locationFilter && locationFilter !== 'all') || (jobTypeFilter && jobTypeFilter !== 'all')) && (
+
+
+ Clear Filters
+
+
+ {filteredJobs.length} of {jobs.length} jobs
+
+
+ )}
@@ -201,7 +320,7 @@ export default async function JobsPage() {
Total Jobs
-
{allJobs.length}
+
{jobs.length}
@@ -214,7 +333,7 @@ export default async function JobsPage() {
Active Companies
- {new Set(allJobs.map(job => job.company.name)).size}
+ {new Set(jobs.map(job => job.company.name)).size}
@@ -228,7 +347,7 @@ export default async function JobsPage() {
Remote Jobs
- {allJobs.filter(job => job.location.toLowerCase().includes('remote')).length}
+ {jobs.filter(job => job.location.toLowerCase().includes('remote')).length}
@@ -251,7 +370,7 @@ export default async function JobsPage() {
{/* Jobs Grid */}
- {allJobs.map((job) => (
+ {displayedJobs.map((job) => (
@@ -263,8 +382,8 @@ export default async function JobsPage() {
{job.title}
-
{job.company.name}
-
+
{job.company.name}
+
{job.location}
@@ -300,23 +419,23 @@ export default async function JobsPage() {
-
-
-
- {job.link && (
-
-
- View Details
-
- )}
-
+
+
+
+ {job.link && (
+
+
+ View Details
+
+ )}
+
@@ -332,21 +451,23 @@ export default async function JobsPage() {
{/* Load More */}
-
+ {showLoadMore && (
+
+ )}
{/* Empty State */}
- {allJobs.length === 0 && (
+ {filteredJobs.length === 0 && (
No jobs found
Try adjusting your search criteria or check back later for new opportunities
-
+
Clear Filters
diff --git a/apps/student/app/(main)/layout.tsx b/apps/student/app/(main)/layout.tsx
index 6f7445f..ee20c76 100644
--- a/apps/student/app/(main)/layout.tsx
+++ b/apps/student/app/(main)/layout.tsx
@@ -48,6 +48,7 @@ export default function MainLayout({ children }: { children: React.ReactNode })
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [isProfileDropdownOpen, setIsProfileDropdownOpen] = useState(false);
+ const [isSearchOpen, setIsSearchOpen] = useState(false);
// Handle scroll effect
useEffect(() => {
@@ -63,6 +64,16 @@ export default function MainLayout({ children }: { children: React.ReactNode })
setIsMobileMenuOpen(false);
}, [pathname]);
+ const handleSearchClick = () => {
+ // Navigate to jobs page with search focus
+ window.location.href = '/jobs';
+ };
+
+ const handleNotificationClick = () => {
+ // Navigate to applications page to see status updates
+ window.location.href = '/applications';
+ };
+
return (
{/* Modern Student Navbar */}
@@ -127,6 +138,7 @@ export default function MainLayout({ children }: { children: React.ReactNode })
@@ -137,6 +149,7 @@ export default function MainLayout({ children }: { children: React.ReactNode })
@@ -167,14 +180,16 @@ export default function MainLayout({ children }: { children: React.ReactNode })
student@college.edu
-
-
- Settings
-
+
+
+
+ Settings
+
+
-
-
- Settings
-
+
+
+
+ Settings
+
+
acc + company.jobs.filter((job) => job.active).length, 0)
@@ -78,14 +82,18 @@ export default async function DashboardPage() {
Explore opportunities from top companies and find the perfect role that matches your skills and aspirations
-
-
- Browse All Jobs
-
-
-
- Saved Jobs
-
+
+
+
+ Browse All Jobs
+
+
+
+
+
+ My Applications
+
+
@@ -94,6 +102,12 @@ export default async function DashboardPage() {
{/* Stats Section */}
+ {!success && error && (
+
+
+ Using demo data: {error}
+
+ )}
@@ -137,76 +151,93 @@ export default async function DashboardPage() {
-
- {featuredCompanies.map((company) => (
-
-
-
-
-
-
-
-
- Featured
-
-
-
-
-
-
-
-
{company.name}
-
{company.email}
+ {featuredCompanies.length > 0 ? (
+
+ {featuredCompanies.map((company) => (
+
+
+
+
+
+
+
+
+ Featured
+
-
- {company.jobs.length} jobs
-
- {company.description && company.description !== "N/A" && (
- {company.description}
- )}
-
-
- {company.jobs.slice(0, 2).map((job) => (
-
-
-
{job.title}
-
- {job.location && job.location !== "N/A" && (
-
-
- {job.location}
-
- )}
- {job.salary && job.salary !== "N/A" && (
-
-
- {job.salary}
-
- )}
-
-
-
-
-
+
+
+
+
{company.name}
+
{company.email}
- ))}
-
-
-
-
- View All Jobs
-
-
-
-
-
-
-
-
- ))}
-
+
+ {company.jobs.length} jobs
+
+
+
+ {company.description && company.description !== "N/A" && (
+ {company.description}
+ )}
+
+
+ {company.jobs.slice(0, 2).map((job) => (
+
+
+
{job.title}
+
+ {job.location && job.location !== "N/A" && (
+
+
+ {job.location}
+
+ )}
+ {job.salary && job.salary !== "N/A" && (
+
+
+ {job.salary}
+
+ )}
+
+
+
+
+
+
+ ))}
+
+
+
+
+
+ View All Jobs
+
+
+
+
+
+
+
+
+
+ ))}
+
+ ) : (
+
+
+
+ No featured companies yet
+ Check back later for exciting opportunities
+
+
+ Browse All Jobs
+
+
+
+
+ )}
@@ -218,91 +249,112 @@ export default async function DashboardPage() {
Recent Opportunities
Latest job postings from top companies
-
-
- Filter Jobs
-
+
+
+
+ Filter Jobs
+
+
-
- {recentJobs.map((job) => (
-
-
-
-
-
-
+ {recentJobs.length > 0 ? (
+
+ {recentJobs.map((job) => (
+
+
+
+
+
+
+
+
+
{job.title}
+
{job.company.name}
+
-
-
{job.title}
-
{job.company.name}
-
-
-
-
-
- Active
-
-
-
-
-
-
-
-
- {job.location && job.location !== "N/A" && (
-
-
- {job.location}
-
- )}
- {job.salary && job.salary !== "N/A" && (
-
-
- {job.salary}
-
- )}
-
-
- Deadline: {job.applicationDeadline.toLocaleDateString()}
-
-
-
- Min CGPA: {job.minCGPA}
-
-
-
- {job.description && job.description !== "N/A" && (
-
{job.description}
- )}
-
-
-
-
- Apply Now
-
-
- {job.link && (
-
-
- View Details
+
+
+
+ Active
+
+
+
- )}
+
-
-
-
-
-
-
- ))}
-
+
+
+ {job.location && job.location !== "N/A" && (
+
+
+ {job.location}
+
+ )}
+ {job.salary && job.salary !== "N/A" && (
+
+
+ {job.salary}
+
+ )}
+
+
+ Deadline: {job.applicationDeadline.toLocaleDateString()}
+
+
+
+ Min CGPA: {job.minCGPA}
+
+
+
+ {job.description && job.description !== "N/A" && (
+
{job.description}
+ )}
+
+
+
+
+
+ Apply Now
+
+
+
+ {job.link && (
+
+
+ View Details
+
+ )}
+
+
+
+
+
+
+
+ ))}
+
+ ) : (
+
+
+
+ No recent opportunities
+ Check back later for new job postings
+
+
+ Browse All Jobs
+
+
+
+
+ )}
-
- View All Opportunities
-
-
+
+
+ View All Opportunities
+
+
+
@@ -315,13 +367,17 @@ export default async function DashboardPage() {
Join thousands of students who have found their dream jobs through NextPlacement
-
- Create Your Profile
-
-
-
- Learn More
-
+
+
+ Create Your Profile
+
+
+
+
+
+ Browse Jobs
+
+
diff --git a/apps/student/app/(main)/profile/page.tsx b/apps/student/app/(main)/profile/page.tsx
index 5f249fe..10e3cb4 100644
--- a/apps/student/app/(main)/profile/page.tsx
+++ b/apps/student/app/(main)/profile/page.tsx
@@ -1,3 +1,6 @@
+'use client';
+
+import { useState, useEffect } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from "@workspace/ui/components/card"
import { Button } from "@workspace/ui/components/button"
import { Badge } from "@workspace/ui/components/badge"
@@ -23,10 +26,12 @@ import {
Download,
Upload,
CheckCircle,
- AlertCircle
+ AlertCircle,
+ X
} from "lucide-react"
+import { getStudentProfile, updateStudentProfile } from "../actions"
-// Mock student data - in real app this would come from database
+// Mock student data as fallback
const mockStudent = {
id: 1,
firstName: "John",
@@ -49,10 +54,82 @@ const mockStudent = {
isDiploma: false,
verified: true,
markedOut: false,
- profilePicture: null
+ profilePicture: null,
+ resumes: [
+ { id: 1, title: "Resume_v2.pdf", link: "/resumes/resume_v2.pdf" }
+ ]
}
export default function ProfilePage() {
+ const [student, setStudent] = useState(mockStudent);
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState
(null);
+ const [editingSection, setEditingSection] = useState(null);
+ const [editData, setEditData] = useState({});
+ const [isSaving, setIsSaving] = useState(false);
+
+ useEffect(() => {
+ loadStudentProfile();
+ }, []);
+
+ const loadStudentProfile = async () => {
+ try {
+ const result = await getStudentProfile(1); // Using student ID 1 for demo
+ if (result.success && result.student) {
+ setStudent(result.student as any);
+ } else {
+ setError(result.error || 'Failed to load profile');
+ }
+ } catch (err) {
+ setError('Failed to load profile data');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const handleEdit = (section: string) => {
+ setEditingSection(section);
+ setEditData({});
+ };
+
+ const handleCancel = () => {
+ setEditingSection(null);
+ setEditData({});
+ };
+
+ const handleSave = async (section: string) => {
+ setIsSaving(true);
+ try {
+ const result = await updateStudentProfile(student.id, editData);
+ if (result.success) {
+ setStudent(prev => ({ ...prev, ...editData }));
+ setEditingSection(null);
+ setEditData({});
+ } else {
+ setError(result.error || 'Failed to update profile');
+ }
+ } catch (err) {
+ setError('Failed to update profile');
+ } finally {
+ setIsSaving(false);
+ }
+ };
+
+ const handleInputChange = (field: string, value: string) => {
+ setEditData((prev: any) => ({ ...prev, [field]: value }));
+ };
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
return (
@@ -60,6 +137,12 @@ export default function ProfilePage() {
My Profile
Manage your personal information and academic details
+ {error && (
+
+ )}
@@ -70,19 +153,19 @@ export default function ProfilePage() {
- {mockStudent.firstName[0]}{mockStudent.lastName[0]}
+ {student.firstName?.[0] || 'S'}{student.lastName?.[0] || 'T'}
- {mockStudent.firstName} {mockStudent.middleName} {mockStudent.lastName}
+ {student.firstName} {student.middleName} {student.lastName}
-
{mockStudent.email}
+
{student.email}
-
- {mockStudent.verified ? (
+
+ {student.verified ? (
<>
Verified
@@ -94,14 +177,14 @@ export default function ProfilePage() {
>
)}
- {mockStudent.markedOut && (
+ {student.markedOut && (
Marked Out
)}
- Roll Number: {mockStudent.rollNumber}
+ Roll Number: {student.rollNumber}
@@ -111,19 +194,19 @@ export default function ProfilePage() {
Academic Year
- {mockStudent.year}
+ {student.year}
Branch
- {mockStudent.branch}
+ {student.branch}
SSC Score
- {mockStudent.ssc}/10
+ {student.ssc}/10
HSC Score
- {mockStudent.hsc}/10
+ {student.hsc}/10
@@ -157,45 +240,114 @@ export default function ProfilePage() {
Personal Information
-
-
- Edit
-
+ {editingSection !== 'personal' ? (
+
handleEdit('personal')}>
+
+ Edit
+
+ ) : (
+
+ handleSave('personal')} disabled={isSaving}>
+
+ {isSaving ? 'Saving...' : 'Save'}
+
+
+
+ Cancel
+
+
+ )}
@@ -209,37 +361,90 @@ export default function ProfilePage() {
Academic Information
-
-
- Edit
-
+ {editingSection !== 'academic' ? (
+
handleEdit('academic')}>
+
+ Edit
+
+ ) : (
+
+ handleSave('academic')} disabled={isSaving}>
+
+ {isSaving ? 'Saving...' : 'Save'}
+
+
+
+ Cancel
+
+
+ )}
@@ -261,7 +466,7 @@ export default function ProfilePage() {
- {mockStudent.skills.map((skill, index) => (
+ {student.skills?.map((skill, index) => (
{skill}
@@ -280,25 +485,27 @@ export default function ProfilePage() {
-
-
-
-
-
Resume_v2.pdf
-
Updated 2 days ago
+ {student.resumes?.map((resume) => (
+
+
+
+
+
{resume.title}
+
Updated 2 days ago
+
+
+
+
+
+ Download
+
+
+
+ Update
+
-
-
-
- Download
-
-
-
- Update
-
-
-
+ ))}
Keep your resume updated to increase your chances of getting hired.
Make sure it reflects your latest skills and experiences.