Skip to content

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

  1. Hero - "Find Your Dream Job" with gradient background
  2. Features Grid - Swipe to find jobs, AI interviews, Service marketplace
  3. How It Works - 3-step process
  4. Stats - Jobs posted, users, placements
  5. 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 cards
  • ServiceRequestForm - Post a service need
  • CategoryGrid - 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 chats
  • ChatView - Full chat interface
  • MessageBubble - Individual messages
  • MessageInput - 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

  1. Pending Interviews - Applications awaiting AI interview
  2. Active Applications - All submitted applications
  3. 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

  1. Header - Photo, name, location, bio
  2. Profile Completion - Progress bar with missing fields
  3. Education - List with add/edit actions
  4. Experience - Work history entries
  5. Settings - Privacy toggles
  6. 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

  1. Basic Info - Title, company, location, salary
  2. Details - Description, requirements, benefits
  3. Settings - Job type, experience level, expiry
  4. AI Interview - Enable/configure interview settings
  5. 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

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 ──→ Onboarding

Protected 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]);

One chat. Everything done.