Jacob Schmidt d178e39164 Refactor client UI stores and normalize docs formatting
- 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
2026-03-10 19:13:30 -05:00

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;