diff --git a/Documents/Vibe Coding Projects/dedicatednodes-redesign/dedicatednodes-bare-metal/new-baremetal.html b/Documents/Vibe Coding Projects/dedicatednodes-redesign/dedicatednodes-bare-metal/new-baremetal.html index b209838..9c36fb2 100644 --- a/Documents/Vibe Coding Projects/dedicatednodes-redesign/dedicatednodes-bare-metal/new-baremetal.html +++ b/Documents/Vibe Coding Projects/dedicatednodes-redesign/dedicatednodes-bare-metal/new-baremetal.html @@ -4251,7 +4251,8 @@ var wpcf7 = { } // Render options with the exact WHMCS preselected values - renderDynamicOptions(options, true, preselected); + // Also pass the storage requirements for proper pre-filling + renderDynamicOptions(options, true, preselected, storageReqs); } else { renderFallbackOptions(container); @@ -4606,7 +4607,7 @@ var wpcf7 = { updateSummary(); }; - function renderDynamicOptions(options, isInstant = false, preselected = {}) { + function renderDynamicOptions(options, isInstant = false, preselected = {}, storageRequirements = []) { const container = isInstant ? document.getElementById('instantDynamicConfigContainer') : document.getElementById('dynamicConfigContainer'); container.innerHTML = ''; @@ -4643,107 +4644,71 @@ var wpcf7 = { } } - // For instant customization, don't group - show each slot individually - // For custom servers, group similar slots together - if (isInstant) { - // Show each storage slot individually - groups.storage.forEach((opt, index) => { - const label = document.createElement('div'); - label.className = 'section-label'; - label.style.marginTop = '1.5rem'; - label.textContent = `💿 ${opt.label}`; - container.appendChild(label); + // For both instant and custom, group storage options by signature + // This creates a pooled grid where we can prefill with the correct drives + const storageGroups = {}; + const groupOrder = []; // Preserve order - // Create visualizer for this single slot - const visualContainer = document.createElement('div'); - visualContainer.className = 'storage-slots-container'; - const uniqueSuffix = '-instant-s' + index; + groups.storage.forEach(opt => { + // Create a signature based on available values + const signature = opt.values.map(v => v.text.trim().toLowerCase() + '|' + v.price).join('||'); - visualContainer.innerHTML = ` -
- Slot: ${opt.label} - Configured -
-
-
-
- `; - container.appendChild(visualContainer); + if (!storageGroups[signature]) { + storageGroups[signature] = []; + groupOrder.push(signature); + } + storageGroups[signature].push(opt); + }); - // Create individual option grid for this slot - // For instant customization, we need to pass the specific preselected value for this slot - const slotPreselected = {}; - if (isInstant && preselected[opt.id]) { - slotPreselected[opt.id] = preselected[opt.id]; - } - container.appendChild(createOptionGrid(opt, true, -1, isInstant, slotPreselected)); - }); - } else { - // Group storage options by signature for custom servers - const storageGroups = {}; - const groupOrder = []; // Preserve order + // Render each group + groupOrder.forEach((sig, groupIndex) => { + const groupOpts = storageGroups[sig]; + const firstOpt = groupOpts[0]; - groups.storage.forEach(opt => { - // Create a signature based on available values - const signature = opt.values.map(v => v.text.trim().toLowerCase() + '|' + v.price).join('||'); + const label = document.createElement('div'); + label.className = 'section-label'; + label.style.marginTop = '1.5rem'; - if (!storageGroups[signature]) { - storageGroups[signature] = []; - groupOrder.push(signature); - } - storageGroups[signature].push(opt); - }); + // Detect Title based on content + const firstLabel = firstOpt.label.toLowerCase(); + // Check values for keywords + const hasGen5 = firstOpt.values.some(v => v.text.toLowerCase().includes('gen5')); + const hasGen4 = firstOpt.values.some(v => v.text.toLowerCase().includes('gen4')); + const hasEnt = firstOpt.values.some(v => v.text.toLowerCase().includes('enterprise') || v.text.toLowerCase().includes('ent')); - // Render each group - groupOrder.forEach((sig, groupIndex) => { - const groupOpts = storageGroups[sig]; - const firstOpt = groupOpts[0]; + let title = '💿 STORAGE CONFIGURATION'; + if (hasGen5) title = '🚀 GEN5 NVME STORAGE (ENTERPRISE)'; + else if (hasGen4) title = '💿 GEN4 NVME STORAGE (CONSUMER)'; + else if (hasEnt) title = '💾 ENTERPRISE STORAGE'; - const label = document.createElement('div'); - label.className = 'section-label'; - label.style.marginTop = '1.5rem'; + // Append Group Index if multiple groups exist to differentiate + if (groupOrder.length > 1) { + title += ` (Group ${groupIndex + 1})`; + } - // Detect Title based on content - const firstLabel = firstOpt.label.toLowerCase(); - // Check values for keywords - const hasGen5 = firstOpt.values.some(v => v.text.toLowerCase().includes('gen5')); - const hasGen4 = firstOpt.values.some(v => v.text.toLowerCase().includes('gen4')); - const hasEnt = firstOpt.values.some(v => v.text.toLowerCase().includes('enterprise') || v.text.toLowerCase().includes('ent')); + label.textContent = title; + container.appendChild(label); - let title = '💿 STORAGE CONFIGURATION'; - if (hasGen5) title = '🚀 GEN5 NVME STORAGE (ENTERPRISE)'; - else if (hasGen4) title = '💿 GEN4 NVME STORAGE (CONSUMER)'; - else if (hasEnt) title = '💾 ENTERPRISE STORAGE'; + // Visualizer for THIS group + const maxSlots = groupOpts.length; + const visualContainer = document.createElement('div'); + visualContainer.className = 'storage-slots-container'; + const uniqueSuffix = (isInstant ? '-instant' : '') + '-g' + groupIndex; - // Append Group Index if multiple groups exist to differentiate - if (groupOrder.length > 1) { - title += ` (Group ${groupIndex + 1})`; - } + visualContainer.innerHTML = ` +
+ Available Slots: 0/${maxSlots} + Select drives +
+
+ ${Array(maxSlots).fill('
').join('')} +
+ `; + container.appendChild(visualContainer); - label.textContent = title; - container.appendChild(label); - - // Visualizer for THIS group - const maxSlots = groupOpts.length; - const visualContainer = document.createElement('div'); - visualContainer.className = 'storage-slots-container'; - const uniqueSuffix = (isInstant ? '-instant' : '') + '-g' + groupIndex; - - visualContainer.innerHTML = ` -
- Available Slots: 0/${maxSlots} - Select drives -
-
- ${Array(maxSlots).fill('
').join('')} -
- `; - container.appendChild(visualContainer); - - // Always use pooled grid for these groups as they are by definition identical - container.appendChild(createPooledStorageGrid(groupOpts, isInstant, preselected, groupIndex)); - }); - } + // Always use pooled grid for these groups as they are by definition identical + container.appendChild(createPooledStorageGrid(groupOpts, isInstant, preselected, groupIndex, storageRequirements)); + }); } // 3. Network & Other @@ -4769,7 +4734,7 @@ var wpcf7 = { } // New Pooled Storage Grid - function createPooledStorageGrid(storageOptions, isInstant, preselected = {}, groupIndex = 0) { + function createPooledStorageGrid(storageOptions, isInstant, preselected = {}, groupIndex = 0, storageRequirements = []) { const grid = document.createElement('div'); grid.className = 'config-grid'; @@ -4800,29 +4765,72 @@ var wpcf7 = { window.pooledState[poolKey] = { slots: initializedSlots }; - // For instant customization, track original drives + // For instant customization, track original drives and pre-fill with requirements if (isInstant) { window.originalDrives = []; + // First reset all slots to None initializedSlots.forEach(slot => { - // Track the preselected drives as "original" - if (preselected[slot.id] && slot.currentVal.text !== '-' && - !slot.currentVal.text.toLowerCase().includes('none')) { - window.originalDrives.push({ - slotId: slot.id, - driveId: slot.currentVal.id, - text: slot.currentVal.text, - price: slot.currentVal.price - }); - - // Original drives have no additional cost (included in base price) - configState[slot.label] = 0; - } else { - configState[slot.label] = slot.currentVal.price; + const noneOption = slot.values.find(v => + v.text.trim() === '-' || + v.text.toLowerCase().includes('none') || + v.text.toLowerCase().includes('no hard drive') + ); + if (noneOption) { + slot.currentVal = noneOption; + configState[slot.label] = noneOption.price; + configIds[slot.id] = noneOption.id; } - - configIds[slot.id] = slot.currentVal.id; }); + + // Fill slots based on storage requirements + if (storageRequirements && storageRequirements.length > 0) { + storageRequirements.forEach(req => { + for (let i = 0; i < req.qty; i++) { + // Find an empty slot + const emptySlot = initializedSlots.find(s => + s.currentVal.text === '-' || + s.currentVal.text.toLowerCase().includes('none') + ); + + if (emptySlot) { + // Find matching drive type + const match = emptySlot.values.find(v => { + const vText = v.text.toLowerCase(); + const reqSpec = req.spec.toLowerCase(); + + // Extract capacity + const valCap = vText.match(/(\d+(?:\.\d+)?)\s*tb/i); + const reqCap = reqSpec.match(/(\d+(?:\.\d+)?)\s*tb/i); + if (valCap && reqCap && valCap[1] === reqCap[1]) { + // Check brand match + if ((vText.includes('crucial') && reqSpec.includes('crucial')) || + (vText.includes('kioxia') && reqSpec.includes('kioxia')) || + (vText.includes('t705') && reqSpec.includes('t705')) || + (vText.includes('cm7') && reqSpec.includes('cm7'))) { + return true; + } + } + return false; + }); + + if (match) { + emptySlot.currentVal = match; + configState[emptySlot.label] = 0; // Included in base price + configIds[emptySlot.id] = match.id; + + // Track as original drive + window.originalDrives.push({ + slotId: emptySlot.id, + driveId: match.id, + text: match.text, + price: match.price + }); + } + } + } + }); + } } else { // Pre-set configState for custom servers initializedSlots.forEach(slot => {