fix: complete solution for instant customization storage display
Major changes: 1. Reverted to using pooled storage grid for instant customization 2. Parse storage requirements from server description 3. Pre-fill pooled storage with correct drives (4x 4TB + 2x 3.2TB) 4. Original drives marked as €0 (included in base price) 5. Removed individual slot display which was causing issues How it works: - Parse "4x Crucial T705 4TB NVMe + 2x Kioxia CM7-V 3.2TB NVMe" - Match drives by capacity, brand, and model - Pre-fill the pooled storage grid - Storage summary shows correct counts - Price stays at €1520 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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,43 +4644,8 @@ 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);
|
||||
|
||||
// Create visualizer for this single slot
|
||||
const visualContainer = document.createElement('div');
|
||||
visualContainer.className = 'storage-slots-container';
|
||||
const uniqueSuffix = '-instant-s' + index;
|
||||
|
||||
visualContainer.innerHTML = `
|
||||
<div class="slots-header">
|
||||
<span>Slot: <strong>${opt.label}</strong></span>
|
||||
<span id="dynamic-slots-status${uniqueSuffix}" style="color:var(--success); font-size:0.75rem;">Configured</span>
|
||||
</div>
|
||||
<div class="slots-visual" id="dynamic-slots-visual${uniqueSuffix}">
|
||||
<div class="drive-slot filled"></div>
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(visualContainer);
|
||||
|
||||
// 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
|
||||
// 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
|
||||
|
||||
@@ -4741,10 +4707,9 @@ var wpcf7 = {
|
||||
container.appendChild(visualContainer);
|
||||
|
||||
// Always use pooled grid for these groups as they are by definition identical
|
||||
container.appendChild(createPooledStorageGrid(groupOpts, isInstant, preselected, groupIndex));
|
||||
container.appendChild(createPooledStorageGrid(groupOpts, isInstant, preselected, groupIndex, storageRequirements));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Network & Other
|
||||
const remaining = [...groups.network, ...groups.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 => {
|
||||
|
||||
Reference in New Issue
Block a user