Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function Home() {
</div>
<div className="flex items-center gap-2 text-primary opacity-100">
<Sparkles className="w-5 h-5" />
<span className="text-sm font-black uppercase tracking-tighter">Gemini 2.0 Powered</span>
<span className="text-sm font-black uppercase tracking-tighter">Gemini Powered</span>
</div>
</div>
</motion.div>
Expand Down
143 changes: 91 additions & 52 deletions frontend/src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useState, useEffect, useOptimistic, Activity } from 'react';
import { useState, useEffect, useOptimistic } from 'react';
import { useAuth } from '../context/AuthContext';
import {
FileText, Briefcase, Calendar, Download, Loader2,
AlertCircle, Trash2, History, Eye, X,
Plus, Search, TrendingUp, Clock, ChevronRight,
Github
Github, Sparkles
} from 'lucide-react';
import axios from 'axios';
import { motion, AnimatePresence } from 'framer-motion';
Expand Down Expand Up @@ -151,21 +151,31 @@ export default function Dashboard() {
<div className="max-w-7xl mx-auto py-8">
{/* Welcome Section */}
<div className="flex flex-col md:flex-row md:items-end justify-between gap-6 mb-12">
<div>
<h1 className="text-4xl font-black tracking-tight mb-2">
Workspace<span className="text-primary">.</span>
</h1>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
>
<div className="flex items-center gap-3 mb-2">
<h1 className="text-4xl font-black tracking-tight">
Workspace<span className="text-primary">.</span>
</h1>
</div>
<p className="text-muted-foreground text-lg flex items-center gap-2">
Welcome back, <span className="text-foreground font-bold">{user?.username}</span>
Welcome back, <span className="text-foreground font-black underline decoration-primary/30 decoration-4 underline-offset-4">{user?.username}</span>
</p>
</div>
<Link
to="/"
className="inline-flex items-center gap-2 bg-primary hover:bg-primary-dark text-white px-6 py-3 rounded-2xl font-bold shadow-xl shadow-primary/20 transition-smooth"
</motion.div>
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
>
<Plus className="w-5 h-5" />
Create New Resume
</Link>
<Link
to="/"
className="group inline-flex items-center gap-2 bg-primary hover:bg-primary-dark text-white px-8 py-4 rounded-2xl font-black shadow-xl shadow-primary/20 transition-smooth hover:-translate-y-1 active:translate-y-0"
>
<Plus className="w-5 h-5 group-hover:rotate-90 transition-transform duration-300" />
Create New Resume
</Link>
</motion.div>
</div>

{error && (
Expand All @@ -181,49 +191,68 @@ export default function Dashboard() {

{/* Stats Dashboard */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-12">
<div className="glass p-6 rounded-3xl border border-white/5 shadow-sm hover:shadow-md transition-smooth">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 }}
className="glass p-6 rounded-3xl border border-white/5 shadow-sm hover:shadow-xl transition-smooth group"
>
<div className="flex items-center gap-3 mb-4">
<div className="bg-primary/10 p-2.5 rounded-xl">
<div className="bg-primary/10 p-2.5 rounded-xl group-hover:scale-110 transition-transform">
<FileText className="w-5 h-5 text-primary" />
</div>
<span className="text-sm font-bold text-muted-foreground uppercase tracking-wider">Resumes</span>
</div>
<div className="flex items-end justify-between">
<p className="text-4xl font-black">{resumes.length}</p>
<TrendingUp className="w-5 h-5 text-primary mb-1" />
<p className="text-4xl font-black tabular-nums">{resumes.length}</p>
<TrendingUp className="w-5 h-5 text-primary mb-1 animate-pulse" />
</div>
</div>
</motion.div>

<div className="glass p-6 rounded-3xl border border-white/5 shadow-sm hover:shadow-md transition-smooth">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="glass p-6 rounded-3xl border border-white/5 shadow-sm hover:shadow-xl transition-smooth group"
>
<div className="flex items-center gap-3 mb-4">
<div className="bg-success/10 p-2.5 rounded-xl">
<div className="bg-success/10 p-2.5 rounded-xl group-hover:scale-110 transition-transform">
<Briefcase className="w-5 h-5 text-success" />
</div>
<span className="text-sm font-bold text-muted-foreground uppercase tracking-wider">Applications</span>
</div>
<div className="flex items-end justify-between">
<p className="text-4xl font-black">{applications.length}</p>
<div className="bg-success text-white text-[10px] font-black px-2 py-0.5 rounded-full mb-1">LIVE</div>
<p className="text-4xl font-black tabular-nums">{applications.length}</p>
<div className="bg-success text-white text-[10px] font-black px-2 py-0.5 rounded-full mb-1 shadow-sm shadow-success/20">LIVE</div>
</div>
</div>
</motion.div>

<div className="glass p-6 rounded-3xl border border-white/5 shadow-sm hover:shadow-md transition-smooth md:col-span-2 bg-gradient-to-br from-primary/5 to-transparent flex items-center justify-between overflow-hidden relative">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 }}
className="glass p-6 rounded-3xl border border-white/5 shadow-sm hover:shadow-xl transition-smooth md:col-span-2 bg-gradient-to-br from-primary/10 via-background to-transparent flex items-center justify-between overflow-hidden relative group"
>
<div className="relative z-10">
<h3 className="font-bold text-foreground mb-1">Career Insights</h3>
<p className="text-sm text-muted-foreground max-w-[200px]">You've generated {resumes.length} tailored resumes this month. Keep up the momentum!</p>
<h3 className="font-black text-foreground mb-1 flex items-center gap-2">
Career Insights
<Sparkles className="w-4 h-4 text-primary" />
</h3>
<p className="text-sm font-medium text-muted-foreground max-w-[280px]">You've generated <span className="text-primary font-black">{resumes.length} optimized resumes</span>. Your profile matching score is increasing!</p>
</div>
<Clock className="w-24 h-24 text-primary/10 absolute -right-4 -bottom-4 rotate-12" />
</div>
<Clock className="w-24 h-24 text-primary/10 absolute -right-4 -bottom-4 rotate-12 group-hover:scale-125 transition-transform duration-500" />
<div className="absolute top-0 left-0 w-1 h-full bg-primary/20" />
</motion.div>
</div>

{/* Control Bar */}
<div className="flex flex-col sm:flex-row items-center justify-between gap-4 mb-8">
<div className="flex bg-muted p-1.5 rounded-2xl w-full sm:w-auto">
<div className="flex flex-col sm:flex-row items-center justify-between gap-6 mb-12 bg-muted/30 p-4 rounded-[2rem] border border-white/5">
<div className="flex bg-background/50 backdrop-blur-sm p-1.5 rounded-2xl w-full sm:w-auto shadow-inner">
<button
onClick={() => setActiveTab('resumes')}
className={`flex-1 sm:flex-none flex items-center justify-center gap-2 px-6 py-2.5 rounded-xl font-bold text-sm transition-smooth ${
className={`flex-1 sm:flex-none flex items-center justify-center gap-2 px-8 py-3 rounded-xl font-black text-sm transition-smooth ${
activeTab === 'resumes'
? 'bg-card text-foreground shadow-sm px-8'
? 'bg-card text-primary shadow-lg shadow-black/5 ring-1 ring-black/5'
: 'text-muted-foreground hover:text-foreground'
}`}
>
Expand All @@ -232,9 +261,9 @@ export default function Dashboard() {
</button>
<button
onClick={() => setActiveTab('applications')}
className={`flex-1 sm:flex-none flex items-center justify-center gap-2 px-6 py-2.5 rounded-xl font-bold text-sm transition-smooth ${
className={`flex-1 sm:flex-none flex items-center justify-center gap-2 px-8 py-3 rounded-xl font-black text-sm transition-smooth ${
activeTab === 'applications'
? 'bg-card text-foreground shadow-sm px-8'
? 'bg-card text-primary shadow-lg shadow-black/5 ring-1 ring-black/5'
: 'text-muted-foreground hover:text-foreground'
}`}
>
Expand All @@ -243,27 +272,37 @@ export default function Dashboard() {
</button>
</div>

<div className="relative w-full sm:w-72 group">
<Search className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground group-focus-within:text-primary transition-colors" />
<div className="relative w-full sm:w-96 group">
<Search className="absolute left-5 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground group-focus-within:text-primary transition-colors" />
<input
type="text"
placeholder={`Search ${activeTab}...`}
placeholder={`Search ${activeTab === 'resumes' ? 'resumes by job title' : 'applications'}...`}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full pl-11 pr-4 py-2.5 bg-muted border-none rounded-2xl text-sm font-medium focus:ring-2 focus:ring-primary/20 transition-smooth"
className="w-full pl-12 pr-6 py-3.5 bg-card border-none rounded-2xl text-sm font-bold shadow-sm focus:ring-4 focus:ring-primary/10 transition-smooth placeholder:text-muted-foreground/40"
/>
{searchQuery && (
<button
onClick={() => setSearchQuery('')}
className="absolute right-4 top-1/2 -translate-y-1/2 p-1 hover:bg-muted rounded-md transition-colors"
>
<X className="w-3.5 h-3.5 text-muted-foreground" />
</button>
)}
</div>
</div>

{/* Content Area with React 19.2 <Activity> */}
{/* Content Area */}
<div className="relative min-h-[400px]">
{/* Resumes Tab */}
<Activity mode={activeTab === 'resumes' ? 'visible' : 'hidden'}>
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"
>
<AnimatePresence mode="wait">
{activeTab === 'resumes' ? (
<motion.div
key="resumes-tab"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"
>
{filteredResumes.length > 0 ? (
filteredResumes.map(resume => (
<motion.div
Expand Down Expand Up @@ -344,13 +383,12 @@ export default function Dashboard() {
</div>
)}
</motion.div>
</Activity>

{/* Applications Tab */}
<Activity mode={activeTab === 'applications' ? 'visible' : 'hidden'}>
) : (
<motion.div
key="applications-tab"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"
>
{applications.length > 0 ? (
Expand Down Expand Up @@ -398,7 +436,8 @@ export default function Dashboard() {
</div>
)}
</motion.div>
</Activity>
)}
</AnimatePresence>
</div>

{/* Modern Preview Modal */}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/InterviewPrepPlan.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState } from 'react';
import { motion } from 'framer-motion';
import { Trophy, Check, Copy, Lightbulb, CheckCircle2, Brain, MessageSquare, Rocket } from 'lucide-react';
import { Trophy, Check, Copy, Lightbulb, CheckCircle2, Brain, MessageSquare, Briefcase } from 'lucide-react';
import QuestionCard from './QuestionCard';

interface InterviewPrepData {
Expand Down Expand Up @@ -150,7 +150,7 @@ ${behavioralQs.map((q, i) => `${i+1}. ${q?.question}\n Context: ${q?.context}`
<PrepSection
title="Situational Awareness"
subtitle="Scenario Based Challenges"
icon={<Rocket className="w-5 h-5 text-indigo-600" />}
icon={<Briefcase className="w-5 h-5 text-indigo-600" />}
questions={situationalQs}
/>
)}
Expand Down
80 changes: 47 additions & 33 deletions frontend/src/components/MockInterview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,47 +125,61 @@ export default function MockInterview() {

<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{/* Interview Controls */}
<div className="bg-card rounded-[3rem] p-10 border border-muted shadow-xl relative overflow-hidden">
<div className="absolute top-0 right-0 p-8 opacity-5">
<Mic className="w-32 h-32" />
<div className="bg-card rounded-[3rem] p-10 border border-white/10 shadow-2xl relative overflow-hidden group">
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 to-transparent opacity-50" />
<div className="absolute top-0 right-0 p-8 opacity-5 group-hover:opacity-10 transition-opacity">
<Mic className="w-40 h-40 -mr-10 -mt-10 rotate-12" />
</div>

<div className="flex flex-col items-center justify-center space-y-8 relative z-10">
<div className={`w-32 h-32 rounded-full flex items-center justify-center transition-all duration-500 ${
status === 'connected' ? 'bg-primary shadow-2xl shadow-primary/50 scale-110 animate-pulse' : 'bg-muted'
<div className="flex flex-col items-center justify-center space-y-10 relative z-10">
<motion.div
animate={status === 'connected' ? { scale: [1, 1.05, 1] } : {}}
transition={{ duration: 2, repeat: Infinity }}
className={`w-40 h-40 rounded-[2.5rem] flex items-center justify-center transition-all duration-700 shadow-inner ${
status === 'connected'
? 'bg-primary shadow-2xl shadow-primary/30 rotate-3'
: 'bg-muted/50 -rotate-3 hover:rotate-0'
}`}>
{status === 'connected' ? (
<Mic className="w-12 h-12 text-white" />
) : (
<MicOff className="w-12 h-12 text-muted-foreground" />
)}
</div>
<div className={`w-36 h-36 rounded-[2.2rem] border-4 border-white/5 flex items-center justify-center ${
status === 'connected' ? 'bg-primary' : 'bg-muted'
}`}>
{status === 'connected' ? (
<Mic className="w-16 h-16 text-white animate-pulse" />
) : (
<MicOff className="w-16 h-16 text-muted-foreground" />
)}
</div>
</motion.div>

<div className="text-center">
<p className="font-black uppercase tracking-widest text-xs opacity-50 mb-2">Connection Status</p>
<p className={`text-xl font-bold ${status === 'connected' ? 'text-primary' : 'text-muted-foreground'}`}>
{status === 'connected' ? 'Live Interviewing...' : status === 'connecting' ? 'Establishing Line...' : 'Ready to Start'}
<span className="inline-block px-3 py-1 bg-primary/5 text-primary text-[10px] font-black uppercase tracking-[0.2em] rounded-full mb-3 ring-1 ring-primary/20">
AI Voice Interface
</span>
<p className={`text-2xl font-black tracking-tight ${status === 'connected' ? 'text-primary' : 'text-foreground'}`}>
{status === 'connected' ? 'Session Active' : status === 'connecting' ? 'Handshaking...' : 'Standby'}
</p>
</div>

{status !== 'connected' ? (
<button
onClick={startInterview}
disabled={status === 'connecting'}
className="w-full bg-primary text-white py-4 rounded-2xl font-black text-lg hover:shadow-lg hover:shadow-primary/30 transition-all flex items-center justify-center gap-3 disabled:opacity-50"
>
<Mic className="w-5 h-5" />
{status === 'connecting' ? 'Connecting...' : 'Start Voice Interview'}
</button>
) : (
<button
onClick={stopInterview}
className="w-full bg-danger text-white py-4 rounded-2xl font-black text-lg hover:shadow-lg hover:shadow-danger/30 transition-all flex items-center justify-center gap-3"
>
<PhoneOff className="w-5 h-5" />
End Interview Session
</button>
)}
<div className="w-full pt-4">
{status !== 'connected' ? (
<button
onClick={startInterview}
disabled={status === 'connecting'}
className="w-full bg-foreground hover:bg-primary text-white py-5 rounded-[1.5rem] font-black text-lg shadow-xl shadow-primary/10 transition-smooth flex items-center justify-center gap-3 disabled:opacity-50 active:scale-95"
>
<Mic className="w-5 h-5" />
{status === 'connecting' ? 'Establishing Link...' : 'Initialize Voice Agent'}
</button>
) : (
<button
onClick={stopInterview}
className="w-full bg-danger text-white py-5 rounded-[1.5rem] font-black text-lg shadow-xl shadow-danger/20 hover:bg-danger-dark transition-smooth flex items-center justify-center gap-3 active:scale-95"
>
<PhoneOff className="w-5 h-5" />
Terminate Session
</button>
)}
</div>
</div>
</div>

Expand Down
Loading