Opplaus
Job platform — web app + browser extension
A job platform for job seekers, recruiters and organisations — search jobs on a live heatmap, track applications on a Kanban board, chat with recruiters in real time, and save jobs from other job boards with a browser extension.
- Frontend Web Developer
- 2025
- Shipped
Overview
Opplaus is a job board where people find, save and apply for jobs, and where recruiters and organisations post jobs and manage candidates. I built the frontend at Paragon Global Services. That meant four portals in one React app (job seeker, organisation, recruiter and super admin), each with its own protected routes. It also meant a browser extension that pulls job listings from LinkedIn, Indeed and Monster into the same dashboard, so a job seeker can track everything in one place. Much of the app updates live: WebSockets drive both the recruiter–candidate chat and the dashboard's metric cards.
Problem
Job seekers apply across many job boards and lose track of where they applied and what stage each application is at. Recruiters and organisations need somewhere to post jobs and talk to candidates. Opplaus has to serve both sides, and a job seeker's data has to come in from sites Opplaus does not control.
Approach
Build one frontend with a separate portal for each role, and bring outside jobs in through a browser extension rather than asking people to copy them over. Opplaus jobs and saved external jobs appear together on the same dashboard, map and Kanban board, so there is one place to track a search.
Architecture
React and TypeScript on Vite (migrated from Create React App), talking to a Spring Boot REST API. Shared client state lives in Zustand stores. Authentication decides which portal (job seeker, organisation, recruiter or super admin) a user reaches, and route guards protect each one. A WebSocket connection over SockJS carries chat messages and live metric updates. The extension is a separate React + Vite build: it scrapes the job on the current job-board page, shows a floating popup injected into the page, and saves the job to the dashboard through the same API.
Key interactions
- Leaflet map with a job-count heatmap, job location markers and interactive zoom, driven by search and filters
- Kanban board for moving applications through their statuses
- Browser extension for LinkedIn, Indeed and Monster: scrapes the listing, injects a floating React popup, saves the job to the dashboard
- Real-time chat between recruiters and job seekers over WebSockets
- Metric cards that update live over WebSockets, with drag-and-drop layout and per-user visibility preferences
- Protected routes across job seeker, organisation, recruiter and super admin portals
- Migrated the build from Create React App to Vite
Technical decisions
Save jobs from the page they're on
A tracker only helps if it holds every application, and most jobs are posted somewhere else. Scraping the listing and saving it from a popup on the job-board page itself means an external job goes into the dashboard in one click instead of being copied over by hand.
Search and filters drive the map
The heatmap, the markers and the job list all come from the same search and filters. Narrowing a search changes where the heat is, so the map works as part of the search rather than as a separate picture of the results.
Live data where it changes
Chat messages and dashboard metrics arrive over WebSockets rather than on refresh. A recruiter's reply and a new application show up as they happen, and the dashboard stays current without the user reloading it.
One app, four portals
Job seekers, organisations, recruiters and super admins share one codebase, and authentication decides which portal each user sees. Shared components and API code are written once, while each role only gets routes it is allowed to reach.
What I took from it
- A browser extension is a second frontend running inside pages you don't control. Treat it as its own product, with its own build.
- Letting users choose which metric cards they see made the dashboard more useful than adding more cards would have.