Files
red-valley/docs/nui_simulator.html

874 lines
30 KiB
HTML
Raw Normal View History

2026-03-29 21:41:17 +03:00
<!DOCTYPE html>
<html lang="ro">
2026-03-30 02:20:03 +03:00
2026-03-29 21:41:17 +03:00
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Red Valley — NUI Simulator</title>
2026-03-30 02:20:03 +03:00
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap"
rel="stylesheet">
2026-03-29 21:41:17 +03:00
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-primary: #0a0b0d;
--bg-secondary: #111318;
--bg-tertiary: #181a1f;
--bg-card: #1a1c22;
--accent: #ff1a35;
--accent-glow: rgba(255, 26, 53, 0.15);
--text-primary: #ffffff;
--text-secondary: #8b8d94;
--text-muted: #555760;
--border: #222430;
--border-hover: #333540;
--success: #22c55e;
--warning: #f59e0b;
}
body {
font-family: 'Inter', sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
min-height: 100vh;
overflow: hidden;
}
/* Layout */
.app {
display: flex;
height: 100vh;
}
/* Sidebar */
.sidebar {
width: 320px;
background: var(--bg-secondary);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
flex-shrink: 0;
}
.sidebar-header {
padding: 20px;
border-bottom: 1px solid var(--border);
}
.sidebar-header h1 {
font-size: 16px;
font-weight: 700;
display: flex;
align-items: center;
gap: 8px;
}
.sidebar-header h1 .badge {
font-size: 11px;
font-weight: 600;
background: var(--accent);
padding: 2px 8px;
border-radius: 4px;
letter-spacing: 0.5px;
}
.sidebar-header p {
font-size: 12px;
color: var(--text-muted);
margin-top: 4px;
}
.search-box {
padding: 12px 20px;
border-bottom: 1px solid var(--border);
}
.search-box input {
width: 100%;
height: 36px;
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text-primary);
font-size: 13px;
font-family: 'Inter', sans-serif;
padding: 0 12px 0 36px;
outline: none;
transition: border-color 0.2s;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23555760' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.3-4.3'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: 10px center;
}
.search-box input:focus {
border-color: var(--accent);
}
.search-box input::placeholder {
color: var(--text-muted);
}
.nui-list {
flex: 1;
overflow-y: auto;
padding: 8px;
}
.nui-list::-webkit-scrollbar {
width: 4px;
}
.nui-list::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 2px;
}
.nui-list::-webkit-scrollbar-track {
background: transparent;
}
.nui-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border-radius: 6px;
cursor: pointer;
transition: all 0.15s;
margin-bottom: 2px;
}
.nui-item:hover {
background: var(--bg-tertiary);
}
.nui-item.active {
background: var(--accent-glow);
border: 1px solid rgba(255, 26, 53, 0.3);
}
.nui-item .icon {
width: 34px;
height: 34px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
flex-shrink: 0;
background: var(--bg-tertiary);
border: 1px solid var(--border);
}
.nui-item.active .icon {
background: var(--accent);
border-color: var(--accent);
}
.nui-item .info {
flex: 1;
min-width: 0;
}
.nui-item .info .name {
font-size: 13px;
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.nui-item .info .path {
font-size: 11px;
color: var(--text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.nui-item .tag {
font-size: 10px;
font-weight: 500;
padding: 2px 6px;
border-radius: 3px;
flex-shrink: 0;
}
2026-03-30 02:20:03 +03:00
.tag-custom {
background: rgba(34, 197, 94, 0.15);
color: #22c55e;
}
.tag-vendor {
background: rgba(96, 165, 250, 0.15);
color: #60a5fa;
}
.tag-core {
background: rgba(192, 132, 252, 0.15);
color: #c084fc;
}
2026-03-29 21:41:17 +03:00
/* Main content */
.main {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
}
.toolbar {
height: 50px;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
padding: 0 16px;
gap: 8px;
}
.toolbar .btn {
height: 32px;
padding: 0 14px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg-tertiary);
color: var(--text-secondary);
font-size: 12px;
font-weight: 500;
font-family: 'Inter', sans-serif;
cursor: pointer;
display: flex;
align-items: center;
gap: 5px;
transition: all 0.15s;
}
.toolbar .btn:hover {
background: var(--bg-card);
color: var(--text-primary);
border-color: var(--border-hover);
}
.toolbar .btn-accent {
background: var(--accent);
border-color: var(--accent);
color: white;
}
.toolbar .btn-accent:hover {
opacity: 0.85;
}
.toolbar .resolution {
margin-left: auto;
display: flex;
align-items: center;
gap: 6px;
}
.toolbar .resolution select {
height: 32px;
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text-primary);
font-size: 12px;
font-family: 'Inter', sans-serif;
padding: 0 8px;
outline: none;
cursor: pointer;
}
.toolbar .resolution label {
font-size: 11px;
color: var(--text-muted);
}
/* Preview area */
.preview-container {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
2026-03-30 02:20:03 +03:00
background:
2026-03-29 21:41:17 +03:00
radial-gradient(circle at 20% 50%, rgba(255, 26, 53, 0.03) 0%, transparent 50%),
2026-03-30 02:20:03 +03:00
repeating-linear-gradient(0deg, transparent, transparent 19px, rgba(255, 255, 255, 0.02) 19px, rgba(255, 255, 255, 0.02) 20px),
repeating-linear-gradient(90deg, transparent, transparent 19px, rgba(255, 255, 255, 0.02) 19px, rgba(255, 255, 255, 0.02) 20px),
2026-03-29 21:41:17 +03:00
var(--bg-primary);
padding: 20px;
position: relative;
}
.preview-frame {
width: 1280px;
height: 720px;
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
background: #000;
position: relative;
box-shadow: 0 0 60px rgba(0, 0, 0, 0.5);
transform-origin: center center;
}
.preview-frame iframe {
width: 100%;
height: 100%;
border: none;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
gap: 12px;
}
.empty-state .icon-large {
width: 64px;
height: 64px;
border-radius: 16px;
background: var(--bg-tertiary);
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
border: 1px solid var(--border);
}
.empty-state h3 {
font-size: 16px;
font-weight: 600;
}
.empty-state p {
font-size: 13px;
color: var(--text-muted);
text-align: center;
max-width: 300px;
}
/* Status bar */
.status-bar {
height: 30px;
background: var(--bg-secondary);
border-top: 1px solid var(--border);
display: flex;
align-items: center;
padding: 0 16px;
font-size: 11px;
color: var(--text-muted);
gap: 16px;
}
.status-bar .dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--success);
}
.status-bar .item {
display: flex;
align-items: center;
gap: 5px;
}
/* Simulation Panel */
.sim-panel {
width: 280px;
background: var(--bg-secondary);
border-left: 1px solid var(--border);
display: flex;
flex-direction: column;
flex-shrink: 0;
}
.sim-panel-header {
padding: 16px 16px 12px;
border-bottom: 1px solid var(--border);
}
.sim-panel-header h3 {
font-size: 13px;
font-weight: 700;
display: flex;
align-items: center;
gap: 6px;
}
.sim-panel-header p {
font-size: 11px;
color: var(--text-muted);
margin-top: 3px;
}
.sim-section {
padding: 14px 16px;
border-bottom: 1px solid var(--border);
}
.sim-section h4 {
font-size: 11px;
font-weight: 600;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 10px;
}
.sim-toggle {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 0;
}
.sim-toggle label {
font-size: 12px;
font-weight: 500;
}
.toggle-switch {
position: relative;
width: 40px;
height: 22px;
cursor: pointer;
}
.toggle-switch input {
display: none;
}
.toggle-slider {
position: absolute;
2026-03-30 02:20:03 +03:00
top: 0;
left: 0;
right: 0;
bottom: 0;
2026-03-29 21:41:17 +03:00
background: #333;
border-radius: 11px;
transition: 0.2s;
}
.toggle-slider::before {
content: '';
position: absolute;
width: 16px;
height: 16px;
left: 3px;
top: 3px;
background: white;
border-radius: 50%;
transition: 0.2s;
}
2026-03-30 02:20:03 +03:00
.toggle-switch input:checked+.toggle-slider {
2026-03-29 21:41:17 +03:00
background: var(--success);
}
2026-03-30 02:20:03 +03:00
.toggle-switch input:checked+.toggle-slider::before {
2026-03-29 21:41:17 +03:00
transform: translateX(18px);
}
.sim-job-btn {
width: 100%;
padding: 10px 12px;
margin-bottom: 6px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg-tertiary);
color: var(--text-primary);
font-size: 12px;
font-weight: 500;
font-family: 'Inter', sans-serif;
cursor: pointer;
transition: all 0.15s;
display: flex;
align-items: center;
gap: 8px;
text-align: left;
}
.sim-job-btn:hover {
background: var(--bg-card);
border-color: var(--border-hover);
}
.sim-job-btn .req {
margin-left: auto;
font-size: 10px;
color: var(--accent);
font-weight: 600;
}
.sim-log {
flex: 1;
overflow-y: auto;
padding: 12px 16px;
}
.sim-log h4 {
font-size: 11px;
font-weight: 600;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
}
.sim-log::-webkit-scrollbar {
width: 4px;
}
.sim-log::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 2px;
}
.log-entry {
font-size: 11px;
padding: 4px 0;
2026-03-30 02:20:03 +03:00
border-bottom: 1px solid rgba(255, 255, 255, 0.03);
2026-03-29 21:41:17 +03:00
line-height: 1.5;
}
.log-entry .time {
color: var(--text-muted);
font-size: 10px;
}
2026-03-30 02:20:03 +03:00
.log-success {
color: var(--success);
}
.log-error {
color: var(--accent);
}
.log-info {
color: #60a5fa;
}
2026-03-29 21:41:17 +03:00
</style>
</head>
2026-03-30 02:20:03 +03:00
2026-03-29 21:41:17 +03:00
<body>
<div class="app">
<!-- Sidebar -->
<div class="sidebar">
<div class="sidebar-header">
<h1>🎮 NUI Simulator <span class="badge">RV</span></h1>
<p>Test NUI resources without launching FiveM</p>
</div>
<div class="search-box">
<input type="text" id="searchInput" placeholder="Search resources...">
</div>
<div class="nui-list" id="nuiList"></div>
</div>
<!-- Main -->
<div class="main">
<div class="toolbar">
<button class="btn" id="btnRefresh" title="Reload NUI">↻ Reload</button>
<button class="btn" id="btnSendShow" title="Send show message">📤 Send {show}</button>
<button class="btn" id="btnSendHide" title="Send hide message">📥 Send {hide}</button>
<button class="btn" id="btnDevTools" title="Open in new tab">🔗 Open Tab</button>
<div class="resolution">
<label>Resolution:</label>
<select id="resolutionSelect">
<option value="1920x1080">1920×1080</option>
<option value="1280x720" selected>1280×720</option>
<option value="1600x900">1600×900</option>
<option value="2560x1440">2560×1440</option>
</select>
</div>
</div>
<div class="preview-container">
<div class="preview-frame" id="previewFrame">
<div class="empty-state" id="emptyState">
<div class="icon-large">🖥️</div>
<h3>No NUI Selected</h3>
<p>Select a resource from the sidebar to preview its NUI interface</p>
</div>
</div>
</div>
<div class="status-bar">
<div class="item">
<span class="dot"></span>
Ready
</div>
<div class="item" id="statusResource">No resource loaded</div>
<div class="item" id="statusResolution">1280×720</div>
</div>
</div>
<!-- Simulation Panel -->
<div class="sim-panel">
<div class="sim-panel-header">
<h3>🧪 Flow Simulator</h3>
<p>Simulează logica server-side</p>
</div>
<div class="sim-section">
<h4>👤 Player State</h4>
<div class="sim-toggle">
<label>🪪 Permis de conducere</label>
<div class="toggle-switch">
<input type="checkbox" id="toggleLicense">
<span class="toggle-slider"></span>
</div>
</div>
<div class="sim-toggle">
<label>🆔 Buletin (ID Card)</label>
<div class="toggle-switch">
<input type="checkbox" id="toggleIdCard" checked>
<span class="toggle-slider"></span>
</div>
</div>
</div>
<div class="sim-section">
<h4>💼 Job Center — Apply</h4>
2026-03-30 02:20:03 +03:00
<button class="sim-job-btn" data-job="garbage" onclick="simApply('garbage')">🗑️ Garbage Collector <span
class="req">🚗 permis</span></button>
<button class="sim-job-btn" data-job="deliverer" onclick="simApply('deliverer')">📦 Deliverer <span
class="req">🚗 permis</span></button>
<button class="sim-job-btn" data-job="postman" onclick="simApply('postman')">✉️ Postman <span
class="req">🚗 permis</span></button>
<button class="sim-job-btn" data-job="lumberjack" onclick="simApply('lumberjack')">🪓 Lumberjack <span
class="req">🚗 permis</span></button>
<button class="sim-job-btn" data-job="bus" onclick="simApply('bus')">🚌 Bus Driver <span class="req">🚗
permis</span></button>
2026-03-29 21:41:17 +03:00
<button class="sim-job-btn" data-job="miner" onclick="simApply('miner')">⛏️ Miner</button>
2026-03-30 02:20:03 +03:00
<button class="sim-job-btn" data-job="electrician" onclick="simApply('electrician')">
Electrician</button>
2026-03-29 21:41:17 +03:00
</div>
<div class="sim-log" id="simLog">
<h4>📋 Server Log</h4>
</div>
</div>
</div>
<script>
// NUI Registry - all NUI pages on the server
const nuiResources = [
{ name: "⭐ Job Center (Simulator)", path: "__mock__/jobcenter", tag: "custom", icon: "💼", mock: "../docs/mock_jobcenter.html" },
{ name: "rv-license-dialog", path: "[framework]/[addons]/rv-license-dialog/html/index.html", tag: "custom", icon: "🔒" },
{ name: "17mov_JobCenter", path: "[framework]/[base]/[jobs]/17mov_JobCenter/web/index.html", tag: "vendor", icon: "💼" },
{ name: "17mov_CharacterSystem", path: "[framework]/[base]/[auth]/17mov_CharacterSystem/web/index.html", tag: "vendor", icon: "👤" },
{ name: "17mov_Hud", path: "[framework]/[base]/[ui]/17mov_Hud/web/index.html", tag: "vendor", icon: "📊" },
{ name: "17mov_Lumberjack", path: "[framework]/[base]/[jobs]/[citizen]/17mov_Lumberjack/web/index.html", tag: "vendor", icon: "🪓" },
{ name: "17mov_Miner", path: "[framework]/[base]/[jobs]/[citizen]/17mov_Miner/web/index.html", tag: "vendor", icon: "⛏️" },
{ name: "17mov_OilRig", path: "[framework]/[base]/[jobs]/[citizen]/17mov_OilRig/web/index.html", tag: "vendor", icon: "🛢️" },
{ name: "luxu_admin", path: "luxu_admin/web/index.html", tag: "vendor", icon: "⚙️" },
{ name: "ac-carcontrol", path: "[framework]/[addons]/ac-carcontrol/html/index.html", tag: "vendor", icon: "🚗" },
{ name: "ak4y-dice", path: "[framework]/[addons]/ak4y-dice/html/index.html", tag: "vendor", icon: "🎲" },
{ name: "bit-driverschool", path: "[framework]/[addons]/bit-driverschool/html/index.html", tag: "vendor", icon: "🏫" },
{ name: "kq_animsuggest", path: "[framework]/[addons]/kq_animsuggest/nui/index.html", tag: "vendor", icon: "💃" },
{ name: "kq_dyno", path: "[framework]/[addons]/kq_dyno/html/index.html", tag: "vendor", icon: "📈" },
{ name: "qb-input", path: "[framework]/[addons]/qb-input/html/index.html", tag: "core", icon: "📝" },
{ name: "qs-smartphone-pro", path: "[framework]/[addons]/qs-smartphone-pro/html/index.html", tag: "vendor", icon: "📱" },
{ name: "svdden_banking", path: "[framework]/[addons]/svdden_banking/html/index.html", tag: "vendor", icon: "🏦" },
{ name: "rcore_casino", path: "[framework]/[addons]/[casino]/rcore_casino/client/html/index.html", tag: "vendor", icon: "🎰" },
{ name: "qs-notify", path: "[framework]/[addons]/[notify]/qs-notify/html/index.html", tag: "vendor", icon: "🔔" },
{ name: "mBossmenu", path: "[framework]/[base]/[jobs]/mBossmenu/html/index.html", tag: "vendor", icon: "👔" },
{ name: "wasabi_ambulance", path: "[framework]/[base]/[jobs]/[legal]/[ambulance]/wasabi_ambulance/nui/index.html", tag: "vendor", icon: "🚑" },
{ name: "t1ger_mechanic", path: "[framework]/[base]/[jobs]/[legal]/[mechanic]/t1ger_mechanic/web/index.html", tag: "vendor", icon: "🔧" },
{ name: "codem-dispatch", path: "[framework]/[base]/[jobs]/[legal]/[police]/codem-dispatch/nui/index.html", tag: "vendor", icon: "📻" },
{ name: "t1ger_tuningsystem", path: "[framework]/[base]/[jobs]/[legal]/[tuner]/t1ger_tuningsystem/web/index.html", tag: "vendor", icon: "🏎️" },
{ name: "qb-core", path: "[framework]/[core]/qb-core/html/index.html", tag: "core", icon: "🧩" },
{ name: "qb-menu", path: "[framework]/[core]/qb-menu/html/index.html", tag: "core", icon: "📋" },
{ name: "qb-target", path: "[framework]/[core]/qb-target/html/index.html", tag: "core", icon: "🎯" },
{ name: "interact-sound", path: "[framework]/[depends]/interact-sound/client/html/index.html", tag: "core", icon: "🔊" },
{ name: "phone-radio", path: "[framework]/[depends]/phone-radio/html/index.html", tag: "core", icon: "📻" },
{ name: "progressbar", path: "[framework]/[depends]/progressbar/html/index.html", tag: "core", icon: "⏳" },
{ name: "xsound", path: "[framework]/[depends]/xsound/html/index.html", tag: "core", icon: "🎵" },
];
const BASE_PATH = "../resources/";
let activeResource = null;
// Render sidebar
function renderList(filter = "") {
const list = document.getElementById("nuiList");
2026-03-30 02:20:03 +03:00
const filtered = nuiResources.filter(r =>
2026-03-29 21:41:17 +03:00
r.name.toLowerCase().includes(filter.toLowerCase()) ||
r.path.toLowerCase().includes(filter.toLowerCase())
);
list.innerHTML = filtered.map(r => `
<div class="nui-item ${activeResource === r.name ? 'active' : ''}" data-name="${r.name}" data-path="${r.path}">
<div class="icon">${r.icon}</div>
<div class="info">
<div class="name">${r.name}</div>
<div class="path">${r.path}</div>
</div>
<span class="tag tag-${r.tag}">${r.tag}</span>
</div>
`).join("");
// Click handlers
list.querySelectorAll(".nui-item").forEach(item => {
item.addEventListener("click", () => loadNUI(item.dataset.name, item.dataset.path));
});
}
// Load NUI
function loadNUI(name, path) {
activeResource = name;
const frame = document.getElementById("previewFrame");
const empty = document.getElementById("emptyState");
2026-03-30 02:20:03 +03:00
2026-03-29 21:41:17 +03:00
if (empty) empty.remove();
// Remove existing iframe
const existingIframe = frame.querySelector("iframe");
if (existingIframe) existingIframe.remove();
// Check if this is a mock resource
const resource = nuiResources.find(r => r.name === name);
const src = resource && resource.mock ? resource.mock : BASE_PATH + path;
// Create new iframe
const iframe = document.createElement("iframe");
iframe.src = src;
iframe.id = "nuiIframe";
frame.appendChild(iframe);
// Update status
document.getElementById("statusResource").textContent = `📦 ${name}`;
renderList(document.getElementById("searchInput").value);
}
// Send NUI message to iframe
function sendMessage(data) {
const iframe = document.getElementById("nuiIframe");
if (iframe && iframe.contentWindow) {
iframe.contentWindow.postMessage(data, "*");
}
}
// Resize preview
function updateResolution() {
const [w, h] = document.getElementById("resolutionSelect").value.split("x").map(Number);
const frame = document.getElementById("previewFrame");
const container = document.querySelector(".preview-container");
2026-03-30 02:20:03 +03:00
2026-03-29 21:41:17 +03:00
frame.style.width = w + "px";
frame.style.height = h + "px";
// Scale to fit
const maxW = container.clientWidth - 40;
const maxH = container.clientHeight - 40;
const scale = Math.min(maxW / w, maxH / h, 1);
frame.style.transform = `scale(${scale})`;
document.getElementById("statusResolution").textContent = `${w}×${h} (${Math.round(scale * 100)}%)`;
}
// Event listeners
document.getElementById("searchInput").addEventListener("input", e => renderList(e.target.value));
2026-03-30 02:20:03 +03:00
2026-03-29 21:41:17 +03:00
document.getElementById("btnRefresh").addEventListener("click", () => {
const iframe = document.getElementById("nuiIframe");
if (iframe) iframe.src = iframe.src;
});
document.getElementById("btnSendShow").addEventListener("click", () => {
sendMessage({ action: "show" });
sendMessage({ type: "open" });
sendMessage({ show: true });
});
document.getElementById("btnSendHide").addEventListener("click", () => {
sendMessage({ action: "hide" });
sendMessage({ type: "close" });
sendMessage({ show: false });
});
document.getElementById("btnDevTools").addEventListener("click", () => {
if (activeResource) {
const r = nuiResources.find(n => n.name === activeResource);
if (r) window.open(BASE_PATH + r.path, "_blank");
}
});
document.getElementById("resolutionSelect").addEventListener("change", updateResolution);
window.addEventListener("resize", updateResolution);
// ========== FLOW SIMULATOR ==========
const licenseJobs = { garbage: true, deliverer: true, postman: true, lumberjack: true, bus: true };
function simApply(job) {
const hasLicense = document.getElementById('toggleLicense').checked;
const hasIdCard = document.getElementById('toggleIdCard').checked;
const needsLicense = licenseJobs[job] || false;
logSim('info', `Player apasă APPLY pe [${job}]`);
logSim('info', `→ Server: SetPlayerJob(src, "${job}", 0)`);
if (needsLicense) {
logSim('info', `→ Check: licenseJobs["${job}"] = true → verificare permis...`);
logSim('info', `→ metadata.licences.driver = ${hasLicense}`);
if (!hasLicense) {
logSim('error', `✖ BLOCAT: Nu are permis de conducere!`);
logSim('error', `→ TriggerClientEvent("QBCore:Notify", "error")`);
logSim('error', `→ TriggerClientEvent("rv:showLicenseDialog")`);
logSim('info', `→ return false (job NU a fost setat)`);
// Show the license dialog in preview
showLicenseDialog();
return;
}
}
logSim('success', `✔ metadata.licences.driver = true`);
logSim('success', `✔ player.Functions.SetJob("${job}", 0) → SUCCESS`);
logSim('success', `→ Player este acum: ${job}`);
}
function showLicenseDialog() {
// Load rv-license-dialog in preview if not already loaded
const iframe = document.getElementById('nuiIframe');
const licensePath = '[framework]/[addons]/rv-license-dialog/html/index.html';
if (!iframe || activeResource !== 'rv-license-dialog') {
// Load license dialog
loadNUI('rv-license-dialog', licensePath);
// Wait for iframe to load then send show
setTimeout(() => {
sendMessage({ action: 'show' });
}, 500);
} else {
sendMessage({ action: 'show' });
}
}
function logSim(type, msg) {
const log = document.getElementById('simLog');
const time = new Date().toLocaleTimeString('ro-RO', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
const entry = document.createElement('div');
entry.className = `log-entry log-${type}`;
entry.innerHTML = `<span class="time">[${time}]</span> ${msg}`;
log.appendChild(entry);
log.scrollTop = log.scrollHeight;
}
// Init
renderList();
setTimeout(updateResolution, 100);
</script>
</body>
2026-03-30 02:20:03 +03:00
</html>