Theme editor

QBCORE JG-Dealerships Fixed and Unlocked (client-side readable)

too much :'( can you lower the price please ? and do you have cl and sv employee ?
I feel like with the amount of time I spent fixing this, this would be the lowest I'd be willing to go unfortunately. The thread will probably cost more to be honest, this will be the lowest it'll be available from me.

Feel free to wait until others have confirmed its authenticity before spending yours!

cl-employees & sv-employees are both available and work without errors, however they are obfuscated.
Here are the cleaned .lua for those if you'd like to replace them:

sv-employees.lua
Code:
-- Employee/role helpers & dealership employee management (server)

---@param src number               -- player source
---@param dealershipId string      -- dealership name/id
---@param roleFilter string|table|nil  -- exact role or one-of list to require (optional)
---@param allowAdmin boolean|nil   -- if true, server admins pass automatically
---@return string|boolean          -- "server_admin" | "owner" | employee role string | false
local function IsEmployee(src, dealershipId, roleFilter, allowAdmin)
    -- Admin bypass
    if allowAdmin then
        if Framework.Server.IsAdmin(src) then
            DebugPrint(src .. " returned as server admin")
            return "server_admin"
        end
    end

    -- Identify player
    local identifier = Framework.Server.GetPlayerIdentifier(src)

    -- Owner check
    local ownerRow = MySQL.single.await(
        "SELECT name FROM dealership_data WHERE name = ? AND owner_id = ?",
        { dealershipId, identifier }
    )
    if ownerRow then
        return "owner"
    end

    -- Employee row (if any)
    local employeeRow = MySQL.single.await(
        "SELECT * FROM dealership_employees WHERE identifier = ? AND dealership = ?",
        { identifier, dealershipId }
    )
    if not employeeRow then
        DebugPrint(identifier .. " is not an employee at " .. dealershipId, "debug")
        return false
    end

    -- Optional role filtering
    if roleFilter then
        local t = type(roleFilter)
        if t == "string" then
            if roleFilter ~= employeeRow.role then
                return false
            end
        elseif t == "table" then
            if not IsItemInList(roleFilter, employeeRow.role) then
                return false
            end
        end
    end

    DebugPrint(identifier .. " is an employee at " .. dealershipId .. " with role " .. employeeRow.role, "debug")
    return employeeRow.role
end

IsEmployee = IsEmployee

-- Callback: ensure caller is an employee at their nearest dealership (within distance)
lib.callback.register("jg-dealerships:server:employee-nearest-dealership", function(src)
    local nearestId, distance = GetCurrentDealershipLocation(GetEntityCoords(GetPlayerPed(src)))
    local cfg = Config.DealershipLocations[nearestId]
    local maxDistance = cfg.directSaleDistance or 50

    if distance > maxDistance then
        Framework.Server.Notify(src, Locale.dealershipTooFarAway, "error")
        return { error = true }
    end

    local roleOrFalse = IsEmployee(src, nearestId)
    if not roleOrFalse then
        Framework.Server.Notify(src, Locale.employeePermissionsError, "error")
        return { error = true }
    end

    return nearestId
end)

-- Callback: list all employees of a dealership (manager/admin only)
lib.callback.register("jg-dealerships:server:get-employees", function(src, dealershipId)
    local ok = IsEmployee(src, dealershipId, "manager", true)
    if not ok then
        Framework.Server.Notify(src, Locale.employeePermissionsError, "error")
        return { error = true }
    end

    local rows = MySQL.query.await(
        "SELECT * FROM dealership_employees WHERE dealership = ? ORDER BY joined DESC",
        { dealershipId }
    )
    return rows
end)

-- Event: ask a target player to confirm being hired
RegisterNetEvent("jg-dealerships:server:request-hire-employee", function(payload)
    local src = source
    payload.requesterId = src

    local ok = IsEmployee(src, payload.dealershipId, "manager", true)
    if not ok then
        Framework.Server.Notify(src, Locale.employeePermissionsError, "error")
        return
    end

    TriggerClientEvent("jg-dealerships:client:show-confirm-employment", payload.playerId, payload)
end)

-- Event: target player rejected the offer
RegisterNetEvent("jg-dealerships:server:employee-hire-rejected", function(targetSrc)
    Framework.Server.Notify(targetSrc, Locale.employeeRejectedMsg, "error")
end)

-- Event: finalize hire (insert row, optionally set job, notify + webhook)
RegisterNetEvent("jg-dealerships:server:hire-employee", function(data)
    local src = source
    local locCfg = Config.DealershipLocations[data.dealershipId]
    local newIdentifier = Framework.Server.GetPlayerIdentifier(data.playerId)

    DebugPrint(("Hiring employee %s(%s) at %s with role %s"):format(
        newIdentifier, data.playerId, data.dealershipId, data.role
    ), "debug")

    MySQL.insert.await(
        "INSERT INTO dealership_employees (identifier, dealership, role) VALUES (?, ?, ?)",
        { newIdentifier, data.dealershipId, data.role }
    )

    -- Optional job assignment from config
    if locCfg.job then
        DebugPrint(("Setting job for %s(%s) to %s with role %s"):format(
            newIdentifier, data.playerId, locCfg.job, data.role
        ), "debug")

        Framework.Server.PlayerSetJob(data.playerId, locCfg.job, data.role)
    end

    -- Webhook/notify
    local pInfo = Framework.Server.GetPlayerInfo(data.playerId)

    SendWebhook(
        src,
        Webhooks.Dealership,
        "Dealership: Employee Hired",
        "success",
        {
            { key = "Dealership", value = data.dealershipId },
            { key = "Employee",   value = (pInfo and pInfo.name) or newIdentifier },
            { key = "Role",       value = data.role },
        }
    )

    Framework.Server.Notify(data.requesterId, Locale.employeeHiredMsg, "success")
    TriggerClientEvent("jg-dealerships:client:update-blips-text-uis", src)
end)

