- Rework org and store UI state modules (rename/move store/getter files, add runtime and bridge wiring) - Update store UI components and page structure (navbar/cart split, new StoreView flow) - Apply broad markdown/YAML/HTML/CSS/JS formatting cleanup across docs, templates, and workflows
473 lines
12 KiB
JavaScript
473 lines
12 KiB
JavaScript
/**
|
|
* Vehicle Garage Interface
|
|
* Handles vehicle management with spawn and store actions
|
|
*/
|
|
|
|
// Mock data - sample vehicles
|
|
const mockData = {
|
|
vehicles: [
|
|
// Cars
|
|
{
|
|
id: 1,
|
|
name: "Sedan",
|
|
type: "car",
|
|
icon: "🚗",
|
|
status: "stored",
|
|
condition: 95,
|
|
fuel: 80,
|
|
location: "Garage A",
|
|
seats: 4,
|
|
speed: "180 km/h",
|
|
cargo: "200 kg",
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Sports Car",
|
|
type: "car",
|
|
icon: "🏎️",
|
|
status: "stored",
|
|
condition: 100,
|
|
fuel: 100,
|
|
location: "Garage A",
|
|
seats: 2,
|
|
speed: "250 km/h",
|
|
cargo: "50 kg",
|
|
},
|
|
{
|
|
id: 3,
|
|
name: "SUV",
|
|
type: "car",
|
|
icon: "🚙",
|
|
status: "active",
|
|
condition: 85,
|
|
fuel: 60,
|
|
location: "In Use",
|
|
seats: 6,
|
|
speed: "160 km/h",
|
|
cargo: "400 kg",
|
|
},
|
|
{
|
|
id: 4,
|
|
name: "Hatchback",
|
|
type: "car",
|
|
icon: "🚗",
|
|
status: "stored",
|
|
condition: 90,
|
|
fuel: 75,
|
|
location: "Garage B",
|
|
seats: 4,
|
|
speed: "170 km/h",
|
|
cargo: "250 kg",
|
|
},
|
|
|
|
// Trucks
|
|
{
|
|
id: 5,
|
|
name: "Pickup Truck",
|
|
type: "truck",
|
|
icon: "🚛",
|
|
status: "stored",
|
|
condition: 88,
|
|
fuel: 70,
|
|
location: "Garage A",
|
|
seats: 2,
|
|
speed: "140 km/h",
|
|
cargo: "800 kg",
|
|
},
|
|
{
|
|
id: 6,
|
|
name: "Delivery Van",
|
|
type: "truck",
|
|
icon: "🚚",
|
|
status: "stored",
|
|
condition: 92,
|
|
fuel: 85,
|
|
location: "Garage B",
|
|
seats: 3,
|
|
speed: "130 km/h",
|
|
cargo: "1200 kg",
|
|
},
|
|
{
|
|
id: 7,
|
|
name: "Heavy Truck",
|
|
type: "truck",
|
|
icon: "🚛",
|
|
status: "active",
|
|
condition: 75,
|
|
fuel: 50,
|
|
location: "In Use",
|
|
seats: 2,
|
|
speed: "120 km/h",
|
|
cargo: "2000 kg",
|
|
},
|
|
{
|
|
id: 8,
|
|
name: "Box Truck",
|
|
type: "truck",
|
|
icon: "📦",
|
|
status: "stored",
|
|
condition: 80,
|
|
fuel: 65,
|
|
location: "Garage A",
|
|
seats: 3,
|
|
speed: "110 km/h",
|
|
cargo: "1500 kg",
|
|
},
|
|
|
|
// Aircraft
|
|
{
|
|
id: 9,
|
|
name: "Helicopter",
|
|
type: "air",
|
|
icon: "🚁",
|
|
status: "stored",
|
|
condition: 95,
|
|
fuel: 90,
|
|
location: "Helipad",
|
|
seats: 6,
|
|
speed: "280 km/h",
|
|
cargo: "500 kg",
|
|
},
|
|
{
|
|
id: 10,
|
|
name: "Light Plane",
|
|
type: "air",
|
|
icon: "✈️",
|
|
status: "stored",
|
|
condition: 100,
|
|
fuel: 100,
|
|
location: "Hangar",
|
|
seats: 4,
|
|
speed: "320 km/h",
|
|
cargo: "300 kg",
|
|
},
|
|
|
|
// Boats
|
|
{
|
|
id: 11,
|
|
name: "Speedboat",
|
|
type: "sea",
|
|
icon: "🚤",
|
|
status: "stored",
|
|
condition: 93,
|
|
fuel: 80,
|
|
location: "Marina",
|
|
seats: 4,
|
|
speed: "100 km/h",
|
|
cargo: "150 kg",
|
|
},
|
|
{
|
|
id: 12,
|
|
name: "Yacht",
|
|
type: "sea",
|
|
icon: "🛥️",
|
|
status: "stored",
|
|
condition: 98,
|
|
fuel: 95,
|
|
location: "Marina",
|
|
seats: 12,
|
|
speed: "60 km/h",
|
|
cargo: "800 kg",
|
|
},
|
|
],
|
|
};
|
|
|
|
// State
|
|
let selectedVehicle = null;
|
|
let statusFilter = "all";
|
|
let typeFilter = "all";
|
|
let searchQuery = "";
|
|
|
|
// Icons by type
|
|
const typeIcons = {
|
|
car: "🚗",
|
|
truck: "🚛",
|
|
air: "🚁",
|
|
sea: "🚤",
|
|
};
|
|
|
|
// Initialize
|
|
function initGarage() {
|
|
console.log("Garage interface initializing...");
|
|
|
|
setupEventHandlers();
|
|
renderVehicles();
|
|
updateStats();
|
|
|
|
console.log("Garage interface initialized");
|
|
}
|
|
|
|
// Event Handlers
|
|
function setupEventHandlers() {
|
|
// Close button
|
|
const closeBtn = document.querySelector(".close-btn");
|
|
if (closeBtn) {
|
|
closeBtn.addEventListener("click", () => {
|
|
console.log("Closing garage...");
|
|
sendEvent("garage::close", {});
|
|
});
|
|
}
|
|
|
|
// Status filters
|
|
const filterBtns = document.querySelectorAll(".filter-btn");
|
|
filterBtns.forEach((btn) => {
|
|
btn.addEventListener("click", () => {
|
|
filterBtns.forEach((b) => b.classList.remove("active"));
|
|
btn.classList.add("active");
|
|
statusFilter = btn.dataset.filter;
|
|
renderVehicles();
|
|
});
|
|
});
|
|
|
|
// Type filters
|
|
const typeItems = document.querySelectorAll(".type-item");
|
|
typeItems.forEach((item) => {
|
|
item.addEventListener("click", () => {
|
|
typeItems.forEach((i) => i.classList.remove("active"));
|
|
item.classList.add("active");
|
|
typeFilter = item.dataset.type;
|
|
renderVehicles();
|
|
});
|
|
});
|
|
|
|
// Search
|
|
const searchInput = document.getElementById("searchInput");
|
|
if (searchInput) {
|
|
searchInput.addEventListener("input", (e) => {
|
|
searchQuery = e.target.value.toLowerCase();
|
|
renderVehicles();
|
|
});
|
|
}
|
|
|
|
// Spawn button
|
|
const spawnBtn = document.getElementById("spawnBtn");
|
|
if (spawnBtn) {
|
|
spawnBtn.addEventListener("click", () => {
|
|
if (selectedVehicle) {
|
|
spawnVehicle(selectedVehicle);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Store button
|
|
const storeBtn = document.getElementById("storeBtn");
|
|
if (storeBtn) {
|
|
storeBtn.addEventListener("click", () => {
|
|
if (selectedVehicle) {
|
|
storeVehicle(selectedVehicle);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Render vehicles
|
|
function renderVehicles() {
|
|
const vehiclesGrid = document.getElementById("vehiclesGrid");
|
|
if (!vehiclesGrid) return;
|
|
|
|
vehiclesGrid.innerHTML = "";
|
|
|
|
// Filter vehicles
|
|
let filtered = mockData.vehicles;
|
|
|
|
// Status filter
|
|
if (statusFilter !== "all") {
|
|
filtered = filtered.filter((v) => v.status === statusFilter);
|
|
}
|
|
|
|
// Type filter
|
|
if (typeFilter !== "all") {
|
|
filtered = filtered.filter((v) => v.type === typeFilter);
|
|
}
|
|
|
|
// Search filter
|
|
if (searchQuery) {
|
|
filtered = filtered.filter(
|
|
(v) =>
|
|
v.name.toLowerCase().includes(searchQuery) ||
|
|
v.type.toLowerCase().includes(searchQuery),
|
|
);
|
|
}
|
|
|
|
// Render vehicles
|
|
filtered.forEach((vehicle) => {
|
|
const card = document.createElement("div");
|
|
card.className = "vehicle-card";
|
|
if (selectedVehicle && selectedVehicle.id === vehicle.id) {
|
|
card.classList.add("selected");
|
|
}
|
|
|
|
card.innerHTML = `
|
|
<div class="vehicle-icon">${vehicle.icon}</div>
|
|
<div class="vehicle-name">${vehicle.name}</div>
|
|
<div class="vehicle-type">${vehicle.type}</div>
|
|
<div class="vehicle-status ${vehicle.status}">${vehicle.status}</div>
|
|
`;
|
|
|
|
card.addEventListener("click", () => selectVehicle(vehicle));
|
|
vehiclesGrid.appendChild(card);
|
|
});
|
|
|
|
console.log(`Rendered ${filtered.length} vehicles`);
|
|
}
|
|
|
|
// Select vehicle
|
|
function selectVehicle(vehicle) {
|
|
selectedVehicle = vehicle;
|
|
|
|
// Update selected state in grid
|
|
document.querySelectorAll(".vehicle-card").forEach((card) => {
|
|
card.classList.remove("selected");
|
|
});
|
|
event.currentTarget.classList.add("selected");
|
|
|
|
// Show details
|
|
showVehicleDetails(vehicle);
|
|
}
|
|
|
|
// Show vehicle details
|
|
function showVehicleDetails(vehicle) {
|
|
const noSelection = document.getElementById("noSelection");
|
|
const vehicleDetails = document.getElementById("vehicleDetails");
|
|
const spawnBtn = document.getElementById("spawnBtn");
|
|
const storeBtn = document.getElementById("storeBtn");
|
|
|
|
if (noSelection) noSelection.style.display = "none";
|
|
if (vehicleDetails) vehicleDetails.style.display = "flex";
|
|
|
|
// Update details
|
|
document.getElementById("detailIcon").textContent = vehicle.icon;
|
|
document.getElementById("detailName").textContent = vehicle.name;
|
|
document.getElementById("detailType").textContent = vehicle.type;
|
|
document.getElementById("detailStatus").textContent = vehicle.status;
|
|
document.getElementById("detailCondition").textContent =
|
|
`${vehicle.condition}%`;
|
|
document.getElementById("detailFuel").textContent = `${vehicle.fuel}%`;
|
|
document.getElementById("detailLocation").textContent = vehicle.location;
|
|
document.getElementById("detailSeats").textContent = vehicle.seats;
|
|
document.getElementById("detailSpeed").textContent = vehicle.speed;
|
|
document.getElementById("detailCargo").textContent = vehicle.cargo;
|
|
|
|
// Show/hide action buttons based on status
|
|
if (vehicle.status === "stored") {
|
|
spawnBtn.style.display = "flex";
|
|
storeBtn.style.display = "none";
|
|
} else {
|
|
spawnBtn.style.display = "none";
|
|
storeBtn.style.display = "flex";
|
|
}
|
|
}
|
|
|
|
// Spawn vehicle
|
|
function spawnVehicle(vehicle) {
|
|
console.log("Spawning vehicle:", vehicle.name);
|
|
|
|
// Update local state
|
|
vehicle.status = "active";
|
|
vehicle.location = "In Use";
|
|
|
|
sendEvent("garage::spawn", {
|
|
vehicleId: vehicle.id,
|
|
vehicleName: vehicle.name,
|
|
vehicleType: vehicle.type,
|
|
});
|
|
|
|
// Re-render
|
|
renderVehicles();
|
|
updateStats();
|
|
if (selectedVehicle && selectedVehicle.id === vehicle.id) {
|
|
showVehicleDetails(vehicle);
|
|
}
|
|
}
|
|
|
|
// Store vehicle
|
|
function storeVehicle(vehicle) {
|
|
console.log("Storing vehicle:", vehicle.name);
|
|
|
|
// Update local state
|
|
vehicle.status = "stored";
|
|
vehicle.location = "Garage A";
|
|
|
|
sendEvent("garage::store", {
|
|
vehicleId: vehicle.id,
|
|
vehicleName: vehicle.name,
|
|
vehicleType: vehicle.type,
|
|
});
|
|
|
|
// Re-render
|
|
renderVehicles();
|
|
updateStats();
|
|
if (selectedVehicle && selectedVehicle.id === vehicle.id) {
|
|
showVehicleDetails(vehicle);
|
|
}
|
|
}
|
|
|
|
// Update stats
|
|
function updateStats() {
|
|
const stored = mockData.vehicles.filter(
|
|
(v) => v.status === "stored",
|
|
).length;
|
|
const active = mockData.vehicles.filter(
|
|
(v) => v.status === "active",
|
|
).length;
|
|
const capacity = mockData.vehicles.length + 6; // Mock capacity
|
|
|
|
document.getElementById("storedCount").textContent = stored;
|
|
document.getElementById("activeCount").textContent = active;
|
|
document.getElementById("capacityCount").textContent = capacity;
|
|
}
|
|
|
|
// Update garage data from external source
|
|
function updateGarageData(data) {
|
|
if (data.vehicles) {
|
|
mockData.vehicles = data.vehicles;
|
|
renderVehicles();
|
|
updateStats();
|
|
|
|
// Update selected vehicle if it still exists
|
|
if (selectedVehicle) {
|
|
const updated = mockData.vehicles.find(
|
|
(v) => v.id === selectedVehicle.id,
|
|
);
|
|
if (updated) {
|
|
selectedVehicle = updated;
|
|
showVehicleDetails(updated);
|
|
} else {
|
|
selectedVehicle = null;
|
|
const noSelection = document.getElementById("noSelection");
|
|
const vehicleDetails =
|
|
document.getElementById("vehicleDetails");
|
|
if (noSelection) noSelection.style.display = "flex";
|
|
if (vehicleDetails) vehicleDetails.style.display = "none";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Send event to Arma
|
|
function sendEvent(event, data) {
|
|
if (typeof A3API !== "undefined") {
|
|
A3API.SendAlert(
|
|
JSON.stringify({
|
|
event: event,
|
|
data: data,
|
|
}),
|
|
);
|
|
} else {
|
|
console.log("Event:", event, "Data:", data);
|
|
}
|
|
}
|
|
|
|
// Auto-initialize
|
|
if (document.readyState === "loading") {
|
|
document.addEventListener("DOMContentLoaded", initGarage);
|
|
} else {
|
|
initGarage();
|
|
}
|
|
|
|
// Expose functions globally
|
|
window.updateGarageData = updateGarageData;
|
|
window.selectVehicle = selectVehicle;
|
|
window.spawnVehicle = spawnVehicle;
|
|
window.storeVehicle = storeVehicle;
|