mirror of
https://github.com/wg-easy/wg-easy.git
synced 2026-08-04 15:28:41 +00:00
fix buggy quota field
This commit is contained in:
parent
fae5b75193
commit
68571d99b9
3 changed files with 82 additions and 9 deletions
|
|
@ -12,13 +12,16 @@
|
|||
>
|
||||
<BaseInput
|
||||
:id="id"
|
||||
v-model.number="quotaGiB"
|
||||
:max="maximumGiB"
|
||||
:min="minimumGiB"
|
||||
:model-value="quotaInput"
|
||||
:name="id"
|
||||
step="any"
|
||||
type="number"
|
||||
class="w-full !border-0"
|
||||
@blur="onBlur"
|
||||
@focus="isFocused = true"
|
||||
@update:model-value="updateQuotaInput"
|
||||
/>
|
||||
<div
|
||||
class="flex h-full items-center bg-neutral-200 px-4 dark:bg-neutral-800"
|
||||
|
|
@ -35,11 +38,27 @@ const quotaBytes = defineModel<number | null>({ required: true });
|
|||
const minimumGiB = quotaBytesToGiB(1)!;
|
||||
const maximumGiB = quotaBytesToGiB(Number.MAX_SAFE_INTEGER)!;
|
||||
|
||||
const quotaGiB = computed<number | null>({
|
||||
get: () => quotaBytesToGiB(quotaBytes.value),
|
||||
set: (value) => {
|
||||
const normalized = (value as number | string | null) === '' ? null : value;
|
||||
quotaBytes.value = quotaGiBToBytes(normalized);
|
||||
},
|
||||
const isFocused = ref(false);
|
||||
const quotaInput = ref(quotaBytesToGiBInput(quotaBytes.value));
|
||||
|
||||
watch(quotaBytes, (value) => {
|
||||
if (!isFocused.value) {
|
||||
quotaInput.value = quotaBytesToGiBInput(value);
|
||||
}
|
||||
});
|
||||
|
||||
function updateQuotaInput(value: unknown) {
|
||||
const nextValue = typeof value === 'string' ? value : String(value ?? '');
|
||||
quotaInput.value = nextValue;
|
||||
|
||||
const nextBytes = quotaGiBInputToBytes(nextValue);
|
||||
if (nextBytes !== undefined) {
|
||||
quotaBytes.value = nextBytes;
|
||||
}
|
||||
}
|
||||
|
||||
function onBlur() {
|
||||
isFocused.value = false;
|
||||
quotaInput.value = quotaBytesToGiBInput(quotaBytes.value);
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -4,12 +4,45 @@ export function quotaBytesToGiB(bytes: number | null): number | null {
|
|||
return bytes === null ? null : bytes / GIBIBYTE_IN_BYTES;
|
||||
}
|
||||
|
||||
export function quotaBytesToGiBInput(bytes: number | null): string {
|
||||
if (bytes === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return quotaBytesToGiB(bytes)!
|
||||
.toFixed(9)
|
||||
.replace(/\.?0+$/, '');
|
||||
}
|
||||
|
||||
export function quotaGiBToBytes(gibibytes: number | null): number | null {
|
||||
if (gibibytes === null || gibibytes === 0) {
|
||||
if (gibibytes === null || gibibytes <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Math.round(gibibytes * GIBIBYTE_IN_BYTES);
|
||||
const bytes = Math.round(gibibytes * GIBIBYTE_IN_BYTES);
|
||||
return bytes > 0 ? bytes : null;
|
||||
}
|
||||
|
||||
export function quotaGiBInputToBytes(
|
||||
gibibytes: string
|
||||
): number | null | undefined {
|
||||
const value = gibibytes.trim();
|
||||
|
||||
if (value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const quotaGiB = Number(value);
|
||||
if (!Number.isFinite(quotaGiB) || quotaGiB < 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const quotaBytes = quotaGiBToBytes(quotaGiB);
|
||||
if (quotaBytes !== null && !Number.isSafeInteger(quotaBytes)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return quotaBytes;
|
||||
}
|
||||
|
||||
export function bytes(
|
||||
|
|
|
|||
|
|
@ -3,20 +3,28 @@ import { describe, expect, test } from 'vitest';
|
|||
import {
|
||||
GIBIBYTE_IN_BYTES,
|
||||
quotaBytesToGiB,
|
||||
quotaBytesToGiBInput,
|
||||
quotaGiBInputToBytes,
|
||||
quotaGiBToBytes,
|
||||
} from '#app/utils/math';
|
||||
|
||||
describe('quota conversions', () => {
|
||||
test('keeps disabled quotas null', () => {
|
||||
expect(quotaBytesToGiB(null)).toBeNull();
|
||||
expect(quotaBytesToGiBInput(null)).toBe('');
|
||||
expect(quotaGiBInputToBytes('')).toBeNull();
|
||||
expect(quotaGiBInputToBytes('0')).toBeNull();
|
||||
expect(quotaGiBToBytes(null)).toBeNull();
|
||||
expect(quotaGiBToBytes(0)).toBeNull();
|
||||
expect(quotaGiBToBytes(0.1 / GIBIBYTE_IN_BYTES)).toBeNull();
|
||||
});
|
||||
|
||||
test('converts whole and fractional GiB to bytes', () => {
|
||||
expect(quotaGiBToBytes(1)).toBe(GIBIBYTE_IN_BYTES);
|
||||
expect(quotaGiBToBytes(0.5)).toBe(GIBIBYTE_IN_BYTES / 2);
|
||||
expect(quotaGiBToBytes(1 / GIBIBYTE_IN_BYTES)).toBe(1);
|
||||
expect(quotaGiBInputToBytes('1')).toBe(GIBIBYTE_IN_BYTES);
|
||||
expect(quotaGiBInputToBytes('0.5')).toBe(GIBIBYTE_IN_BYTES / 2);
|
||||
});
|
||||
|
||||
test('rounds edited GiB values to the nearest byte', () => {
|
||||
|
|
@ -27,6 +35,19 @@ describe('quota conversions', () => {
|
|||
'round-trips the exact byte value %s',
|
||||
(bytes) => {
|
||||
expect(quotaGiBToBytes(quotaBytesToGiB(bytes))).toBe(bytes);
|
||||
expect(quotaGiBInputToBytes(quotaBytesToGiBInput(bytes))).toBe(bytes);
|
||||
}
|
||||
);
|
||||
|
||||
test('formats quota input values without noisy floating point decimals', () => {
|
||||
expect(quotaBytesToGiBInput(GIBIBYTE_IN_BYTES)).toBe('1');
|
||||
expect(quotaBytesToGiBInput(GIBIBYTE_IN_BYTES / 2)).toBe('0.5');
|
||||
expect(quotaBytesToGiBInput(107374182)).toBe('0.1');
|
||||
});
|
||||
|
||||
test('ignores invalid quota input values', () => {
|
||||
expect(quotaGiBInputToBytes('-1')).toBeUndefined();
|
||||
expect(quotaGiBInputToBytes('invalid')).toBeUndefined();
|
||||
expect(quotaGiBInputToBytes('9007199254740992')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue