From 9a111be10dd42b1645efee5fe55742c794863108 Mon Sep 17 00:00:00 2001 From: aryan Date: Sat, 14 Feb 2026 20:38:23 +0530 Subject: [PATCH 1/2] Add URL param preselect support Adds support for pre-selecting vehicle, version, and board from URL parameters. Example: ?vehicle=copter&version=stable&board=pixhawk - Frontend-only change - No backend changes - Safe fallback if parameters are invalid - Does not affect existing behavior This allows external links to directly open pre-configured build selections. --- web/static/js/add_build.js | 63 ++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/web/static/js/add_build.js b/web/static/js/add_build.js index b65241d..837e4c3 100644 --- a/web/static/js/add_build.js +++ b/web/static/js/add_build.js @@ -302,17 +302,32 @@ var rebuildConfig = { versionId: null, boardId: null, selectedFeatures: [], - isRebuildMode: false + isRebuildMode: false, + fromUrlParams: false }; async function init() { if (typeof rebuildFromBuildId !== 'undefined') { await initRebuild(rebuildFromBuildId); + } else { + initFromUrlParams(); } fetchVehicles(); } +function initFromUrlParams() { + const params = new URLSearchParams(window.location.search); + const hasParams = params.has('vehicle') || params.has('board') || params.has('version'); + + if (params.has('vehicle')) rebuildConfig.vehicleId = params.get('vehicle'); + if (params.has('board')) rebuildConfig.boardId = params.get('board'); + if (params.has('version')) rebuildConfig.versionId = params.get('version'); + + // Set flag to indicate URL parameters were provided + rebuildConfig.fromUrlParams = hasParams; +} + async function initRebuild(buildId) { try { const buildResponse = await fetch(`/api/v1/builds/${buildId}`); @@ -361,6 +376,7 @@ function clearRebuildConfig() { rebuildConfig.boardId = null; rebuildConfig.selectedFeatures = []; rebuildConfig.isRebuildMode = false; + rebuildConfig.fromUrlParams = false; } // enables or disables the elements with ids passed as an array @@ -400,10 +416,17 @@ function fetchVehicles() { if (rebuildConfig.vehicleId) { const vehicleExists = all_vehicles.some(v => v.id === rebuildConfig.vehicleId); if (!vehicleExists) { - console.warn(`Rebuild vehicle '${rebuildConfig.vehicleId}' not found in available vehicles`); - alert(`Warning: The vehicle from the original build is no longer available.\n\nRedirecting to new build page...`); - window.location.href = '/add_build'; - return; + if (rebuildConfig.isRebuildMode) { + console.warn(`Rebuild vehicle '${rebuildConfig.vehicleId}' not found in available vehicles`); + alert(`Warning: The vehicle from the original build is no longer available.\n\nRedirecting to new build page...`); + window.location.href = '/add_build'; + return; + } else if (rebuildConfig.fromUrlParams) { + console.warn(`URL parameter vehicle '${rebuildConfig.vehicleId}' not found. Defaulting to first available vehicle.`); + rebuildConfig.vehicleId = null; + } else { + rebuildConfig.vehicleId = null; + } } } @@ -442,10 +465,17 @@ function onVehicleChange(new_vehicle_id) { if (rebuildConfig.versionId) { const versionExists = all_versions.some(v => v.id === rebuildConfig.versionId); if (!versionExists) { - console.warn(`Rebuild version '${rebuildConfig.versionId}' not found for vehicle '${new_vehicle_id}'`); - alert(`Warning: The version from the original build is no longer available.\n\nRedirecting to new build page...`); - window.location.href = '/add_build'; - return; + if (rebuildConfig.isRebuildMode) { + console.warn(`Rebuild version '${rebuildConfig.versionId}' not found for vehicle '${new_vehicle_id}'`); + alert(`Warning: The version from the original build is no longer available.\n\nRedirecting to new build page...`); + window.location.href = '/add_build'; + return; + } else if (rebuildConfig.fromUrlParams) { + console.warn(`URL parameter version '${rebuildConfig.versionId}' not found for vehicle '${new_vehicle_id}'. Defaulting to first available version.`); + rebuildConfig.versionId = null; + } else { + rebuildConfig.versionId = null; + } } } @@ -498,10 +528,17 @@ function onVersionChange(new_version) { if (rebuildConfig.boardId) { const boardExists = boards.some(b => b.id === rebuildConfig.boardId); if (!boardExists) { - console.warn(`Rebuild board '${rebuildConfig.boardId}' not found for version '${version_id}'`); - alert(`Warning: The board from the original build is no longer available.\n\nRedirecting to new build page...`); - window.location.href = '/add_build'; - return; + if (rebuildConfig.isRebuildMode) { + console.warn(`Rebuild board '${rebuildConfig.boardId}' not found for version '${version_id}'`); + alert(`Warning: The board from the original build is no longer available.\n\nRedirecting to new build page...`); + window.location.href = '/add_build'; + return; + } else if (rebuildConfig.fromUrlParams) { + console.warn(`URL parameter board '${rebuildConfig.boardId}' not found for version '${version_id}'. Defaulting to first available board.`); + rebuildConfig.boardId = null; + } else { + rebuildConfig.boardId = null; + } } } From 9081dc1631e62281d45071dc6767cd93b0455d4e Mon Sep 17 00:00:00 2001 From: aryan Date: Tue, 17 Feb 2026 17:34:10 +0530 Subject: [PATCH 2/2] Refactor preselect logic per review: merge init flows and unify error handling --- web/static/js/add_build.js | 158 ++++++++++++++++--------------------- 1 file changed, 69 insertions(+), 89 deletions(-) diff --git a/web/static/js/add_build.js b/web/static/js/add_build.js index 837e4c3..c3deb16 100644 --- a/web/static/js/add_build.js +++ b/web/static/js/add_build.js @@ -202,6 +202,8 @@ const Features = (() => { function updateGlobalCheckboxState() { const total_options = Object.keys(features_by_id).length; let global_checkbox = document.getElementById("check-uncheck-all"); + if (!global_checkbox) return; + let indeterminate_state = false; switch (selected_options) { @@ -302,64 +304,58 @@ var rebuildConfig = { versionId: null, boardId: null, selectedFeatures: [], - isRebuildMode: false, - fromUrlParams: false + isRebuildMode: false }; async function init() { - if (typeof rebuildFromBuildId !== 'undefined') { - await initRebuild(rebuildFromBuildId); - } else { - initFromUrlParams(); - } - - fetchVehicles(); + await initPreSelect(); + fetchVehicles(); } -function initFromUrlParams() { - const params = new URLSearchParams(window.location.search); - const hasParams = params.has('vehicle') || params.has('board') || params.has('version'); - - if (params.has('vehicle')) rebuildConfig.vehicleId = params.get('vehicle'); - if (params.has('board')) rebuildConfig.boardId = params.get('board'); - if (params.has('version')) rebuildConfig.versionId = params.get('version'); - - // Set flag to indicate URL parameters were provided - rebuildConfig.fromUrlParams = hasParams; -} -async function initRebuild(buildId) { +async function initPreSelect() { + const params = new URLSearchParams(window.location.search); + + // Rebuild flow --- + if (typeof rebuildFromBuildId !== "undefined") { try { - const buildResponse = await fetch(`/api/v1/builds/${buildId}`); - if (!buildResponse.ok) { - throw new Error('Failed to fetch build details'); - } - const buildData = await buildResponse.json(); - - if (!buildData.vehicle || !buildData.vehicle.id) { - throw new Error('Vehicle information is missing from the build'); - } - if (!buildData.version || !buildData.version.id) { - throw new Error('Version information is missing from the build'); - } - if (!buildData.board || !buildData.board.id) { - throw new Error('Board information is missing from the build'); - } - - rebuildConfig.vehicleId = buildData.vehicle.id; - rebuildConfig.versionId = buildData.version.id; - rebuildConfig.boardId = buildData.board.id; - rebuildConfig.selectedFeatures = buildData.selected_features || []; - rebuildConfig.isRebuildMode = true; - - } catch (error) { - console.error('Error loading rebuild configuration:', error); - alert('Failed to load build configuration: ' + error.message + '\n\nRedirecting to new build page...'); - window.location.href = '/add_build'; - throw error; - } + const res = await fetch(`/api/v1/builds/${rebuildFromBuildId}`); + if (!res.ok) throw new Error("Failed to fetch build"); + + const data = await res.json(); + + rebuildConfig.vehicleId = data.vehicle?.id || null; + rebuildConfig.versionId = data.version?.id || null; + rebuildConfig.boardId = data.board?.id || null; + rebuildConfig.selectedFeatures = data.selected_features || []; + rebuildConfig.isRebuildMode = true; + } catch (err) { + console.error("Error loading rebuild config:", err); + alert( + "Failed to load build configuration.\n\nRedirecting to new build page..." + ); + window.location.href = "/add_build"; + throw err; + } + + return; + } + + // ===== URL PARAM FLOW ===== + if (params.has("vehicle_id")) { + rebuildConfig.vehicleId = params.get("vehicle_id"); + } + + if (params.has("version_id")) { + rebuildConfig.versionId = params.get("version_id"); + } + + if (params.has("board_id")) { + rebuildConfig.boardId = params.get("board_id"); + } } + function applyRebuildFeatures(featuresList) { Features.checkUncheckAll(false); @@ -376,7 +372,7 @@ function clearRebuildConfig() { rebuildConfig.boardId = null; rebuildConfig.selectedFeatures = []; rebuildConfig.isRebuildMode = false; - rebuildConfig.fromUrlParams = false; + } // enables or disables the elements with ids passed as an array @@ -416,18 +412,13 @@ function fetchVehicles() { if (rebuildConfig.vehicleId) { const vehicleExists = all_vehicles.some(v => v.id === rebuildConfig.vehicleId); if (!vehicleExists) { - if (rebuildConfig.isRebuildMode) { - console.warn(`Rebuild vehicle '${rebuildConfig.vehicleId}' not found in available vehicles`); - alert(`Warning: The vehicle from the original build is no longer available.\n\nRedirecting to new build page...`); + const msg = `Vehicle '${rebuildConfig.vehicleId}' is no longer listed for building. Redirecting to new build page...`; + console.warn(msg); + alert(msg); window.location.href = '/add_build'; return; - } else if (rebuildConfig.fromUrlParams) { - console.warn(`URL parameter vehicle '${rebuildConfig.vehicleId}' not found. Defaulting to first available vehicle.`); - rebuildConfig.vehicleId = null; - } else { - rebuildConfig.vehicleId = null; } - } + } let new_vehicle = rebuildConfig.vehicleId || @@ -464,19 +455,14 @@ function onVehicleChange(new_vehicle_id) { if (rebuildConfig.versionId) { const versionExists = all_versions.some(v => v.id === rebuildConfig.versionId); - if (!versionExists) { - if (rebuildConfig.isRebuildMode) { - console.warn(`Rebuild version '${rebuildConfig.versionId}' not found for vehicle '${new_vehicle_id}'`); - alert(`Warning: The version from the original build is no longer available.\n\nRedirecting to new build page...`); - window.location.href = '/add_build'; - return; - } else if (rebuildConfig.fromUrlParams) { - console.warn(`URL parameter version '${rebuildConfig.versionId}' not found for vehicle '${new_vehicle_id}'. Defaulting to first available version.`); - rebuildConfig.versionId = null; - } else { - rebuildConfig.versionId = null; - } - } + if (!versionExists) { + const msg = `Version '${rebuildConfig.versionId}' is no longer listed for building. Redirecting to new build page...`; + console.warn(msg); + alert(msg); + window.location.href = '/add_build'; + return; + } + } const new_version = rebuildConfig.versionId || all_versions[0].id; @@ -528,20 +514,14 @@ function onVersionChange(new_version) { if (rebuildConfig.boardId) { const boardExists = boards.some(b => b.id === rebuildConfig.boardId); if (!boardExists) { - if (rebuildConfig.isRebuildMode) { - console.warn(`Rebuild board '${rebuildConfig.boardId}' not found for version '${version_id}'`); - alert(`Warning: The board from the original build is no longer available.\n\nRedirecting to new build page...`); - window.location.href = '/add_build'; - return; - } else if (rebuildConfig.fromUrlParams) { - console.warn(`URL parameter board '${rebuildConfig.boardId}' not found for version '${version_id}'. Defaulting to first available board.`); - rebuildConfig.boardId = null; - } else { - rebuildConfig.boardId = null; - } - } + const msg = `Board '${rebuildConfig.boardId}' is no longer listed for building. Redirecting to new build page...`; + console.warn(msg); + alert(msg); + window.location.href = '/add_build'; + return; + } + } - let new_board = rebuildConfig.boardId || (boards.length > 0 ? boards[0].id : null); updateBoards(boards, new_board); }) @@ -620,7 +600,7 @@ var toggle_all_categories = (() => { for (let i=0; i { })(); function createCategoryCard(category_name, features_in_category, expanded) { - options_html = ""; + let options_html = ""; features_in_category.forEach(feature => { options_html += '
' + '' + @@ -745,9 +725,9 @@ function fillVehicles(vehicles, vehicle_id_to_select) { var output = document.getElementById('vehicle_list'); output.innerHTML = '' + ''; - vehicleList = document.getElementById("vehicle"); + let vehicleList = document.getElementById("vehicle"); vehicles.forEach(vehicle => { - opt = document.createElement('option'); + let opt = document.createElement('option'); opt.value = vehicle.id; opt.innerHTML = vehicle.name; opt.selected = (vehicle.id === vehicle_id_to_select);