JIT
fixing errors and updating icons in privatinköp script
client.lua ESX = nil local display = false local markers = {} -- Initiera ESX Citizen.CreateThread(function() while ESX == nil do TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) Citizen.Wait(10) end end) -- Visa / Dölj NUI function SetDisplay(bool) display = bool SetNuiFocus(bool, bool) SendNUIMessage({ action = "setVisible", visible = bool }) end -- Event för att öppna UI RegisterNetEvent('privatinkop:openUI') AddEventHandler('privatinkop:openUI', function() SetDisplay(true) end) -- NUI callback för att stänga UI RegisterNUICallback('close', function(data, cb) SetDisplay(false) TriggerEvent('esx:showNotification', 'Stängde Privat Inköp') cb('ok') end) -- NUI notify (för felmeddelanden mm) RegisterNUICallback('notify', function(data, cb) local msg = data.message or "Okänt fel" TriggerEvent('esx:showNotification', msg) cb('ok') end) -- Testkommando för att öppna UI RegisterCommand('privatinkop', function() TriggerEvent('privatinkop:openUI') end) -- NUI callback för att hämta coords till NUI (om du behöver det) RegisterNUICallback("getCoords", function(data, cb) local playerPed = PlayerPedId() local coords = GetEntityCoords(playerPed) local coordsString = string.format("%.2f, %.2f, %.2f", coords.x, coords.y, coords.z) SendNUIMessage({ action = "setCoords", coords = coordsString }) cb("ok") end) -- NUI callback för att spara koordinater, skickar till servern RegisterNUICallback("sparaKoordinater", function(data, cb) if data.x and data.y and data.z then TriggerServerEvent("privatinkop:sparaKoordinater", { x = tonumber(data.x), y = tonumber(data.y), z = tonumber(data.z) }) else TriggerEvent('esx:showNotification', 'Fel vid sparande av koordinater.') end cb("ok") end) -- Event för att ta emot markörer från servern RegisterNetEvent("privatinkop:läggTillMarker") AddEventHandler("privatinkop:läggTillMarker", function(coords) table.insert(markers, vector3(coords.x, coords.y, coords.z)) print(("Ny markör tillagd: %.2f, %.2f, %.2f"):format(coords.x, coords.y, coords.z)) end) -- Loop som ritar markörer och hanterar input nära markörerna Citizen.CreateThread(function() while true do Citizen.Wait(0) local playerCoords = GetEntityCoords(PlayerPedId()) for _, coord in ipairs(markers) do DrawMarker( 1, -- Cylinder marker coord.x, coord.y, coord.z - 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0, 255, 0, 200, -- Grön färg false, true, 2, false, nil, nil, false ) local dist = #(playerCoords - coord) if dist < 2.0 then ESX.ShowHelpNotification("Tryck ~INPUT_CONTEXT~ (E) för att öppna Privat Inköp") if IsControlJustReleased(0, 38) then -- 38 = E TriggerEvent('privatinkop:openUI') end end end end end) config.lua Config = {} -- Lägg till Steam Hexs för de som ska kunna använda /privatinkop Config.AllowedSteamHex = { "steam:110000112345678", -- exempel hex "steam:110000112345679" } server.lua local savedCoords = {} -- Kontrollera om spelaren har behörighet att använda kommandot RegisterCommand('privatinkop', function(source, args, rawCommand) local src = source local steamHex = GetPlayerIdentifiers(src)[1] -- steam hex är alltid första identifieraren local allowed = false for _, hex in pairs(Config.AllowedSteamHex) do if hex == steamHex then allowed = true break end end if allowed then -- Skicka event till klienten att öppna UI TriggerClientEvent('privatinkop:openUI', src) else TriggerClientEvent('chat:addMessage', src, { args = { "^1SYSTEM", "Du har inte behörighet att använda detta kommando." } }) end end) -- Event som hanterar sparandet av koordinater från klienten RegisterNetEvent("privatinkop:sparaKoordinater") AddEventHandler("privatinkop:sparaKoordinater", function(data) local src = source if data and data.x and data.y and data.z then -- Spara i tabell med key = player source, value = coords savedCoords[src] = {x = tonumber(data.x), y = tonumber(data.y), z = tonumber(data.z)} print(("[PrivatInköp] Sparade coords från spelare %d: %.2f, %.2f, %.2f"):format(src, data.x, data.y, data.z)) -- Skicka coords tillbaka till just den spelaren för att visa markör TriggerClientEvent("privatinkop:läggTillMarker", src, savedCoords[src]) else print("[PrivatInköp] Felaktiga koordinater från spelare " .. src) end end) -- (Valfritt) Kommando för att skicka alla sparade coords till en spelare (exempelvis admin) RegisterCommand('visaMarkers', function(source, args, rawCommand) local src = source for _, coords in pairs(savedCoords) do TriggerClientEvent("privatinkop:läggTillMarker", src, coords) end TriggerClientEvent('chat:addMessage', src, { args = { "^2SYSTEM", "Visar alla sparade markörer." } }) end) script.js document.addEventListener('DOMContentLoaded', () => { const appContainer = document.getElementById('appContainer'); function setAppVisible(show) { appContainer.style.display = show ? 'flex' : 'none'; } window.setAppVisible = setAppVisible; const RES = typeof GetParentResourceName === 'function' ? GetParentResourceName() : 'privatinkop'; function nuiPost(name, payload = {}) { fetch(`https://${RES}/${name}`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8' }, body: JSON.stringify(payload) }).catch(() => {}); } document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { setAppVisible(false); nuiPost('close'); } }); window.addEventListener('message', (event) => { const data = event.data || {}; if (data.action === 'setVisible') setAppVisible(!!data.visible); if (data.action === 'toggle') setAppVisible(appContainer.style.display === 'none'); if (data.action === 'setCoords') { const createCoords = document.getElementById('createCoords'); createCoords.value = data.coords; } }); const purchaseList = document.getElementById('purchaseList'); const emptyMessage = document.getElementById('emptyMessage'); const searchInput = document.getElementById('searchInput'); const btnOpenCreateModal = document.getElementById('btnOpenCreateModal'); const createModalBackdrop = document.getElementById('createModalBackdrop'); const createModal = document.getElementById('createModal'); const btnCancelCreate = document.getElementById('btnCancelCreate'); const btnSaveCreate = document.getElementById('btnSaveCreate'); const createName = document.getElementById('createName'); const createWeaponName = document.getElementById('createWeaponName'); const createDisplayName = document.getElementById('createDisplayName'); const createPrice = document.getElementById('createPrice'); const createCoords = document.getElementById('createCoords'); const btnGetCoords = document.getElementById('btnGetCoords'); const createSteamHex = document.getElementById('createSteamHex'); const editModalBackdrop = document.getElementById('editModalBackdrop'); const editModal = document.getElementById('editModal'); const btnCancelEdit = document.getElementById('btnCancelEdit'); const btnSaveEdit = document.getElementById('btnSaveEdit'); const editWeaponName = document.getElementById('editWeaponName'); const editDisplayName = document.getElementById('editDisplayName'); const editPrice = document.getElementById('editPrice'); const editSteamHex = document.getElementById('editSteamHex'); let purchases = []; let currentEditIndex = null; function showModal(backdrop, modal) { backdrop.classList.add('show'); setTimeout(() => modal.classList.add('show'), 10); } function hideModal(backdrop, modal) { modal.classList.remove('show'); setTimeout(() => backdrop.classList.remove('show'), 300); } btnOpenCreateModal.addEventListener('click', () => { createName.value = ''; createWeaponName.value = ''; createDisplayName.value = ''; createPrice.value = ''; createCoords.value = ''; createSteamHex.value = ''; showModal(createModalBackdrop, createModal); }); btnCancelCreate.addEventListener('click', () => { hideModal(createModalBackdrop, createModal); }); btnSaveCreate.addEventListener('click', () => { if (!createName.value.trim()) { nuiPost('notify', { message: 'Du måste ange ett namn!' }); return; } const newPurchase = { name: createName.value.trim(), weaponName: createWeaponName.value.trim(), displayName: createDisplayName.value.trim(), price: createPrice.value.trim() || 0, coords: createCoords.value.trim(), steamHex: createSteamHex.value.trim() }; purchases.push(newPurchase); renderPurchases(searchInput.value); hideModal(createModalBackdrop, createModal); }); btnGetCoords.addEventListener('click', () => { btnGetCoords.disabled = true; btnGetCoords.textContent = '⏳'; fetch(`https://${RES}/getCoords`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8' }, body: JSON.stringify({}) }).finally(() => { btnGetCoords.disabled = false; btnGetCoords.textContent = '🗺️'; }); }); function renderPurchases(filter = '') { purchaseList.innerHTML = ''; const filtered = purchases .map((purchase, index) => ({ ...purchase, index })) .filter(p => { const text = `${p.name} ${p.weaponName} ${p.displayName} ${p.steamHex}`.toLowerCase(); return text.includes(filter.toLowerCase()); }); if (filtered.length === 0) { emptyMessage.style.display = 'flex'; return; } emptyMessage.style.display = 'none'; filtered.forEach((p) => { const div = document.createElement('div'); div.className = 'purchase-item'; div.innerHTML = ` <div class="name">${p.name} <div class="icon"></div></div> <div class="details"> Vapen: ${p.weaponName}<br /> Visningsnamn: ${p.displayName}<br /> Pris: ${p.price} kr<br /> Steam HEX: ${p.steamHex}<br /> ${p.coords ? `Koordinater: ${p.coords}` : ''} </div> <div class="actions"> <button class="btn-edit" data-index="${p.index}">Redigera</button> <button class="btn-delete" data-index="${p.index}">Ta bort</button> </div> `; purchaseList.appendChild(div); }); // Lägg till eventlisteners för edit och delete EFTER rendering document.querySelectorAll('.btn-edit').forEach(button => { button.addEventListener('click', e => { currentEditIndex = parseInt(e.target.dataset.index, 10); const p = purchases[currentEditIndex]; if (!p) return; editWeaponName.value = p.weaponName; editDisplayName.value = p.displayName; editPrice.value = p.price; editSteamHex.value = p.steamHex; showModal(editModalBackdrop, editModal); }); }); document.querySelectorAll('.btn-delete').forEach(button => { button.addEventListener('click', e => { e.preventDefault(); e.stopPropagation(); const idx = parseInt(e.target.dataset.index, 10); console.log('Trying to delete idx:', idx); if (Number.isInteger(idx) && purchases[idx]) { if (window.confirm('Vill du verkligen ta bort detta inköp?')) { purchases.splice(idx, 1); console.log('Deleted index:', idx); console.log('Remaining purchases:', purchases); renderPurchases(searchInput.value); } else { console.log('User cancelled deletion'); } } else { console.log('Invalid index or item does not exist'); } }); }); } fetch(`https://${GetParentResourceName()}/sparaKoordinater`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: JSON.stringify({ x: coords.x, y: coords.y, z: coords.z }) }) searchInput.addEventListener('input', () => { renderPurchases(searchInput.value); }); btnCancelEdit.addEventListener('click', () => { hideModal(editModalBackdrop, editModal); }); btnSaveEdit.addEventListener('click', () => { if (currentEditIndex === null) return; purchases[currentEditIndex].weaponName = editWeaponName.value.trim(); purchases[currentEditIndex].displayName = editDisplayName.value.trim(); purchases[currentEditIndex].price = editPrice.value.trim() || 0; purchases[currentEditIndex].steamHex = editSteamHex.value.trim(); renderPurchases(searchInput.value); hideModal(editModalBackdrop, editModal); }); // Initial render renderPurchases(); }); style.css @import url('https://fonts.googleapis.com/css2?family=Segoe+UI:wght@600&display=swap'); body { background-color: transparent; margin: 0; font-family: 'Segoe UI', sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; } /* Appen är gömd tills du visar den via JS */ #appContainer { display: none; } .container { width: 900px; height: 530px; background-color: #1a3d7a; border-radius: 6px; box-shadow: 0 0 15px rgba(0,0,0,0.6); display: flex; flex-direction: column; } .header { height: 60px; background-color: #2353a1; display: flex; align-items: center; padding: 0 24px; color: white; font-weight: 600; font-size: 20px; gap: 10px; } .icon-store { width: 25px; height: 25px; background: white; mask: url('data:image/svg+xml;utf8,<svg fill="black" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M3 9v11h18V9H3zm16 9H5v-7h14v7zm-1-16H6v3h12V2z"/></svg>') no-repeat center; mask-size: contain; } .icon-diamond { margin-left: auto; width: 30px; height: 30px; background: #3b6ecc; mask: url('data:image/svg+xml;utf8,<svg fill="black" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2l-8 8 8 12 8-12-8-8z"/></svg>') no-repeat center; mask-size: contain; } .content { flex-grow: 1; padding: 18px 24px 24px 24px; display: flex; flex-direction: column; } .search-row { display: flex; gap: 10px; } .search-row input { flex-grow: 1; background-color: #1a3d7a; border: 2px solid #4e75ca; border-radius: 6px; padding: 10px 14px; font-weight: 600; font-size: 16px; color: white; box-sizing: border-box; transition: border-color 0.3s ease; } .search-row input::placeholder { color: #8c9dbc; font-weight: 600; font-size: 15px; } .search-row input:focus { outline: none; border-color: #3db855; background-color: #164076; } .search-row button { background-color: #4e75ca; font-weight: 600; font-size: 15px; height: 42px; width: 140px; border-radius: 6px; border: none; cursor: pointer; color: white; transition: background-color 0.3s ease; text-transform: uppercase; } .search-row button:hover { background-color: #3b5bbf; } .empty-message { flex-grow: 1; display: flex; justify-content: center; align-items: center; color: #8c9dbc; font-size: 19px; text-align: center; user-select: none; } .purchase-list { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 8px; } .purchase-item { background-color: #1f3b82; border-radius: 8px; padding: 14px; color: white; margin-bottom: 16px; width: 250px; box-shadow: 0 2px 5px rgba(0,0,0,0.3); display: flex; flex-direction: column; justify-content: space-between; } .purchase-item .name { font-weight: 700; font-size: 16px; margin-bottom: 10px; display: flex; align-items: center; justify-content: space-between; } .purchase-item .name .icon { background-image: url('data:image/svg+xml;utf8,<svg fill="white" height="16" viewBox="0 0 24 24" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M12 12c2.7 0 8 1.3 8 4v2H4v-2c0-2.7 5.3-4 8-4zm0-2c-1.7 0-3-1.3-3-3s1.3-3 3-3 3 1.3 3 3-1.3 3-3 3z"/></svg>'); background-size: contain; width: 16px; height: 16px; } .purchase-item .details { font-size: 13px; line-height: 1.4; margin-bottom: 12px; } .purchase-item .actions { display: flex; gap: 8px; } .purchase-item .actions button { padding: 6px 10px; background-color: #2c57d0; color: white; border: none; border-radius: 4px; font-size: 13px; cursor: pointer; transition: background-color 0.2s ease; } .purchase-item .actions button:hover { background-color: #3e6ef3; } /* Modal backdrop */ .modal-backdrop { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(26, 61, 122, 0.8); display: none; justify-content: center; align-items: center; z-index: 1000; opacity: 0; transition: opacity 0.3s ease; } .modal-backdrop.show { display: flex; opacity: 1; } .modal { width: 450px; /* Lite mindre bredd som i referensbild */ background-color: #1a3d7a; border-radius: 10px; box-shadow: 0 0 15px rgba(0,0,0,0.6); display: flex; flex-direction: column; padding: 20px; opacity: 0; transform: translateY(-20px); transition: opacity 0.3s ease, transform 0.3s ease; } .modal.show { opacity: 1; transform: translateY(0); } .modal-header { font-weight: 600; font-size: 18px; color: white; margin-bottom: 12px; } .modal-content label { font-size: 13px; color: #8c9dbc; margin-bottom: 5px; font-weight: 600; } .modal-content input[type="text"], .modal-content input[type="number"] { width: 100%; border: 2px solid #4e75ca; border-radius: 6px; padding: 10px 14px; font-weight: 600; font-size: 15px; color: white; background-color: #164076; transition: border-color 0.3s ease; box-sizing: border-box; margin-bottom: 12px; } .modal-content input[type="text"]::placeholder, .modal-content input[type="number"]::placeholder { color: #8c9dbc; font-weight: 600; font-size: 14px; } .modal-content input[type="text"]:focus, .modal-content input[type="number"]:focus { outline: none; border-color: #3db855; background-color: #164076; } /* Rad med flera inputs (Tillgängliga varor) */ .flex-row { display: flex; gap: 10px; margin-top: 10px; } .flex-row input[type="text"], .flex-row input[type="number"] { border-radius: 6px; font-size: 14px; padding: 8px 12px; } .flex-row input[type="text"]:nth-child(1) { flex: 2; /* större */ } .flex-row input[type="text"]:nth-child(2) { flex: 3; /* lite större */ } .flex-row input[type="number"] { flex: 1; } .modal-footer { display: flex; justify-content: flex-end; gap: 12px; margin-top: 20px; } .btn-cancel { font-weight: 600; font-size: 14px; color: #ff5555; background: transparent; border: none; cursor: pointer; letter-spacing: 1.5px; padding: 6px 10px; transition: background-color 0.3s ease; } .btn-cancel:hover { background-color: rgba(255, 85, 85, 0.1); border-radius: 6px; } .btn-save { font-weight: 600; font-size: 14px; padding: 8px 20px; border-radius: 6px; background-color: #7eea28; /* grön */ border: none; color: black; cursor: pointer; letter-spacing: 1.5px; transition: background-color 0.3s ease; } .btn-save:hover { background-color: #5ec21f; } /* Placera "Steam ID" input ovanför tillgängliga varor */ .steam-id-input { width: 100%; margin-bottom: 15px; padding: 10px 14px; border-radius: 6px; border: 2px solid #4e75ca; font-weight: 600; font-size: 15px; background-color: #164076; color: white; box-sizing: border-box; } .steam-id-input::placeholder { color: #8c9dbc; font-weight: 600; font-size: 14px; } .steam-id-input:focus { outline: none; border-color: #3db855; background-color: #164076; } .btn-cancel { font-weight: 600; font-size: 16px; color: #ff5555; background: transparent; border: none; cursor: pointer; letter-spacing: 1.5px; } index.html <!DOCTYPE html> <html lang="sv"> <head> <meta charset="UTF-8" /> <title>Privatinköp med lista och redigering</title> <link rel="stylesheet" href="style.css"> </head> <body> <!-- Själva appen (gömd som standard via CSS) --> <div class="container" id="appContainer"> <div class="header"> <div class="icon-store"></div> PRIVATINKÖP <div class="icon-diamond"></div> </div> <div class="content"> <div class="search-row"> <input type="text" id="searchInput" placeholder="Sök efter inköp..." /> <button id="btnOpenCreateModal">SKAPA INKÖP</button> </div> <div class="purchase-list" id="purchaseList"></div> <div class="empty-message" id="emptyMessage">Inga privata inköp hittades</div> </div> </div> <!-- Skapa modal --> <div class="modal-backdrop" id="createModalBackdrop"> <div class="modal" id="createModal"> <div class="modal-content"> <label for="createName">Namn</label> <input type="text" id="createName" placeholder="Namn" /> <label for="createWeaponName">Vapen namn</label> <input type="text" id="createWeaponName" placeholder="Vapen namn" /> <label for="createDisplayName">Visningsnamn</label> <input type="text" id="createDisplayName" placeholder="Visningsnamn" /> <label for="createPrice">Pris</label> <input type="number" id="createPrice" placeholder="Pris" min="0" /> <label for="createCoords">Koordinater</label> <div style="display:flex; gap:8px; align-items:center;"> <input type="text" id="createCoords" placeholder="X, Y, Z" /> <button type="button" id="btnGetCoords" title="Hämta nuvarande position">🗺️</button> </div> <label for="createSteamHex">Steam HEX</label> <input type="text" id="createSteamHex" placeholder="Steam HEX" maxlength="17" /> </div> <div class="modal-footer"> <button class="btn-cancel" id="btnCancelCreate">Avbryt</button> <button class="btn-save" id="btnSaveCreate">Skapa</button> </div> </div> </div> <!-- Redigera modal --> <div class="modal-backdrop" id="editModalBackdrop"> <div class="modal" id="editModal"> <div class="modal-content"> <label for="editWeaponName">Vapen namn</label> <input type="text" id="editWeaponName" placeholder="Vapen namn" /> <label for="editDisplayName">Visningsnamn</label> <input type="text" id="editDisplayName" placeholder="Visningsnamn" /> <label for="editPrice">Pris</label> <input type="number" id="editPrice" placeholder="Pris" min="0" /> <label for="editSteamHex">Steam HEX</label> <input type="text" id="editSteamHex" placeholder="Steam HEX" maxlength="17" /> </div> <div class="modal-footer"> <button class="btn-cancel" id="btnCancelEdit">Avbryt</button> <button class="btn-save" id="btnSaveEdit">Spara</button> </div> </div> </div> <script src="script.js" defer></script> </body> </html> Fixa alla fel errros osv jag har nyaste esx legacy , Fixa allt som e fel sen fixa ist för vanliga ikomner det ska vara mdi ikoner
Updated files
v1
Litet fel när man satt ut och man ska öppna sitt privatinköp får man uin man ska it de man ska få upp esx menu med de vapnet man lagt till att man ska kunna köpa ut för just den personen? Sen at bort all ondöig kod
Updated files
v2