const app = new Vue({ el: '#app', data: { show: false, showAdminPanel: false, isAdmin: false, garageId: null, garageType: 'normal', playerName: '', avatar: 'images/defaultimage.png', vehicles: [], activeTab: 'all', showFavorites: false, searchQuery: '', selectedVehicle: null, selectedIndex: null, impound: false, impoundPrice: 1500, adminGarages: [], editingGarage: null, deleteConfirmId: null, capturingField: null, windowWidth: window.innerWidth, posFields: [ { field: 'npc', label: 'NPC Position', hasHeading: true }, { field: 'spawn', label: 'Spawn Position (Fahrzeug erscheint)', hasHeading: true }, { field: 'park', label: 'Einpark-Zone (Marker am Boden)', hasHeading: false }, ], jobVehicles: [], // Fahrzeuge für die aktuelle Jobgarage newJobVehicle: '', // Eingabe: neues Modell }, computed: { filteredVehicles() { return this.vehicles.filter(v => { if (this.showFavorites && v.favorite != 1) return false; if (this.searchQuery) { const q = this.searchQuery.toLowerCase(); if (!v.carname.toLowerCase().includes(q) && !v.plate.toLowerCase().includes(q)) return false; } const cls = v.vehClass || 0; if (this.activeTab === 'cars') return ![8,13,14,15,16].includes(cls); if (this.activeTab === 'motor') return cls === 8; if (this.activeTab === 'bikes') return cls === 13; if (this.activeTab === 'boat') return cls === 14; if (this.activeTab === 'aircraft') return cls === 15 || cls === 16; return true; }); } }, methods: { close() { this.show = false; this.showAdminPanel = false; this.selectedVehicle = null; $.post(`https://${GetParentResourceName()}/close`, JSON.stringify({})); }, setTab(tab) { this.activeTab = tab; this.showFavorites = false; this.selectedVehicle = null; this.selectedIndex = null; }, toggleFavorites() { this.showFavorites = !this.showFavorites; this.selectedVehicle = null; this.selectedIndex = null; }, selectVehicle(v, index) { this.selectedVehicle = v; this.selectedIndex = index; $.post(`https://${GetParentResourceName()}/previewVehicle`, JSON.stringify({ modelname: v.modelname, props: v.props })); }, takeOut() { if (!this.selectedVehicle) return; $.post(`https://${GetParentResourceName()}/takeOut`, JSON.stringify({ plate: this.selectedVehicle.plate, vehClass: this.selectedVehicle.vehClass || 0, })); // close() aufrufen damit SetNuiFocus(false) auf Lua-Seite läuft this.close(); }, parkFromPanel() { if (!this.selectedVehicle || !this.selectedVehicle.nearby) return; $.post(`https://${GetParentResourceName()}/parkFromPanel`, JSON.stringify({ plate: this.selectedVehicle.plate })); }, parkJobVehicle() { if (!this.selectedVehicle || !this.selectedVehicle.nearby) return; $.post(`https://${GetParentResourceName()}/parkJobVehicle`, JSON.stringify({ plate: this.selectedVehicle.plate, nearbyJobPlate: this.selectedVehicle.nearbyJobPlate || null, })); }, toggleFav(plate, current) { const newVal = current == 1 ? 0 : 1; $.post(`https://${GetParentResourceName()}/setFavorite`, JSON.stringify({ plate, value: newVal })); const v = this.vehicles.find(x => x.plate === plate); if (v) { v.favorite = newVal; if (this.selectedVehicle && this.selectedVehicle.plate === plate) this.selectedVehicle.favorite = newVal; } }, // ADMIN openAdmin() { this.showAdminPanel = true; this.isAdmin = true; this.loadAdminGarages(); }, openAdminFromGarage() { // Via NUI Callback damit Lua die Kamera sauber aufräumt $.post(`https://${GetParentResourceName()}/openAdminFromGarage`, JSON.stringify({})); }, closeAdmin() { this.showAdminPanel = false; this.show = false; $.post(`https://${GetParentResourceName()}/closeAdmin`, JSON.stringify({})); }, loadAdminGarages() { $.post(`https://${GetParentResourceName()}/getAdminGarages`, JSON.stringify({}), (data) => { try { const d = typeof data === 'string' ? JSON.parse(data) : data; if (Array.isArray(d) && d.length > 0) { this.adminGarages = d.sort((a,b) => a.label.localeCompare(b.label)); } // Falls leer: Daten kommen via ADMIN_GARAGES message } catch(e) {} }); }, newGarage() { this.editingGarage = { _isNew: true, id:'', label:'', type:'normal', access:'none', gang:'none', npc_model:'a_m_m_prolhost_01', blip_show:1, blip_show_bool:true, blip_type:357, blip_colour:3, npc_x:0,npc_y:0,npc_z:0,npc_heading:0, spawn_x:0,spawn_y:0,spawn_z:0,spawn_heading:0, park_x:0,park_y:0,park_z:0,park_heading:0 }; this.jobVehicles = []; }, editGarage(g) { this.editingGarage = Object.assign({ _isNew: false, blip_show_bool: g.blip_show == 1 }, g); // Job-Fahrzeuge laden falls Jobgarage if (g.type === 'jobgarage') { this.jobVehicles = g.job_vehicles ? [...g.job_vehicles] : []; } else { this.jobVehicles = []; } }, confirmDelete(id) { this.deleteConfirmId = id; }, deleteGarage() { if (!this.deleteConfirmId) return; $.post(`https://${GetParentResourceName()}/adminDeleteGarage`, JSON.stringify({ id: this.deleteConfirmId })); this.adminGarages = this.adminGarages.filter(g => g.id !== this.deleteConfirmId); if (this.editingGarage && this.editingGarage.id === this.deleteConfirmId) this.editingGarage = null; this.deleteConfirmId = null; }, // Job-Fahrzeug hinzufügen addJobVehicle() { const model = this.newJobVehicle.trim().toLowerCase(); if (!model) return; if (!this.jobVehicles.find(v => v.model === model)) { this.jobVehicles.push({ model, label: model.toUpperCase() }); } this.newJobVehicle = ''; }, removeJobVehicle(index) { this.jobVehicles.splice(index, 1); }, saveGarage() { const g = this.editingGarage; if (!g) return; if (!g.id || !g.id.trim()) { return; } if (!g.label || !g.label.trim()) { return; } g.id = g.id.trim().replace(/\s+/g,'_'); const payload = Object.assign({}, g); delete payload._isNew; delete payload.blip_show_bool; payload.blip_show = g.blip_show_bool ? 1 : 0; if (g.type === 'jobgarage') { payload.job_vehicles = this.jobVehicles; } $.post(`https://${GetParentResourceName()}/adminSaveGarage`, JSON.stringify(payload)); const i = this.adminGarages.findIndex(x => x.id === payload.id); if (i >= 0) this.adminGarages.splice(i, 1, payload); else this.adminGarages.push(payload); this.adminGarages.sort((a,b) => a.label.localeCompare(b.label)); this.editingGarage._isNew = false; }, teleportTo(g) { $.post(`https://${GetParentResourceName()}/teleportToGarage`, JSON.stringify({ x:g.npc_x, y:g.npc_y, z:g.npc_z, heading:g.npc_heading })); }, capturePos(field) { this.capturingField = field; $.post(`https://${GetParentResourceName()}/startCapture`, JSON.stringify({ field })); }, getFieldLabel(field) { const l = { npc:'NPC Position', spawn:'Spawn Position', park:'Einpark-Zone' }; return l[field] || field; }, getTypeIcon(type) { const m = { normal:'fas fa-car', aircraft:'fas fa-plane', boat:'fas fa-ship', jobgarage:'fas fa-briefcase', impound:'fas fa-lock', impoundboat:'fas fa-ship', impoundplane:'fas fa-plane' }; return m[type] || 'fas fa-warehouse'; }, applyCapture(field, x, y, z, heading) { if (!this.editingGarage) return; this.editingGarage[field+'_x'] = x; this.editingGarage[field+'_y'] = y; this.editingGarage[field+'_z'] = z; if (heading !== undefined) this.editingGarage[field+'_heading'] = heading; }, } }); window.addEventListener('message', function(e) { const msg = e.data; if (!msg || !msg.action) return; switch(msg.action) { case 'OPEN': app.show = true; app.showAdminPanel = false; app.garageId = msg.garageId; app.garageType = msg.garageType || 'normal'; app.vehicles = msg.vehicles || []; app.playerName = msg.playerName || ''; app.impound = msg.impound || false; app.impoundPrice = msg.impoundPrice || 1500; app.selectedVehicle = null; app.selectedIndex = null; app.showFavorites = false; app.searchQuery = ''; if (msg.garageType === 'aircraft' || msg.garageType === 'impoundplane') app.activeTab = 'aircraft'; else if (msg.garageType === 'boat' || msg.garageType === 'impoundboat') app.activeTab = 'boat'; else app.activeTab = 'all'; break; case 'CLOSE': app.show = false; app.showAdminPanel = false; break; case 'OPEN_ADMIN': app.show = true; app.showAdminPanel = true; app.isAdmin = true; app.loadAdminGarages(); break; case 'SET_PROFILE': app.playerName = msg.name || ''; app.avatar = msg.avatar || 'images/defaultimage.png'; break; case 'SET_ADMIN': app.isAdmin = msg.isAdmin || false; break; case 'POSITION_CAPTURED': app.capturingField = null; if (app.editingGarage) app.applyCapture(msg.field, msg.x, msg.y, msg.z, msg.heading); break; case 'CAPTURE_CANCELLED': app.capturingField = null; break; case 'ADMIN_GARAGES': if (msg.garages) { app.adminGarages = msg.garages.sort((a,b) => a.label.localeCompare(b.label)); } break; case 'SYNC_GARAGES_DONE': if (app.showAdminPanel) app.loadAdminGarages(); break; } }); $(document).keydown(function(e) { if (e.keyCode === 27) { if (app.showAdminPanel) app.closeAdmin(); else app.close(); } });