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 ( +
+
+ +

Loading jobs...

+
+
+ ); + } return (
@@ -147,6 +245,12 @@ export default async function JobsPage() {

Browse Jobs

Find the perfect opportunity that matches your skills and aspirations

+ {error && ( +
+ + {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')) && ( +
+ + + {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 && ( - - )} -
+
+
+ + {job.link && ( + + )} +
{/* 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

-
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 })
- + + +
- + + + - + + + + + +
@@ -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}

- ))} -
- -
- - -
-
- - ))} -
+ + {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} + + )} +
+
+ +
+ ))} +
+ +
+ + + + +
+ +
+ ))} +
+ ) : ( + + + +

No featured companies yet

+

Check back later for exciting opportunities

+ + + +
+
+ )}
@@ -218,91 +249,112 @@ export default async function DashboardPage() {

Recent Opportunities

Latest job postings from top companies

- + + +
-
- {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}

- )} - -
-
- - {job.link && ( - - )} +
- -
- - - ))} -
+ +
+ {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}

+ )} + +
+
+ + + + {job.link && ( + + )} +
+ +
+ + + ))} +
+ ) : ( + + + +

No recent opportunities

+

Check back later for new job postings

+ + + +
+
+ )}
- + + +
@@ -315,13 +367,17 @@ export default async function DashboardPage() { Join thousands of students who have found their dream jobs through NextPlacement

- - + + + + + +
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 ( +
+
+
+

Loading profile...

+
+
+ ); + } + return (
@@ -60,6 +137,12 @@ export default function ProfilePage() {

My Profile

Manage your personal information and academic details

+ {error && ( +
+ + {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 - + {editingSection !== 'personal' ? ( + + ) : ( +
+ + +
+ )}
- + {editingSection === 'personal' ? ( + handleInputChange('firstName', e.target.value)} + /> + ) : ( + + )}
- + {editingSection === 'personal' ? ( + handleInputChange('middleName', e.target.value)} + /> + ) : ( + + )}
- + {editingSection === 'personal' ? ( + handleInputChange('lastName', e.target.value)} + /> + ) : ( + + )}
- +
- + {editingSection === 'personal' ? ( + handleInputChange('phoneNumber', e.target.value)} + /> + ) : ( + + )}
- + {editingSection === 'personal' ? ( + handleInputChange('gender', e.target.value)} + /> + ) : ( + + )}
- + {editingSection === 'personal' ? ( + handleInputChange('dob', e.target.value)} + /> + ) : ( + + )}
-