From 6c3f959c923ad84c5f57201094e848d62e9776ab Mon Sep 17 00:00:00 2001 From: Saurabh Kushwaha Date: Thu, 15 Oct 2026 16:01:11 +0000 Subject: [PATCH] ui: allow memory setting in flex compute offering in GiB with 0.25 GiB step (#14082) - Add compact unit selector dropdown (GiB/MiB) for flex compute offerings - Use fixed 0.25 GiB steps in GiB mode (binary-exact to 256 MiB intervals, zero rounding errors) - Maintain integer MiB as authoritative internal state to prevent lossy conversion on load - Default to GiB for 256 MiB multiples and MiB for arbitrary sizes (e.g. 1500 MiB) - Add hover tooltip to display exact converted values --- .../views/compute/wizard/ComputeSelection.vue | 142 +++++++++++++++--- 1 file changed, 118 insertions(+), 24 deletions(-) diff --git a/ui/src/views/compute/wizard/ComputeSelection.vue b/ui/src/views/compute/wizard/ComputeSelection.vue index 9cf36153f5cd..1fe4ca7a21c5 100644 --- a/ui/src/views/compute/wizard/ComputeSelection.vue +++ b/ui/src/views/compute/wizard/ComputeSelection.vue @@ -57,23 +57,40 @@ - + - - + + + + + + + GiB + MiB + + @@ -165,6 +182,8 @@ export default { cpuNumberInputValue: 0, cpuSpeedInputValue: 0, memoryInputValue: 0, + memoryDisplayValue: 0, + memoryUnit: 'GiB', errors: { cpu: { status: '', @@ -192,6 +211,37 @@ export default { } return 8 + }, + minMemoryInUnit () { + if (!this.minMemory) return 0 + if (this.memoryUnit === 'GiB') { + return Math.ceil(this.minMemory / 256) * 0.25 + } + return this.minMemory + }, + maxMemoryInUnit () { + if (!this.maxMemory) return 0 + if (this.memoryUnit === 'GiB') { + return Math.max(this.minMemoryInUnit, Math.floor(this.maxMemory / 256) * 0.25) + } + return this.maxMemory + }, + memoryStep () { + if (this.memoryUnit === 'GiB') { + return 0.25 + } + return 1 + }, + memoryTooltipText () { + if (!this.memoryInputValue) return '' + const exactGiB = this.memoryInputValue / 1024 + if (this.memoryUnit === 'GiB') { + if (this.memoryDisplayValue === exactGiB) { + return `${this.memoryDisplayValue} GiB = ${this.memoryInputValue} MiB` + } + return `Exact: ${exactGiB} GiB (${this.memoryInputValue} MiB)` + } + return `${this.memoryInputValue} MiB = ${exactGiB} GiB` } }, watch: { @@ -209,13 +259,24 @@ export default { methods: { fillValue () { this.cpuNumberInputValue = this.initialCpuValue > 0 ? this.initialCpuValue : this.minCpu - this.memoryInputValue = this.initialMemoryValue > 0 ? this.initialMemoryValue : this.minMemory + const rawMemory = (this.preFillContent && this.preFillContent.memory) + ? this.preFillContent.memory + : (this.initialMemoryValue > 0 ? this.initialMemoryValue : this.minMemory) + + // MiB is the authoritative internal state + this.memoryInputValue = rawMemory + + // Default to GiB if rawMemory is a multiple of 256 MiB (exact 0.25 GiB increment) + const isQuarterGiB = rawMemory >= 256 && (rawMemory % 256 === 0) + this.memoryUnit = isQuarterGiB ? 'GiB' : 'MiB' + this.memoryDisplayValue = isQuarterGiB ? (rawMemory / 1024) : rawMemory + this.cpuSpeedInputValue = this.cpuSpeed if (!this.preFillContent) { this.updateComputeCpuNumber(this.cpuNumberInputValue) this.updateComputeCpuSpeed(this.cpuSpeedInputValue) - this.updateComputeMemory(this.memoryInputValue) + this.$emit('update-compute-memory', this.memoryInputDecorator, this.memoryInputValue) return } if (this.preFillContent.cpunumber) { @@ -224,12 +285,25 @@ export default { if (this.preFillContent.cpuspeed) { this.cpuSpeedInputValue = this.preFillContent.cpuspeed } - if (this.preFillContent.memory) { - this.memoryInputValue = this.preFillContent.memory - } + this.updateComputeCpuNumber(this.preFillContent.cpunumber || this.cpuNumberInputValue) this.updateComputeCpuSpeed(this.preFillContent.cpuspeed || this.cpuSpeedInputValue) - this.updateComputeMemory(this.preFillContent.memory || this.memoryInputValue) + // Emit exact prefilled MiB without any conversion on load + this.$emit('update-compute-memory', this.memoryInputDecorator, this.memoryInputValue) + }, + handleUnitChange (newUnit) { + if (newUnit === 'MiB') { + this.memoryDisplayValue = this.memoryInputValue + } else { + this.memoryDisplayValue = Math.round(this.memoryInputValue / 256) * 0.25 + } + if (this.isConstrained) { + if (this.memoryDisplayValue < this.minMemoryInUnit) { + this.memoryDisplayValue = this.minMemoryInUnit + } else if (this.memoryDisplayValue > this.maxMemoryInUnit) { + this.memoryDisplayValue = this.maxMemoryInUnit + } + } }, updateComputeCpuNumber (value) { if (!value) this.cpuNumberInputValue = 0 @@ -241,18 +315,37 @@ export default { updateComputeCpuSpeed (value) { this.$emit('update-compute-cpuspeed', this.cpuSpeedInputDecorator, value) }, - updateComputeMemory (value) { - if (!value) this.memoryInputValue = 0 - if (!this.validateInput('memory', value)) { + updateComputeMemory (displayValue) { + if (displayValue === null || displayValue === undefined || displayValue === '') { + this.memoryDisplayValue = 0 + this.memoryInputValue = 0 + } else { + this.memoryDisplayValue = displayValue + } + if (!this.validateInput('memory', displayValue)) { return } - this.$emit('update-compute-memory', this.memoryInputDecorator, value) + let memoryInMiB + if (this.memoryUnit === 'GiB') { + if (this.isConstrained && displayValue === this.minMemoryInUnit) { + memoryInMiB = this.minMemory + } else if (this.isConstrained && displayValue === this.maxMemoryInUnit) { + memoryInMiB = this.maxMemory + } else { + memoryInMiB = Math.round(displayValue * 1024) + } + } else { + memoryInMiB = Math.round(displayValue) + } + + this.memoryInputValue = memoryInMiB + this.$emit('update-compute-memory', this.memoryInputDecorator, memoryInMiB) }, validateInput (input, value) { this.errors[input].status = '' this.errors[input].message = '' - if (value === null || value === undefined || value.length === 0) { + if (value === null || value === undefined || value.length === 0 || value === '' || value === 0) { this.errors[input].status = 'error' this.errors[input].message = this.$t('message.error.required.input') return false @@ -271,8 +364,8 @@ export default { max = this.maxCpu break case 'memory': - min = this.minMemory - max = this.maxMemory + min = this.minMemoryInUnit + max = this.maxMemoryInUnit break } @@ -285,7 +378,8 @@ export default { return true }, checkValidRange (value, min, max) { - if (value < min || value > max) { + const epsilon = 0.001 + if (value < (min - epsilon) || value > (max + epsilon)) { return false }