Job Board
Hireline, the job board in the browser where businesses post jobs and players apply.
Hireline (hireline.com) is a job board built into the browser. Businesses post listings for their job, players apply with a profile, message the employer and book interview slots. It is a built-in clearnet site, so it needs an internet connection like any other site and shows up in search results and in LS Pages under Business. It follows the OS's light or dark theme.
Everything is decided on the server. Every request checks that the player has a laptop on screen that is online, who their character is, whether they may post for their job, and the rate limits.
Settings live under Config.Settings.jobs in shared/unlocked/config.lua, see Config.
#Who Can Post
A player can post listings and handle applicants for their current job only. It is read from the framework on every employer action, so a player who is demoted or changes job loses access at once.
| Setting | What it does |
|---|---|
posting.bossRanks | true lets the job's boss grades in the framework post. |
posting.grades | The lowest grade level of a job that may post as well, such as {police = 3, mechanic = 2}. |
posting.excluded | Jobs that never post, {"unemployed"} by default. |
CanPostJobListing(plyId, jobName, ranked) in server/unlocked/hooks.lua has the last word. ranked is whether the player holds a boss grade or the grade from posting.grades; the default returns it as it is. Jobs in posting.excluded never reach the hook. It runs on every employer action, so keep it fast.
lua---@param plyId integer
---@param jobName string
---@param ranked boolean
---@return boolean allowed
function CanPostJobListing(plyId, jobName, ranked)
-- Every police officer may handle recruitment, other jobs keep the grade rule
if (jobName == "police") then return true end
return ranked
end
The company name on listings is the job's label from the framework. Players who can't post see that the employer pages are for hiring managers.
See Can-Check Hooks.
#Looking for a Job
The site's pages are Find jobs, Messages, My jobs and Profile.
- Find jobs searches open listings by what and where, with filters for job type, pay, date posted and company. Each company has a page with its description and open jobs. Listings can be saved for later under My jobs.
- Profile is the player's résumé: headline, summary, availability, skills and past experience. The name comes from the character and can't be changed. Employers get a copy of the profile with each application, so later changes don't reach applications already sent.
- Apply now sends the profile with a cover message, the answers to the employer's screening questions, and optionally the phone number. Without the phone number the employer reaches the player through messages only. An employer can't apply to their own job's listings.
- My jobs lists every application with its status and next steps. The player can withdraw an application while it is still open, and apply again later while the listing is open.
#Application Statuses
| Status | Shown as | Set by |
|---|---|---|
submitted | Submitted | Applying. |
viewed | Viewed | An employer opening the applicants or the application. |
shortlisted | Shortlisted | The employer, from submitted or viewed. |
invited | Interview invited | The employer, from submitted, viewed or shortlisted. Needs an upcoming interview day on the listing. |
booked | Interview booked | The applicant picking a slot. |
hired | Hired | The employer, from any open status. |
declined | Not selected | The employer, from any open status. |
withdrawn | Withdrawn | The applicant, from any open status. |
hired, declined and withdrawn are final. Hiring or declining frees the applicant's interview slot. Each change writes a line into the application's message thread and notifies the applicant when they are online.
#Messages
Every application has its own thread between the applicant and the job's employers, listed under Messages with filters for As applicant and As employer. The applicant can't write any more once the application is withdrawn or declined; the thread stays readable. Players who are offline see their unread counts the next time they open the site.
#Interviews
An invited applicant picks a free slot on one of the listing's interview days. One applicant fits in each slot; when two pick the same one at once, the second is asked to pick another. Once booked, the applicant can reschedule, cancel, or press Set GPS for a waypoint when the employer added the day with their location.
From interviewLockMinutes (30) before the interview, it can no longer be moved or cancelled.
The player-facing text for the lock, jobs:seeker:interview:locked, names 30 minutes. Change it in the locale files too if you change interviewLockMinutes.
#Reminders
reminderMinutes (15) before an interview, the applicant gets a laptop notification and the job's online employers get one too. With phoneReminders = true, the reminder is also texted to the applicant's phone when your phone resource takes texts. A reminder missed while the server was down still goes out up to five minutes after the interview started.
#Hiring for a Job
The employer side has a Dashboard and a Schedule.
#Listings
The dashboard lists the job's listings with their new applicants, total applicants and upcoming interviews, and the company profile, the About text seekers read on the company page.
A listing has a title, location, employment type (full-time, part-time, temporary, contract, internship or volunteer), duration, pay (a range per hour, shift, day, week or month, unpaid, or commission), openings, a description, responsibilities, requirements, schedule, benefits and up to five screening questions (short answer or yes/no, optionally required). The form has a preview of how seekers see it.
| Listing status | Meaning |
|---|---|
| Draft | Saved but never published. A draft can be saved with only a title, but can't open until every field is filled. |
| Open | Visible and taking applications. |
| Paused | Hidden from seekers. Resume opens it again. |
| Closed | Closed by the employer or expired. Reopen opens it again. |
An open listing closes by itself listingDays (14) after it was opened, and the job's online employers are notified. Renew on an open or paused listing restarts the listingDays from now. A job can have at most maxOpenListings (10) listings open at once; publishing past it is refused, so pause or close one first, or save as a draft.
#Applicants
Opening a listing's applicants shows each application with the profile copy, cover message, answers and the phone number when shared. The number is read fresh, so it follows a new phone or SIM; employers can message the number on Natter, call it or copy it. Opening an application marks it as viewed.
From an application, an employer can Shortlist, Invite to interview, Hire or Decline, with an optional message to the applicant. A Private note is shared only with the job's other employers.
#Interview Days
An employer adds interview days to a listing: date, start and end time, slot length and a place name. Use my current location for GPS stores where the employer stands, so booked applicants can set a waypoint to it.
- A day lasts at most 12 hours and starts at most 60 days ahead.
- Slots last between
limits.slotMinutes.minandmax(10 to 60 minutes). - A listing has at most
limits.interviewDays(8) upcoming days. Closed listings can't get new ones. - Removing a day sends its booked applicants back to picking a new time, with a notification.
The Schedule lists every upcoming booked interview of the job.
#Hired Applicants
Hiring does not change the applicant's job. The applicant is told to check their messages, and the server fires zyke_laptop:jobs:ApplicantHired for your own script to set the job.
| Field | Description |
|---|---|
id | Application id. |
listingId | Listing id. |
listingTitle | The listing's title. |
job | The job the listing was posted for. |
identifier | The applicant's character identifier. |
applicantName | The applicant's name. |
hiredBy | The character identifier of the employer who hired them. |
hiredAt | Unix seconds. |
The event is server-side only and doesn't carry a grade. zyke_lib has no function that sets a job, so set it through your framework.
lua-- In your own resource's server script, with "@zyke_lib/imports.lua" in shared_scripts
---@param application JobsHiredApplication
AddEventHandler("zyke_laptop:jobs:ApplicantHired", function(application)
local plyId = Z.getPlayerId(application.identifier)
if (not plyId) then
-- The applicant is offline: keep application.identifier and application.job, and set the job on their next login
return
end
-- Set the character's job to application.job at your lowest grade with your framework's own function, for example
-- ESX: ESX.GetPlayerFromId(plyId).setJob(application.job, 0)
-- QBCore: QBCore.Functions.GetPlayer(plyId).Functions.SetJob(application.job, 0)
end)
The type is JobsHiredApplication in types/jobs.lua. See Exports & Events.
#Limits
| Setting | Default | What it does |
|---|---|---|
listingDays | 14 | Days an open listing stays up before it closes, unless renewed. |
maxOpenListings | 10 | Listings one job may have open at once. |
maxApplicationsPerDay | 15 | Applications one character may send in 24 hours. |
historyDays | 60 | Days closed listings and finished applications are kept, then deleted with their messages. Checked on start and once a day. |
interviewLockMinutes | 30 | Minutes before an interview from which it can't be moved or cancelled. |
reminderMinutes | 15 | Minutes before an interview its reminders go out. |
phoneReminders | true | Also texts the reminder to the applicant's phone. |
limits | Longest texts and lists, such as title = 80, cover = 1500, questions = 5, payMax = 100000 dollars and slotMinutes = {min = 10, max = 60}. | |
rateLimits | read, write, apply and message, each {count, seconds}. |
#Turning It Off
Set Config.Settings.jobs.enabled = false. The site is gone from the browser, search and LS Pages, nothing of it runs on the client or the server, and the zyke_laptop_jobs_* tables aren't created. Tables from before stay in the database until you drop them.
site = {name, host} renames the site or moves it to another address.
#Data
The resource creates these tables on start: zyke_laptop_jobs_listings, zyke_laptop_jobs_companies, zyke_laptop_jobs_profiles, zyke_laptop_jobs_saved, zyke_laptop_jobs_applications, zyke_laptop_jobs_messages, zyke_laptop_jobs_days and zyke_laptop_jobs_bookings. Look for [jobs] lines in the server console for requests that failed.
For a recruitment page of your own, see the police site in Example Sites.