> For the complete documentation index, see [llms.txt](https://tyrix.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tyrix.gitbook.io/docs/script-resources/tyrix-multi-job/jobs-menu.md).

# Jobs Menu

The **Jobs Menu** is the whole player-facing half of the resource. It lists every job a character currently holds, shows their rank in each one, and lets them clock between them. One job is active at a time; the menu is how a player decides which.

Players open it with the command or the keybind configured in `Config.openJobMenu`:

```lua
Config.openJobMenu = {
    enabled = true,     -- Enable/disable the job menu entirely
    command = 'jobs',   -- Command to open menu (e.g., /jobs)
    key = 'F5',          -- Keybind to open menu (set to false to disable)
}
```

Out of the box that is `/jobs` or **F5**. The keybind is registered through FiveM's key mapping system, so players can rebind it themselves under Settings → Key Bindings → FiveM without you changing anything. Setting `key = false` registers the command only. Setting `enabled = false` removes both, which leaves players with no way to open the menu at all. See [Commands](/docs/script-resources/tyrix-multi-job/commands.md) for the full list.

Escape closes the menu, and steps back one screen at a time if the player has drilled into a job or opened a prompt.

## How players get jobs

There is no "add job" button, and that is the single most important thing to understand about this resource. Jobs are saved **automatically**.

Whenever your framework assigns a character a job — a boss hiring them from the [Boss Menu](/docs/script-resources/tyrix-multi-job/boss-menu.md), a job-centre script, another resource's `SetJob` call, an admin command — the resource sees the job change and writes that job and grade into the player's list. Nothing in your other scripts needs to know tyrix\_multijob exists. The player gets a "You've been Hired!" dialog telling them a job has been added to their menu, and it is there from then on.

Three things are skipped and never occupy a slot in a player's list:

| Skipped                        | Why                                                                         |
| ------------------------------ | --------------------------------------------------------------------------- |
| Anything in `Config.blacklist` | Jobs that are states, not employment — the shipped example is `prisoner`    |
| `Config.offDutyJob.name`       | The off-duty job is the absence of a job, not one to save                   |
| Jobs already in the list       | Re-hiring into a held job updates its stored rank instead of duplicating it |

That third case matters for promotions. If the framework changes a player's grade in a job they already hold, the stored rank is updated in place rather than a second entry being created, so the menu always reflects their real current rank in each job.

Saving is verified server-side. The server re-reads the player's actual framework job and refuses to save anything that does not match it exactly, in both job name and grade, and it re-checks the job cap itself. A modified client cannot inject a job the character was never given.

### When a player is already at their cap

If a player at their job limit is assigned another job, the new job is **not** saved. They are told "Max Jobs Reached", and the resource immediately puts them back on the first job in their existing list at their stored rank — or on the off-duty job if the list is somehow empty. The hire effectively bounces.

{% hint style="info" %}
The Boss Menu checks the target's cap before it hires, so a boss hiring somebody who is already full gets an "Unsuccessful" notice — worded as if the player ID could not be found — and the job is never assigned at all. Other sources do not check: a job centre, an admin command or another resource's `SetJob` will hand the job over, and the bounce above is what the player sees. Either way the fix is for the player to quit a job they no longer want.
{% endhint %}

## What the menu shows

The menu opens as a panel titled "My Jobs" with a badge at the top showing the player's current state: **Clocked In** plus the job label, or **Off Duty** when they are on `Config.offDutyJob`.

Below that is the saved job list. Each row shows the job's label from your framework's `jobs` table and the player's rank in that job, taken from the label of their stored grade. The job they are currently clocked into is highlighted in the list.

A player with no saved jobs gets an empty state instead — nothing is broken, they have never been hired.

Selecting a row opens that job's actions screen, which repeats the label and rank in its header, shows the hours panel when hours tracking is on, and offers Clock In or Clock Out plus Quit Job.

{% hint style="info" %}
A job only appears in the menu if it still exists in your framework's `jobs` table. Deleting or renaming a job there makes it silently disappear from every player's menu while their saved row stays in the database — see [Database](/docs/script-resources/tyrix-multi-job/database.md) and [Troubleshooting](/docs/script-resources/tyrix-multi-job/troubleshooting.md).
{% endhint %}

## Clocking in and out

Clocking in sets the player's active framework job to the selected job. The menu closes and the player is notified.

The rank they come back at is the one stored in their job list, not anything the menu sent. The client asks to clock into a job, and the server looks up that player's own stored grade for it and applies that. If the player has no saved row for the job, nothing happens at all. This is deliberate: a modified client can ask to clock into `police` all it likes, but it cannot ask to do so as a chief, and it cannot clock into a job it was never hired for.

Only one job is active at any moment. Clocking into a different job automatically closes out the one before it, so a player switching from mechanic to police does not need to clock out first — with hours tracking on, the mechanic shift is banked on the way through.

Clocking out sets the player to `Config.offDutyJob`:

```lua
Config.offDutyJob = {
    name = 'unemployed',
    grade = 0,
}
```

This is what makes the rest of your server stop treating the player as employed. Job-gated garages, doors, stashes, dispatch and duty systems all read the framework's active job, so putting the player on a job nobody gates against is what actually takes them off duty. Point this at whatever unemployed or civilian job your framework ships with; it must exist in your `jobs` table.

Clocking out does not remove anything. The job stays in the player's list and they can clock back in whenever they want.

Attempting to clock into the job a player is already on is rejected with an "Already Clocked In" notice rather than restarting their shift, so an accidental double click cannot lose accumulated hours.

## Quitting a job

Quit Job removes the job from the player's list permanently. It asks for confirmation first, because there is no undo.

On confirmation the saved row is deleted, and if the job being quit was the player's active job they are moved to `Config.offDutyJob` in the same step — a player cannot end up clocked into a job they no longer hold. The job disappears from their menu and frees a slot against their cap.

Getting the job back means being hired again by a boss or whatever else grants it on your server. Nothing about quitting is reversible from the player's side, and the boss's employee roster loses them immediately.

Hours are not deleted when a player quits. Their all-time and weekly totals for that job stay in the database, so re-hiring the same character restores their history rather than starting them from zero.

## Hours and goals in the menu

When `Config.hoursTracking.enabled` is `true`, each job's action screen gains an hours panel with two figures: total hours worked all-time for that job, and weekly hours as a progress bar against the weekly goal for the player's current rank. The bar fills as they work and marks itself complete once the goal is met.

The goal shown is the one a boss set for that specific rank, falling back to `Config.hoursTracking.defaultWeeklyGoal` where no goal has been set. Ranks are targeted individually, so a trainee and a supervisor in the same job can be held to different weekly minimums.

Figures are read from the database when the menu opens. A shift still in progress is banked periodically rather than at the moment the menu is read, so an active shift counts up to its last save and the most recent few minutes may not be shown yet. Set `enabled = false` and the panel disappears from the UI entirely. The full model — the reset day, how in-progress shifts are banked, what happens across restarts — is on [Hours Tracking](/docs/script-resources/tyrix-multi-job/hours-tracking.md).

## Job limits

`Config.maxJobs` caps how many jobs one character can hold, and `Config.maxJobsOverrides` raises or removes that cap for individual players by identifier:

```lua
Config.maxJobs = 4                              -- Maximum jobs per player (false = unlimited)
Config.maxJobsOverrides = {
    -- ["license:abc123def456789"] = 10,        -- ESX: This player can have up to 10 jobs
    -- ["ABC12345"] = false,                    -- QB: This character has unlimited jobs
}
```

Players never see a number in the menu. The cap only surfaces at the moment it is hit, as the "Max Jobs Reached" notice described above — and that notice quotes the player's own effective limit, override included, so somebody with an override of 10 is told 10 and not 4.

The limit counts saved jobs, so the off-duty job and anything blacklisted are free. It is enforced on the server every time a job is saved, including a post-write recount that rolls back the insert if two hires landed at the same instant, so the cap holds even under a race. Per-player values are validated on load; a malformed override is ignored with a console warning and the player falls back to `Config.maxJobs`.

Full detail on both settings, including what `false` does, is on [Configuration → Job limits](/docs/script-resources/tyrix-multi-job/configuration.md#job-limits).

Every notification quoted on this page — the hire dialog, the max-jobs notice, the clock in and out confirmations — is editable in one file, covered on [Locales](/docs/script-resources/tyrix-multi-job/locales.md).
