Careers Portal
Add a fully-featured careers section to your Larapen site. Post job openings, receive applications with resumes, and manage the entire hiring pipeline from the admin panel.
Job Positions
Create detailed job listings with descriptions, requirements, benefits, salary ranges, and employment types.
Application Pipeline
Receive applications with resumes, cover letters, and profile links. Track candidates through a 7-stage status workflow.
Category Organization
Organize positions by department or category using the unified Larapen category system.
Notification System
Automatic email notifications to admin users when new applications are submitted.
Use Cases
Company Careers Page
You run a company website on Larapen and need a professional careers section for hiring.
- Create job categories by department (Engineering, Marketing, Design, etc.).
- Post open positions with descriptions, requirements, benefits, and salary ranges.
- Set application deadlines or leave positions open until filled.
- Receive and review applications directly from the admin panel.
Agency with Multiple Clients
You manage multiple client sites and need to provide a job board feature on each.
- Install the add-on on each Larapen instance.
- Customize categories and position types per client.
- Enable CAPTCHA to prevent spam applications.
- Admin users receive email notifications for new applications.
Startup with Remote Team
You’re building a remote-first team and want to showcase your open positions.
- Mark positions as “Remote” for clear visibility on the front-end.
- Use experience level filters (Entry, Mid, Senior, Lead, Executive).
- Display salary ranges (optional, toggle per position).
- Applicants submit resumes (PDF/DOC/DOCX) and LinkedIn profiles alongside their applications.
Requirements
- Larapen CMS v1.0.0 or later
- PHP 8.3+
- MySQL 8.0+
Installation
Step 1: Place the Add-on
Copy or symlink the careers folder into your Larapen "extensions/addons" directory:
Step 2: Activate the Add-on
Go to Admin → Add-ons → Installed Add-ons and activate Careers Portal.
Step 3: Run Migrations
This creates 2 tables: careers_positions and careers_applications.
Categories use the core categories table with categorizable_type = 'career'.
Step 4: Set Permissions
The add-on registers 14 permissions (see Permissions). Assign them to admin roles via Admin → Users → Roles & Permissions.
Step 5: Create Categories
Navigate to Admin → Careers → Categories and create your job categories (e.g., Engineering, Marketing, Design, Operations).
Step 6: Post Your First Position
Go to Admin → Careers → Positions → Add Position and create your first job listing.
Configuration
The add-on ships with a config/careers.php file providing sensible defaults.
These can be overridden from the admin settings panel at Admin → Careers → Settings,
which stores values in the settings table (group careers).
| Setting | Description | Default |
|---|---|---|
careers_per_page |
Number of job positions displayed per page on the front-end listing. | 12 |
careers_allowed_resume_types |
Comma-separated list of allowed file extensions for resume uploads. | pdf,doc,docx |
careers_max_resume_size |
Maximum file size for resume uploads in kilobytes. | 5120 (5 MB) |
careers_notify_admin_on_application |
Send email notification to all admin users when a new application is received. | true |
careers_captcha_enabled |
Require CAPTCHA verification on the application submission form. | false |
Config File Defaults
Admin: Positions
The Positions page (Careers → Positions) manages your job listings.
Positions List
A paginated table (15 per page) showing all positions with:
- Title: translatable job title
- Category: linked career category
- Employment type (Full Time, Part Time, Contract, Freelance, Internship)
- Status (Draft / Published)
- Applications count: number of received applications
- Published date and deadline
Filterable by category and status.
Create & Edit
The position form contains translatable fields (per active locale) and non-translatable fields:
Translatable Fields
| Field | Type | Required |
|---|---|---|
title |
Text input | Yes (default locale) |
slug |
Text input (auto-generated from title) | No |
description |
WYSIWYG editor | Yes (default locale) |
requirements |
WYSIWYG editor | No |
benefits |
WYSIWYG editor | No |
meta_title |
Text input | No |
meta_description |
Textarea | No |
Non-Translatable Fields
| Field | Type | Notes |
|---|---|---|
category_id |
Select dropdown | Optional: career category from the unified categories table |
location |
Text input | e.g., “New York, NY” |
employment_type |
Select dropdown | Required: full_time, part_time, contract, freelance, internship |
experience_level |
Select dropdown | Required: entry, mid, senior, lead, executive |
department |
Text input | Free-text department name |
salary_min / salary_max |
Numeric inputs | Optional: salary range (max must be ≥ min) |
salary_currency |
Select dropdown | From active currencies table |
show_salary |
Checkbox | Toggle salary visibility on the front-end |
is_remote |
Checkbox | Mark as remote position |
status |
Select dropdown | Required: draft or published |
published_at |
Date picker | Defaults to current date |
closes_at |
Date picker | Optional: application deadline (must be in the future) |
position |
Number input | Sort order (auto-incremented) |
Admin: Applications
The Applications page (Careers → Applications) lists all received job applications.
Applications List
A paginated table showing:
- Applicant name (first + last name)
- Position applied for
- Status badge (color-coded)
- Applied on date
Filterable by position and status.
Application Detail
Click an application to view its full details:
- Contact information: name, email, phone
- Cover letter: full text
- Resume: download link (stored securely in
localdisk undercareers/resumes/) - LinkedIn profile: clickable URL
- Portfolio URL: clickable URL
- Internal notes: admin-only notes field
- IP address of submission
Status Workflow
Applications progress through a 7-stage pipeline. The admin can update the status and add internal notes at any stage.
| Status | Color | Description |
|---|---|---|
new |
Info (blue) | Freshly submitted, not yet reviewed |
reviewing |
Warning (yellow) | Under initial review |
shortlisted |
Primary (blue) | Candidate has been shortlisted |
interview |
Secondary (gray) | Interview scheduled or in progress |
offered |
Success (green) | Offer extended to the candidate |
hired |
Success (green) | Candidate has been hired |
rejected |
Danger (red) | Application rejected |
Resume Management
Uploaded resumes are stored on the local disk (not publicly accessible) under careers/resumes/.
Admins download resumes through an authenticated controller action that streams the file with the original filename.
- Allowed file types are configurable (default: PDF, DOC, DOCX).
- Maximum file size is configurable (default: 5 MB / 5120 KB).
- Resume files are automatically deleted when an application or its parent position is deleted.
Admin: Categories
Career categories (Careers → Categories) use the core Larapen unified categories table
with categorizable_type = 'career'.
Category Fields
| Field | Type | Notes |
|---|---|---|
name |
Translatable text | Required (default locale) |
slug |
Translatable text | Auto-generated from name |
description |
Translatable textarea | Optional |
is_active |
Checkbox | Toggle category visibility |
position |
Number | Sort order |
Admin: Settings
The settings page (Careers → Settings) is organized into three sections:
Display Options
- Positions Per Page: number of positions on the front-end listing (min: 4, max: 100).
Application Settings
- Allowed Resume Types: comma-separated list of accepted file extensions.
- Max Resume Size: maximum upload size in kilobytes (min: 512 KB, max: 50 MB).
- Admin Notifications: toggle email alerts for new applications.
CAPTCHA Protection
- Enable CAPTCHA for Applications: toggle CAPTCHA on the front-end application form.
- Requires a CAPTCHA provider to be configured in the core settings. A warning is shown if no provider is set up.
Front-end: Careers Listing
The careers listing page displays all open positions (published status, with a future or null deadline).
Page Features
- Category filter: sidebar or tab bar listing all active career categories with an “All Departments” option.
- Position cards: each card shows the job title, employment type, experience level, location, remote badge, salary range (if shown), and posting date.
- Pagination: controlled by the
careers_per_pagesetting. - SEO: page meta title and description are set via the
SeoService.
Category Filtering
Clicking a category navigates to /{locale}/careers/category/{slug}, which filters positions to that category.
The slug is resolved against the current locale with a fallback to the English translation.
Front-end: Position Detail
The position detail page (/{locale}/careers/{slug}) shows:
- Job title as the page heading
- Position metadata: employment type, experience level, location, remote badge, department
- Salary range (if
show_salaryis enabled for this position) - Posting date and deadline (or “Open until filled”)
- About the Role: full description (WYSIWYG content)
- Requirements section (if provided)
- What We Offer / Benefits section (if provided)
- Application form (see below)
- Related Positions: up to 3 other open positions from the same category
Front-end: Application Form
The application form is displayed on the position detail page and submits via POST
to /{locale}/careers/{slug}/apply.
Form Fields
| Field | Type | Validation |
|---|---|---|
first_name |
Text input | Required, max 255 characters |
last_name |
Text input | Required, max 255 characters |
email |
Email input | Required, valid email, max 255 characters |
phone |
Text input | Optional, max 50 characters |
cover_letter |
Textarea | Optional, max 10,000 characters |
resume |
File upload | Required, allowed types from settings, max size from settings |
linkedin_url |
URL input | Optional, valid URL, max 500 characters |
portfolio_url |
URL input | Optional, valid URL, max 500 characters |
On successful submission, the user is redirected back with a success message: “Thank you for your application! We'll review it and get back to you soon.”
Email Notifications
When careers_notify_admin_on_application is enabled, the add-on sends an email notification
to all admin users (is_admin = true) whenever a new application is submitted.
Notification Content
- Subject: “New Application for {Position Title}”
- Greeting: “New Job Application Received”
- Body: “{Applicant Name} has applied for the {Position Title} position.”
- Action button: Links to the admin applications list
- Footer: “Log in to your admin panel to review the application.”
The notification is sent via the mail channel using Laravel’s built-in MailMessage class.
CAPTCHA Integration
The application form supports CAPTCHA verification to prevent spam submissions.
How It Works
- Enable CAPTCHA in Careers → Settings.
- Ensure a CAPTCHA provider (e.g., reCAPTCHA, hCaptcha) is configured in Admin → Settings → General.
- The
SubmitApplicationRequestautomatically adds the CAPTCHA validation rule when enabled. - The CAPTCHA field name is resolved dynamically from the core
CaptchaService.
Multi-Language Support
The add-on ships with complete translations in English and French.
Translatable Content
- Positions: title, slug, description, requirements, benefits, meta_title, meta_description
- Categories: name, slug, description (via the core categories system)
Static UI Strings
All static text uses translation keys prefixed with careers::careers., organized into sections:
employment_types.*: Full Time, Part Time, Contract, Freelance, Internshipexperience_levels.*: Entry Level, Mid Level, Senior, Lead, Executiveapplication_statuses.*: New, Reviewing, Shortlisted, Interview, Offered, Hired, Rejectedadmin.*: all admin panel labels and messagesfront.*: all front-end labels and messagesnotifications.*: email notification strings
Updating
Step 1: Replace Files
Replace the add-on directory with the new version.
Step 2: Run Migrations
Step 3: Clear Caches
Step 4: Verify
Visit Admin → Careers → Positions to confirm the add-on is working correctly.
Troubleshooting
Positions not showing on the front-end
- Ensure the position status is Published (not Draft).
- Check that the Application Deadline (
closes_at) has not passed. Expired positions are excluded from the front-end listing. - Verify the theme has the
pages/careers/index.blade.phpandpages/careers/show.blade.phpview files.
Resume upload fails
- Check the allowed file types in Careers → Settings (default: pdf, doc, docx).
- Verify the file size does not exceed the configured
careers_max_resume_sizelimit. - Ensure your PHP
upload_max_filesizeandpost_max_sizedirectives inphp.iniare large enough. - Check that the
storage/app/careers/resumes/directory is writable.
Admin not receiving email notifications
- Ensure
careers_notify_admin_on_applicationis enabled in settings. - Verify your mail configuration (
MAIL_MAILER,MAIL_HOST, etc.) in.env. - Check that the admin user has
is_admin = truein the database. - Review
storage/logs/laravel.logfor mail delivery errors.
CAPTCHA not appearing on the application form
- Ensure CAPTCHA is enabled in Careers → Settings.
- A CAPTCHA provider must be configured in the core settings (Admin → Settings → General).
- The theme’s application form template must include the CAPTCHA component
(check
@ifblocks in the Blade template).
Category filtering returns 404
- Verify the category slug exists and matches the URL parameter.
- The slug is resolved against the current locale first, then falls back to English. If neither matches, a 404 is returned.
- Ensure the category is marked as active.
Application form not submitting
- Check that the position is still open (published and deadline not passed). Submitting to a closed position returns a 404.
- Ensure the CSRF token is included in the form (
@csrfdirective). - Review validation errors: the resume field is required and must match the allowed types and size.
Translatable fields not saving
- Ensure the form submits translatable fields as nested arrays
(e.g.,
title[en],title[fr]). - The default locale fields are required; other locales are optional.
- Check that the
spatie/laravel-translatablepackage is installed and configured.