fix: improve storage matching for instant customization
- Separate storage options handling from individual component matching - Pass storage requirements to pooled grid for proper pre-filling - Improve brand and capacity matching logic for storage drives - Reset slots to None before filling with instant server configuration - Add console warnings for debugging unmatched drives This ensures that when customizing an instant server, the storage configuration is correctly preserved (e.g., 4x Crucial T705 4TB + 2x Kioxia CM7-V 3.2TB) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4205,76 +4205,18 @@ var wpcf7 = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enhanced matching for instant server specs
|
// For storage options, we'll handle them in the pooled grid
|
||||||
options.forEach(opt => {
|
// So we skip individual storage matching here
|
||||||
|
const storageOptions = options.filter(opt => opt.type === 'storage');
|
||||||
|
const nonStorageOptions = options.filter(opt => opt.type !== 'storage');
|
||||||
|
|
||||||
|
// Enhanced matching for non-storage instant server specs
|
||||||
|
nonStorageOptions.forEach(opt => {
|
||||||
let selectedValId = null;
|
let selectedValId = null;
|
||||||
let urlId = preselected[opt.id];
|
let urlId = preselected[opt.id];
|
||||||
|
|
||||||
// Enhanced storage matching
|
|
||||||
if (opt.type === 'storage') {
|
|
||||||
// Create a score for each option based on how well it matches the requirements
|
|
||||||
const scores = opt.values.map(val => {
|
|
||||||
let score = 0;
|
|
||||||
const valText = val.text.toLowerCase();
|
|
||||||
|
|
||||||
// Check against each storage requirement
|
|
||||||
for (const req of storageRequirements) {
|
|
||||||
// Extract capacity from value (e.g., "1tb", "2tb")
|
|
||||||
const valCapacityMatch = valText.match(/(\d+)\s*tb/i);
|
|
||||||
const valCapacity = valCapacityMatch ? parseInt(valCapacityMatch[1]) : 0;
|
|
||||||
|
|
||||||
// Extract capacity from requirement
|
|
||||||
const reqCapacityMatch = req.spec.match(/(\d+)\s*tb/i);
|
|
||||||
const reqCapacity = reqCapacityMatch ? parseInt(reqCapacityMatch[1]) : 0;
|
|
||||||
|
|
||||||
// Score based on capacity match
|
|
||||||
if (valCapacity === reqCapacity && reqCapacity > 0) {
|
|
||||||
score += 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Score based on type keywords
|
|
||||||
const keywords = ['nvme', 'ssd', 'gen4', 'gen5', 'enterprise', 'consumer'];
|
|
||||||
keywords.forEach(keyword => {
|
|
||||||
if (valText.includes(keyword) && req.spec.includes(keyword)) {
|
|
||||||
score += 20;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Score based on brand
|
|
||||||
const brands = ['samsung', 'crucial', 'wd', 'seagate', 'kingston'];
|
|
||||||
brands.forEach(brand => {
|
|
||||||
if (valText.includes(brand) && req.spec.includes(brand)) {
|
|
||||||
score += 30;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Bonus for exact text match
|
|
||||||
if (valText.includes(req.spec) || req.spec.includes(valText.replace(/\s?\(.*?\)/, '').trim())) {
|
|
||||||
score += 50;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { id: val.id, score };
|
|
||||||
});
|
|
||||||
|
|
||||||
// Select the highest scoring option
|
|
||||||
scores.sort((a, b) => b.score - a.score);
|
|
||||||
if (scores[0] && scores[0].score > 0) {
|
|
||||||
selectedValId = scores[0].id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no good match, try to use "None" option
|
|
||||||
if (!selectedValId) {
|
|
||||||
const noneOpt = opt.values.find(v =>
|
|
||||||
v.text.trim() === '-' ||
|
|
||||||
v.text.toLowerCase().includes('none') ||
|
|
||||||
v.text.toLowerCase().includes('no hard drive')
|
|
||||||
);
|
|
||||||
selectedValId = noneOpt ? noneOpt.id : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Enhanced RAM matching
|
// Enhanced RAM matching
|
||||||
else if (opt.type === 'ram') {
|
if (opt.type === 'ram') {
|
||||||
const ramMatch = selectedServer.ram.match(/(\d+)\s*GB/i);
|
const ramMatch = selectedServer.ram.match(/(\d+)\s*GB/i);
|
||||||
const ramAmount = ramMatch ? parseInt(ramMatch[1]) : 0;
|
const ramAmount = ramMatch ? parseInt(ramMatch[1]) : 0;
|
||||||
|
|
||||||
@@ -4834,7 +4776,7 @@ var wpcf7 = {
|
|||||||
container.appendChild(visualContainer);
|
container.appendChild(visualContainer);
|
||||||
|
|
||||||
// Always use pooled grid for these groups as they are by definition identical
|
// 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));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4861,7 +4803,7 @@ var wpcf7 = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New Pooled Storage Grid
|
// New Pooled Storage Grid
|
||||||
function createPooledStorageGrid(storageOptions, isInstant, preselected = {}, groupIndex = 0) {
|
function createPooledStorageGrid(storageOptions, isInstant, preselected = {}, groupIndex = 0, storageRequirements = []) {
|
||||||
const grid = document.createElement('div');
|
const grid = document.createElement('div');
|
||||||
grid.className = 'config-grid';
|
grid.className = 'config-grid';
|
||||||
|
|
||||||
@@ -4871,27 +4813,7 @@ var wpcf7 = {
|
|||||||
// Use a unique key for each group to prevent conflicts
|
// Use a unique key for each group to prevent conflicts
|
||||||
const poolKey = (isInstant ? 'instant' : 'custom') + '-g' + groupIndex;
|
const poolKey = (isInstant ? 'instant' : 'custom') + '-g' + groupIndex;
|
||||||
|
|
||||||
// For instant customization, try to match storage requirements from server specs
|
// Storage requirements are now passed as a parameter for instant customization
|
||||||
let storageRequirements = [];
|
|
||||||
if (isInstant && selectedServer && selectedServer.storage) {
|
|
||||||
const parts = selectedServer.storage.split('+');
|
|
||||||
parts.forEach(part => {
|
|
||||||
const trimmed = part.trim();
|
|
||||||
if (!trimmed) return;
|
|
||||||
const match = trimmed.match(/^(\d+)x\s+(.+)$/);
|
|
||||||
if (match) {
|
|
||||||
storageRequirements.push({
|
|
||||||
qty: parseInt(match[1]),
|
|
||||||
spec: match[2].trim().toLowerCase()
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
storageRequirements.push({
|
|
||||||
qty: 1,
|
|
||||||
spec: trimmed.toLowerCase()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize slots with preselected values or defaults
|
// Initialize slots with preselected values or defaults
|
||||||
const initializedSlots = storageOptions.map(opt => {
|
const initializedSlots = storageOptions.map(opt => {
|
||||||
@@ -4916,6 +4838,20 @@ var wpcf7 = {
|
|||||||
|
|
||||||
// For instant customization, pre-fill with the server's storage configuration
|
// For instant customization, pre-fill with the server's storage configuration
|
||||||
if (isInstant && storageRequirements.length > 0) {
|
if (isInstant && storageRequirements.length > 0) {
|
||||||
|
// First, reset all slots to None
|
||||||
|
initializedSlots.forEach(slot => {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Fill slots based on storage requirements
|
// Fill slots based on storage requirements
|
||||||
storageRequirements.forEach(req => {
|
storageRequirements.forEach(req => {
|
||||||
for (let i = 0; i < req.qty; i++) {
|
for (let i = 0; i < req.qty; i++) {
|
||||||
@@ -4927,31 +4863,46 @@ var wpcf7 = {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (emptySlot) {
|
if (emptySlot) {
|
||||||
// Find matching drive type
|
// Find exact matching drive type
|
||||||
const match = emptySlot.values.find(v => {
|
const match = emptySlot.values.find(v => {
|
||||||
const vText = v.text.toLowerCase();
|
const vText = v.text.toLowerCase();
|
||||||
|
|
||||||
|
// Parse both to normalized form for comparison
|
||||||
|
const normalizeSpec = (spec) => {
|
||||||
|
return spec.toLowerCase()
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.replace(/nvme/g, '')
|
||||||
|
.replace(/ssd/g, '')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
};
|
||||||
|
|
||||||
// Extract capacity from value
|
// Extract capacity from value
|
||||||
const valCapacityMatch = vText.match(/(\d+)\s*tb/i);
|
const valCapacityMatch = vText.match(/(\d+(?:\.\d+)?)(?:\s*tb)?/i);
|
||||||
const valCapacity = valCapacityMatch ? parseInt(valCapacityMatch[1]) : 0;
|
const valCapacity = valCapacityMatch ? parseFloat(valCapacityMatch[1]) : 0;
|
||||||
|
|
||||||
// Extract capacity from requirement
|
// Extract capacity from requirement
|
||||||
const reqCapacityMatch = req.spec.match(/(\d+)\s*tb/i);
|
const reqCapacityMatch = req.spec.match(/(\d+(?:\.\d+)?)(?:\s*tb)?/i);
|
||||||
const reqCapacity = reqCapacityMatch ? parseInt(reqCapacityMatch[1]) : 0;
|
const reqCapacity = reqCapacityMatch ? parseFloat(reqCapacityMatch[1]) : 0;
|
||||||
|
|
||||||
// Match capacity and keywords
|
// Match capacity exactly
|
||||||
if (valCapacity === reqCapacity && reqCapacity > 0) {
|
if (Math.abs(valCapacity - reqCapacity) < 0.1) {
|
||||||
// Check for type keywords
|
// Check for exact brand match first
|
||||||
const keywords = ['nvme', 'ssd', 'gen4', 'gen5', 'enterprise', 'consumer'];
|
if (vText.includes('crucial') && req.spec.includes('crucial')) return true;
|
||||||
const hasKeyword = keywords.some(k =>
|
if (vText.includes('kioxia') && req.spec.includes('kioxia')) return true;
|
||||||
vText.includes(k) && req.spec.includes(k)
|
if (vText.includes('samsung') && req.spec.includes('samsung')) return true;
|
||||||
);
|
if (vText.includes('solidigm') && req.spec.includes('solidigm')) return true;
|
||||||
// Check for brand
|
if (vText.includes('wd') && req.spec.includes('wd')) return true;
|
||||||
const brands = ['samsung', 'crucial', 'wd', 'seagate', 'kingston'];
|
if (vText.includes('seagate') && req.spec.includes('seagate')) return true;
|
||||||
const hasBrand = brands.some(b =>
|
if (vText.includes('kingston') && req.spec.includes('kingston')) return true;
|
||||||
vText.includes(b) && req.spec.includes(b)
|
|
||||||
);
|
|
||||||
|
|
||||||
return hasKeyword || hasBrand || vText.includes(req.spec);
|
// If no brand match, check if it's just generic NVMe
|
||||||
|
if (!req.spec.includes('crucial') && !req.spec.includes('kioxia') &&
|
||||||
|
!req.spec.includes('samsung') && !req.spec.includes('solidigm')) {
|
||||||
|
return vText.includes('nvme');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -4959,7 +4910,11 @@ var wpcf7 = {
|
|||||||
emptySlot.currentVal = match;
|
emptySlot.currentVal = match;
|
||||||
configState[emptySlot.label] = match.price;
|
configState[emptySlot.label] = match.price;
|
||||||
configIds[emptySlot.id] = match.id;
|
configIds[emptySlot.id] = match.id;
|
||||||
|
} else {
|
||||||
|
console.warn(`Could not find matching drive for requirement: ${req.spec}`);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(`No empty slots available for requirement: ${req.spec}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user