-- Event: fire an employee (manager/admin only), set unemployed, notify + webhook
RegisterNetEvent("jg-dealerships:server:fire-employee", function(identifier, dealershipId)
    local src = source

    local ok = IsEmployee(src, dealershipId, "manager", true)
    if not ok then
        Framework.Server.Notify(src, Locale.employeePermissionsError, "error")
        return
    end

    MySQL.insert.await(
        "DELETE FROM dealership_employees WHERE identifier = ? AND dealership = ?",
        { identifier, dealershipId }
    )

    local onlineId = Framework.Server.GetPlayerFromIdentifier(identifier)
    if onlineId then
        Framework.Server.PlayerSetJob(onlineId, "unemployed", 0)
        Framework.Server.Notify(
            onlineId,
            (string.gsub(Locale.firedNotification, "%%{value}", dealershipId)),
            "error"
        )
        TriggerClientEvent("jg-dealerships:client:update-blips-text-uis", onlineId)
    else
        Framework.Server.PlayerSetJobOffline(identifier, "unemployed", 0)
    end

    local pInfo = Framework.Server.GetPlayerInfoFromIdentifier(identifier)
    SendWebhook(
        src,
        Webhooks.Dealership,
        "Dealership: Employee Fired",
        "danger",
        {
            { key = "Dealership", value = dealershipId },
            { key = "Employee",   value = (pInfo and pInfo.name) or identifier },
        }
    )
end)

-- Event: update employee role (manager/admin only), update job grade, webhook
RegisterNetEvent("jg-dealerships:server:update-employee-role", function(identifier, dealershipId, newRole)
    local src = source

    local ok = IsEmployee(src, dealershipId, "manager", true)
    if not ok then
        Framework.Server.Notify(src, Locale.employeePermissionsError, "error")
        return
    end

    local locCfg = Config.DealershipLocations[dealershipId]

    MySQL.insert.await(
        "UPDATE dealership_employees SET role = ? WHERE identifier = ? AND dealership = ?",
        { newRole, identifier, dealershipId }
    )

    local onlineId = Framework.Server.GetPlayerFromIdentifier(identifier)
    if onlineId then
        if locCfg.job then
            Framework.Server.PlayerSetJob(onlineId, locCfg.job, newRole)
            TriggerClientEvent("jg-dealerships:client:update-blips-text-uis", onlineId)
        end
    else
        Framework.Server.PlayerSetJobOffline(identifier, locCfg.job, newRole)
    end

    local pInfo = Framework.Server.GetPlayerInfoFromIdentifier(identifier)
    SendWebhook(
        src,
        Webhooks.Dealership,
        "Dealership: Employee Updated",
        nil,
        {
            { key = "Dealership", value = dealershipId },
            { key = "Employee",   value = (pInfo and pInfo.name) or identifier },
            { key = "New role",   value = newRole },
        }
    )
end)



Let me know if you need anything else!
 
Couldn't put it all into one message due to 1000 character limit.

cl-employees.lua

Code:
-- jg-dealerships | Client-side NUI <-> server bridge for employment actions

-- Show the “confirm employment” modal to a player
RegisterNetEvent("jg-dealerships:client:show-confirm-employment", function(payload)
    SetNuiFocus(true, true)
    SendNUIMessage({
        type   = "showConfirmEmployment",
        data   = payload,   -- { requesterId, playerId, dealershipId, role, ... }
        config = Config,
        locale = Locale
    })
end)

-- Candidate accepts hire
RegisterNUICallback("accept-hire-request", function(data, cb)
    -- data: { requesterId, playerId, dealershipId, role, ... }
    TriggerServerEvent("jg-dealerships:server:hire-employee", data)
    cb(true)
end)

-- Candidate denies hire
RegisterNUICallback("deny-hire-request", function(data, cb)
    -- data: { requesterId = number }
    TriggerServerEvent("jg-dealerships:server:employee-hire-rejected", data.requesterId)
    cb(true)
end)

-- Manager requests to hire someone (opens confirm on target)
RegisterNUICallback("request-hire-employee", function(data, cb)
    -- data: { requesterId, playerId, dealershipId, role }
    TriggerServerEvent("jg-dealerships:server:request-hire-employee", data)
    cb(true)
end)

-- Manager fires an employee
RegisterNUICallback("fire-employee", function(data, cb)
    -- data: { identifier = string, dealershipId = string }
    TriggerServerEvent("jg-dealerships:server:fire-employee", data.identifier, data.dealershipId)
    cb(true)
end)

-- Manager updates an employee’s role
RegisterNUICallback("update-employee-role", function(data, cb)
    -- data: { identifier = string, dealershipId = string, newRole = string }
    TriggerServerEvent("jg-dealerships:server:update-employee-role", data.identifier, data.dealershipId, data.newRole)
    cb(true)
end)
 
does the employment work on your end, i tested it now and it didn't work for me ! thank you for the share but didn't work
 
does the employment work on your end, i tested it now and it didn't work for me ! thank you for the share but didn't work
Employment and boss menu works excellently for me with the obfuscated code. I'm guessing something else is messed up in yours, unfortunately.
 
Employment and boss menu works excellently for me with the obfuscated code. I'm guessing something else is messed up in yours, unfortunately.
Thank you for sharing the employment server side code! really appreciate it. I would like to ask you to please share the cl-main.lua if you have it as it seems to be missing in my version. Thanks again and have a good day
 
it work about 80% for qbcore i juste have a little problem with loading menu in the server admin véhicle, display, order, and sale took forever to load and sometimes it doesn't and the focus ui is not totally there but the script is working
 
Back
Top Bottom