Frontend Pages
All page components are in /src/components/pages/.
LandingPage
File: LandingPage.tsx
The public landing page shown to new visitors.
Features
- Hero section with value proposition
- Feature highlights (TikTok-style jobs, AI interviews, services)
- Call-to-action buttons
- Statistics showcase
Props
typescript
interface LandingPageProps {
onGetStarted: () => void;
}Key Sections
- Hero - "Find Your Dream Job" with gradient background
- Features Grid - Swipe to find jobs, AI interviews, Service marketplace
- How It Works - 3-step process
- Stats - Jobs posted, users, placements
- CTA - Get started buttons
JobsPage
File: JobsPage.tsx
The main TikTok-style job browsing experience.
Features
- Full-screen vertical swipe interface
- Job cards with company info, salary, benefits
- Save/apply actions
- Filter by location, job type, experience level
- Tutorial for first-time users
Props
typescript
interface JobsPageProps {
onLoginRequired: () => void;
onNavigateHome: () => void;
}State Management
typescript
const [jobs, setJobs] = useState<Job[]>([]);
const [currentIndex, setCurrentIndex] = useState(0);
const [isLoading, setIsLoading] = useState(true);
const [filters, setFilters] = useState<JobFilters>({
location: '',
jobType: '',
experienceLevel: '',
});API Integration
typescript
useEffect(() => {
const fetchJobs = async () => {
const response = await api.get<Job[]>('/jobs', {
params: { ...filters, page: 1, limit: 20 }
});
setJobs(response.data);
};
fetchJobs();
}, [filters]);ServicesPage
File: ServicesPage.tsx
Informal services marketplace - find or become a service worker.
Features
- Category browser (Plumber, Electrician, Cleaner, etc.)
- Worker cards with ratings, location, price range
- "Become a Worker" CTA
- Request form for clients
- GPS-based nearby workers
Key Components Used
WorkerFeed- TikTok-style worker cardsServiceRequestForm- Post a service needCategoryGrid- Browse service categories
State
typescript
const [activeTab, setActiveTab] = useState<'find' | 'post' | 'become'>('find');
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const [userLocation, setUserLocation] = useState<{ lat: number; lng: number } | null>(null);MessagesPage
File: MessagesPage.tsx
In-app messaging for job inquiries and booking discussions.
Features
- Conversation list with unread counts
- Real-time chat with typing indicators
- Image attachments
- Pin, mute, archive conversations
- System messages for status updates
Props
typescript
interface MessagesPageProps {
onNavigate: (page: string) => void;
}Layout
- Desktop: Split view (conversation list | chat)
- Mobile: Full-screen with back navigation
Components Used
ConversationList- List of chatsChatView- Full chat interfaceMessageBubble- Individual messagesMessageInput- Composer with attachments
ApplicationsPage
File: ApplicationsPage.tsx
Track job applications and interview status.
Features
- List of submitted applications
- Status badges (Pending, Reviewed, Shortlisted, Rejected, Hired)
- AI Interview status and scores
- Filter by status
- Quick actions (withdraw, message employer)
Sections
- Pending Interviews - Applications awaiting AI interview
- Active Applications - All submitted applications
- Interview Results - Completed interviews with scores
Application Card
typescript
const ApplicationCard = ({ application }) => (
<Card>
<div className="flex justify-between">
<div>
<h3>{application.job.title}</h3>
<p>{application.job.company}</p>
</div>
<StatusBadge status={application.status} />
</div>
{application.interviewSessionId && (
<InterviewStatus
status={application.interviewStatus}
score={application.interviewScore}
grade={application.interviewGrade}
/>
)}
<div className="flex gap-2">
<Button onClick={() => startInterview(application.id)}>
{application.interviewStatus === 'pending' ? 'Start Interview' : 'View Results'}
</Button>
<Button variant="ghost" onClick={() => withdraw(application.id)}>
Withdraw
</Button>
</div>
</Card>
);ProfilePage
File: ProfilePage.tsx
User profile management with education and experience.
Features
- Profile photo upload
- Basic info editing
- Education management (add/edit/delete)
- Work experience management
- Privacy settings (interview score visibility)
- YeboScore display
- Logout
Sections
- Header - Photo, name, location, bio
- Profile Completion - Progress bar with missing fields
- Education - List with add/edit actions
- Experience - Work history entries
- Settings - Privacy toggles
- YeboScore - Score breakdown and tier
Form Handling
typescript
const handleUpdateProfile = async (data: ProfileUpdate) => {
try {
const result = await api.put('/users/me', data);
updateUser(result.data);
toast.success('Profile updated');
} catch (error) {
toast.error('Failed to update profile');
}
};
const handleAddEducation = async (education: EducationInput) => {
await api.post('/users/education', education);
refreshProfile();
};PostJobPage
File: PostJobPage.tsx
Employer job posting form.
Features
- Multi-step form
- Rich text description editor
- Requirements and benefits lists
- AI interview configuration
- Preview before posting
- Credit deduction info
Steps
- Basic Info - Title, company, location, salary
- Details - Description, requirements, benefits
- Settings - Job type, experience level, expiry
- AI Interview - Enable/configure interview settings
- Review - Preview and submit
Form State
typescript
const [formData, setFormData] = useState<JobFormData>({
title: '',
company: user?.companyName || '',
location: '',
salary: 0,
currency: 'USD',
description: '',
requirements: [],
benefits: [],
jobType: 'full_time',
experienceLevel: 'entry',
aiInterviewEnabled: false,
interviewQuestions: 10,
interviewTimeLimit: 15,
});CVPage
File: CVPage.tsx
AI-powered CV building through chat.
Features
- Conversational CV creation
- Section-by-section building
- Edit generated sections
- Download as PDF
- Multiple CV management
Chat Interface
typescript
const [messages, setMessages] = useState<ChatMessage[]>([
{
role: 'assistant',
content: "Hi! I'm here to help you build your CV. Let's start with your basic info. What's your full name?"
}
]);
const handleSendMessage = async (content: string) => {
// Add user message
setMessages(prev => [...prev, { role: 'user', content }]);
// Get AI response
const response = await api.post('/cv/chat', {
sessionId,
message: content
});
// Add assistant response
setMessages(prev => [...prev, {
role: 'assistant',
content: response.data.message
}]);
// Update extracted CV data
if (response.data.cvData) {
setCvData(response.data.cvData);
}
};CV Sections
- Personal Info (name, email, phone, location)
- Professional Summary
- Work Experience
- Education
- Skills (technical & soft)
- Languages
- Certifications
Page Navigation Flow
Landing Page
│
├── "Get Started" ──→ Jobs Page (unauthenticated)
│ │
│ ├── Apply ──→ Login Modal ──→ Application Created
│ │
│ └── Save Job ──→ Login Modal ──→ Job Saved
│
├── Login ──→ Jobs Page (authenticated)
│
└── Services ──→ Services Page
│
├── Book Worker ──→ Login Modal ──→ Booking Created
│
└── Become Worker ──→ Login Modal ──→ OnboardingProtected Routes
Protected pages redirect to login if not authenticated:
typescript
const protectedPages = ['post', 'messages', 'applications', 'profile'];
useEffect(() => {
if (!isLoading && !isAuthenticated && protectedPages.includes(currentPage)) {
setLoginModalTab('login');
setShowLoginModal(true);
}
}, [currentPage, isAuthenticated, isLoading]);