diff --git a/apps/admin/package.json b/apps/admin/package.json index 0b5283e..aeafbdd 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -16,6 +16,7 @@ "@heroicons/react": "^2.2.0", "@hookform/resolvers": "^5.1.1", "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-switch": "^1.2.5", "@tailwindcss/postcss": "^4.0.8", "@tanstack/react-table": "^8.21.3", "@workspace/db": "workspace:*", diff --git a/apps/student/README.md b/apps/student/README.md new file mode 100644 index 0000000..2f62a60 --- /dev/null +++ b/apps/student/README.md @@ -0,0 +1,172 @@ +# Student Dashboard - NextPlacement + +A modern, student-focused dashboard for job discovery and application management. + +## 🎨 Design Philosophy + +The student dashboard has been completely redesigned with a different UI/UX approach compared to the admin dashboard: + +- **Student-Centric**: Focused on job discovery and career opportunities +- **Modern & Engaging**: Gradient backgrounds, interactive cards, and smooth animations +- **Mobile-First**: Responsive design that works perfectly on all devices +- **Intuitive Navigation**: Clear, student-focused navigation structure + +## 🚀 Features + +### 1. **Hero Section** +- Inspirational messaging focused on career discovery +- Call-to-action buttons for job browsing and saved jobs +- Gradient background with engaging visuals + +### 2. **Statistics Dashboard** +- Real-time stats showing active companies, open positions, student count +- Visual indicators with icons and color-coded metrics +- Success rate tracking + +### 3. **Featured Companies** +- Showcase of top companies with active job postings +- Company cards with job previews +- Quick access to company-specific job listings + +### 4. **Recent Job Opportunities** +- Latest job postings from all companies +- Detailed job cards with company information +- Quick apply functionality with resume selection + +### 5. **Job Applications Tracking** +- Comprehensive view of all submitted applications +- Status tracking (Pending, Under Review, Accepted, Rejected) +- Application history with company and job details + +### 6. **Student Profile Management** +- Complete profile information management +- Academic details and skills showcase +- Resume upload and management +- Social media links integration + +## 🛠Technical Implementation + +### Backend Integration +- **Database Schema**: Uses the existing schema with `students`, `jobs`, `companies`, `applications`, and `resumes` tables +- **Server Actions**: New actions for job applications, profile management, and data fetching +- **Real-time Data**: Dynamic data fetching with proper error handling + +### Key Components + +#### 1. **Layout (`layout.tsx`)** +- Student-specific navigation with blue color scheme +- Responsive design with mobile menu +- Profile dropdown with student information + +#### 2. **Dashboard (`page.tsx`)** +- Hero section with career-focused messaging +- Statistics cards with real data +- Featured companies showcase +- Recent job opportunities grid + +#### 3. **Applications (`applications/page.tsx`)** +- Application status tracking +- Detailed application history +- Export functionality +- Status-based filtering + +#### 4. **Profile (`profile/page.tsx`)** +- Comprehensive profile management +- Academic information display +- Skills showcase +- Resume management + +#### 5. **Jobs (`jobs/page.tsx`)** +- Job browsing with search and filters +- Detailed job cards +- Application modal integration +- Company information display + +#### 6. **Job Application Modal (`components/job-application-modal.tsx`)** +- Interactive application form +- Resume selection +- Real-time validation +- Success/error messaging + +## 🎯 Key Differences from Admin Dashboard + +| Aspect | Admin Dashboard | Student Dashboard | +|--------|----------------|-------------------| +| **Color Scheme** | Red/Pink gradients | Blue/Indigo gradients | +| **Navigation** | Admin-focused (Dashboard, Students, Jobs) | Student-focused (Home, Applications, Profile) | +| **Layout** | Data-heavy, management-focused | Career-focused, opportunity-driven | +| **Interactions** | CRUD operations, data management | Job discovery, applications, profile management | +| **Visual Style** | Professional, corporate | Modern, engaging, student-friendly | + +## 🔧 Backend Actions + +### New Server Actions Added: + +1. **`applyForJob(jobId, studentId, resumeId)`** + - Handles job application submission + - Prevents duplicate applications + - Updates application status + +2. **`getStudentApplications(studentId)`** + - Fetches all applications for a student + - Includes job and company details + - Status tracking + +3. **`getStudentProfile(studentId)`** + - Retrieves complete student profile + - Includes grades, resumes, internships + +4. **`updateStudentProfile(studentId, data)`** + - Updates student profile information + - Handles validation and error cases + +5. **`getAvailableJobs()`** + - Fetches all active job postings + - Includes company information + +6. **`getFeaturedCompanies()`** + - Gets top companies with active jobs + - Sorted by job count + +## 🎨 UI Components Used + +- **Cards**: For job listings, company showcases, and profile sections +- **Buttons**: Various styles for different actions +- **Badges**: Status indicators and skill tags +- **Dialogs**: Application modals and confirmations +- **Forms**: Profile editing and application submission +- **Icons**: Lucide React icons for visual consistency + +## 🚀 Getting Started + +1. **Installation**: The dashboard is part of the monorepo structure +2. **Database**: Ensure the database schema is up to date +3. **Authentication**: Student authentication should be configured +4. **Development**: Run the student app with `pnpm dev` in the student directory + +## 📱 Responsive Design + +The dashboard is fully responsive with: +- Mobile-first approach +- Tablet-optimized layouts +- Desktop-enhanced features +- Touch-friendly interactions + +## 🔮 Future Enhancements + +- **Real-time Notifications**: WebSocket integration for application updates +- **Advanced Filtering**: More sophisticated job search and filtering +- **Resume Builder**: Integrated resume creation tool +- **Interview Scheduling**: Calendar integration for interviews +- **Analytics Dashboard**: Personal application analytics +- **Social Features**: Student networking and recommendations + +## 🎯 User Experience Goals + +1. **Easy Job Discovery**: Students can quickly find relevant opportunities +2. **Simple Application Process**: Streamlined job application workflow +3. **Clear Status Tracking**: Transparent application status updates +4. **Profile Management**: Easy profile updates and maintenance +5. **Mobile Accessibility**: Full functionality on mobile devices + +This student dashboard provides a modern, engaging experience that helps students discover career opportunities and manage their job applications effectively. \ No newline at end of file diff --git a/apps/student/app/(main)/actions.ts b/apps/student/app/(main)/actions.ts index 9ef1e5e..9cd2194 100644 --- a/apps/student/app/(main)/actions.ts +++ b/apps/student/app/(main)/actions.ts @@ -1,6 +1,134 @@ 'use server' import { signOut } from "@/auth"; +import { db, applications, jobs, students, resumes } from "@workspace/db"; +import { eq, and } from "@workspace/db/drizzle"; +import { revalidatePath } from "next/cache"; export async function signOutAction() { await signOut(); +} + +export async function applyForJob(jobId: number, studentId: number, resumeId: number) { + try { + // Check if student has already applied for this job + const existingApplication = await db.query.applications.findFirst({ + where: and( + eq(applications.jobId, jobId), + eq(applications.studentId, studentId) + ) + }); + + if (existingApplication) { + return { success: false, error: "You have already applied for this job" }; + } + + // Create new application + await db.insert(applications).values({ + jobId, + studentId, + resumeId, + status: 'pending' + }); + + revalidatePath('/applications'); + return { success: true }; + } catch (error) { + console.error("Error applying for job:", error); + return { success: false, error: "Failed to apply for job" }; + } +} + +export async function getStudentApplications(studentId: number) { + try { + const studentApplications = await db.query.applications.findMany({ + where: eq(applications.studentId, studentId), + with: { + job: { + with: { + company: true + } + }, + resume: true + } + }); + + return { success: true, applications: studentApplications }; + } catch (error) { + console.error("Error fetching student applications:", error); + return { success: false, error: "Failed to fetch applications" }; + } +} + +export async function getStudentProfile(studentId: number) { + try { + const student = await db.query.students.findFirst({ + where: eq(students.id, studentId), + with: { + grades: true, + resumes: true, + internships: true + } + }); + + return { success: true, student }; + } catch (error) { + console.error("Error fetching student profile:", error); + return { success: false, error: "Failed to fetch student profile" }; + } +} + +export async function updateStudentProfile(studentId: number, data: any) { + try { + await db.update(students) + .set({ + ...data, + updatedAt: new Date() + }) + .where(eq(students.id, studentId)); + + revalidatePath('/profile'); + return { success: true }; + } catch (error) { + console.error("Error updating student profile:", error); + return { success: false, error: "Failed to update profile" }; + } +} + +export async function getAvailableJobs() { + try { + const availableJobs = await db.query.jobs.findMany({ + where: eq(jobs.active, true), + with: { + company: true + } + }); + + return { success: true, jobs: availableJobs }; + } catch (error) { + console.error("Error fetching available jobs:", error); + return { success: false, error: "Failed to fetch jobs" }; + } +} + +export async function getFeaturedCompanies() { + try { + const companies = await db.query.companies.findMany({ + with: { + jobs: { + where: eq(jobs.active, true) + } + } + }); + + // Filter companies with active jobs and sort by number of jobs + const companiesWithJobs = companies + .filter(company => company.jobs.length > 0) + .sort((a, b) => b.jobs.length - a.jobs.length) + .slice(0, 6); // Top 6 companies + + return { success: true, companies: companiesWithJobs }; + } catch (error) { + console.error("Error fetching featured companies:", error); + return { success: false, error: "Failed to fetch companies" }; + } } \ No newline at end of file diff --git a/apps/student/app/(main)/applications/page.tsx b/apps/student/app/(main)/applications/page.tsx new file mode 100644 index 0000000..a00c785 --- /dev/null +++ b/apps/student/app/(main)/applications/page.tsx @@ -0,0 +1,271 @@ +import { Card, CardContent, CardHeader, CardTitle } from "@workspace/ui/components/card" +import { Button } from "@workspace/ui/components/button" +import { Badge } from "@workspace/ui/components/badge" +import { Separator } from "@workspace/ui/components/separator" +import { + FileText, + Clock, + CheckCircle, + XCircle, + AlertCircle, + Building2, + Calendar, + MapPin, + DollarSign, + Eye, + 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" + } +] + +const getStatusConfig = (status: string) => { + switch (status) { + case 'pending': + return { + icon: Clock, + color: 'bg-yellow-100 text-yellow-700', + text: 'Pending Review' + } + case 'reviewed': + return { + icon: Eye, + color: 'bg-blue-100 text-blue-700', + text: 'Under Review' + } + case 'accepted': + return { + icon: CheckCircle, + color: 'bg-green-100 text-green-700', + text: 'Accepted' + } + case 'rejected': + return { + icon: XCircle, + color: 'bg-red-100 text-red-700', + text: 'Rejected' + } + default: + return { + icon: AlertCircle, + color: 'bg-gray-100 text-gray-700', + text: 'Unknown' + } + } +} + +export default function ApplicationsPage() { + const statusConfig = getStatusConfig('pending') + const StatusIcon = statusConfig.icon + + // 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 + + return ( +
Track your job applications and their status
+Total Applications
+{totalApplications}
+Pending Review
+{pendingApplications}
+Accepted
+{acceptedApplications}
+Rejected
+{rejectedApplications}
+{application.company}
+Start applying to jobs to see your applications here
+ +Find the perfect opportunity that matches your skills and aspirations
+Total Jobs
+{allJobs.length}
+Active Companies
++ {new Set(allJobs.map(job => job.company.name)).size} +
+Remote Jobs
++ {allJobs.filter(job => job.location.toLowerCase().includes('remote')).length} +
+Avg Salary
+$28/hr
+{job.company.name}
+{job.description}
+ +Try adjusting your search criteria or check back later for new opportunities
+ +