Update the availability duration and size input

This commit is contained in:
Arnaud 2024-11-27 13:40:38 +01:00
parent cd35972992
commit 174dcf78d9
No known key found for this signature in database
GPG Key ID: 69D6CE281FCAE663
5 changed files with 95 additions and 51 deletions

View File

@ -1,11 +1,16 @@
import test, { expect } from "@playwright/test";
import { Bytes } from "../src/utils/bytes"
import { GB } from "../src/utils/constants"
test('create an availability', async ({ page }) => {
await page.goto('/dashboard/availabilities');
await page.waitForTimeout(500);
await page.locator('.availability-edit button').first().click();
await page.getByLabel('Total size').click();
await page.getByLabel('Total size').fill('0.50');
const value = (Math.random() * 0.5);
await page.getByLabel('Total size').fill(value.toFixed(2));
await page.getByLabel('Duration').click();
await page.getByLabel('Duration').fill('30');
await page.getByLabel('Min price').click();
@ -20,7 +25,7 @@ test('create an availability', async ({ page }) => {
await page.getByRole('button', { name: 'Next' }).click();
await expect(page.getByText('Success', { exact: true })).toBeVisible();
await page.getByRole('button', { name: 'Finish' }).click();
await expect(page.getByText('512.0 MB allocated for the').first()).toBeVisible();
await expect(page.getByText(Bytes.pretty(parseFloat(value.toFixed(1)) * GB)).first()).toBeVisible();
})
test('availability navigation buttons', async ({ page }) => {
@ -54,3 +59,63 @@ test('availability navigation buttons', async ({ page }) => {
await page.getByRole('button', { name: 'Finish' }).click();
await expect(page.locator('.modal--open')).not.toBeVisible();
})
test('create an availability with changing the duration to months', async ({ page }) => {
await page.goto('/dashboard/availabilities');
await page.waitForTimeout(500);
await page.locator('.availability-edit button').first().click();
await page.getByLabel('Total size').click();
await page.getByLabel('Total size').fill("0.1");
await page.getByLabel('Duration').click();
await page.getByLabel('Duration').fill("3");
await page.getByRole('combobox').nth(1).selectOption('months');
await page.getByLabel('Min price').click();
await page.getByLabel('Min price').fill('5');
await page.getByLabel('Max collateral').click();
await page.getByLabel('Max collateral').fill('30');
await page.getByLabel('Min price').fill('5');
await page.getByLabel('Nickname').click();
await page.getByLabel('Nickname').fill('test');
await page.getByRole('button', { name: 'Next' }).click();
await expect(page.getByText('Confirm your new sale')).toBeVisible();
await page.getByRole('button', { name: 'Next' }).click();
await expect(page.getByText('Success', { exact: true })).toBeVisible();
await page.getByRole('button', { name: 'Finish' }).click();
await expect(page.getByText("3 months").first()).toBeVisible();
})
test('create an availability after checking max size and invalid input', async ({ page }) => {
await page.goto('/dashboard/availabilities');
await page.waitForTimeout(500);
await page.locator('.availability-edit button').first().click();
await page.getByLabel('Total size').click();
await page.getByLabel('Total size').fill("9999");
await expect(page.getByLabel('Total size')).toHaveAttribute("aria-invalid");
await page.getByText("Use max size").click()
await expect(page.getByLabel('Total size')).not.toHaveAttribute("aria-invalid");
const value = (Math.random() * 0.5);
await page.getByLabel('Total size').fill(value.toFixed(1));
await page.getByLabel('Duration').click();
await page.getByLabel('Duration').fill('30');
await page.getByLabel('Min price').click();
await page.getByLabel('Min price').fill('5');
await page.getByLabel('Max collateral').click();
await page.getByLabel('Max collateral').fill('30');
await page.getByLabel('Min price').fill('5');
await page.getByLabel('Nickname').click();
await page.getByLabel('Nickname').fill('test');
await page.getByRole('button', { name: 'Next' }).click();
await expect(page.getByText('Confirm your new sale')).toBeVisible();
await page.getByRole('button', { name: 'Next' }).click();
await expect(page.getByText('Success', { exact: true })).toBeVisible();
await page.getByRole('button', { name: 'Finish' }).click();
await expect(page.getByText(Bytes.pretty(parseFloat(value.toFixed(1)) * GB)).first()).toBeVisible();
})

View File

@ -28,8 +28,8 @@ type Props = {
const CONFIRM_STATE = 2;
const defaultAvailabilityData: AvailabilityState = {
totalSize: 0.5 * GB,
duration: Times.value("days"),
totalSize: 0.5,
duration: 1,
minPrice: 0,
maxCollateral: 0,
totalSizeUnit: "gb",
@ -52,7 +52,7 @@ export function AvailabilityEdit({
useEffect(() => {
Promise.all([
WebStorage.get<number>("availability-step"),
WebStorage.get<AvailabilityState>("availability"),
WebStorage.get<AvailabilityState>("availability-1"),
]).then(([s, a]) => {
if (s) {
dispatch({
@ -67,26 +67,6 @@ export function AvailabilityEdit({
});
}, [dispatch]);
// We use a custom event to not re render the sunburst component
// useEffect(() => {
// const onAvailabilityIdChange = (e: Event) => {
// const custom = e as CustomEvent;
// setAvailabilityId(custom.detail);
// };
// document.addEventListener(
// "codexavailabilityid",
// onAvailabilityIdChange,
// false
// );
// return () =>
// document.removeEventListener(
// "codexavailabilityid",
// onAvailabilityIdChange
// );
// }, []);
const components = [
AvailabilityForm,
AvailabilityConfirm,
@ -157,7 +137,7 @@ export function AvailabilityEdit({
editAvailabilityValue.current = a.totalSize;
WebStorage.set("availability-step", 0);
WebStorage.set("availability", a);
WebStorage.set("availability-1", a);
const unit = Times.unit(a.duration);

View File

@ -11,7 +11,6 @@ import NodesIcon from "../../assets/icons/nodes.svg?react";
import InfoIcon from "../../assets/icons/info.svg?react";
import { attributes } from "../../utils/attributes";
import { AvailabilityUtils } from "./availability.utils";
import { Times } from "../../utils/times";
export function AvailabilityForm({
dispatch,
@ -39,27 +38,24 @@ export function AvailabilityForm({
const element = e.currentTarget;
onAvailabilityChange({
totalSize: 0,
totalSizeUnit: element.value as "tb" | "gb",
});
};
const onDurationChange = async (e: ChangeEvent<HTMLInputElement>) => {
const element = e.currentTarget;
const unitValue = Times.value(availability.durationUnit);
onAvailabilityChange({
duration: parseInt(element.value) * unitValue,
duration: parseInt(element.value),
});
};
const onDurationUnitChange = async (e: ChangeEvent<HTMLSelectElement>) => {
const element = e.currentTarget;
const unit = element.value as "hours" | "days" | "months";
const unitValue = Times.value(unit);
onAvailabilityChange({
duration: unitValue,
duration: availability.duration,
durationUnit: unit,
});
};
@ -67,10 +63,9 @@ export function AvailabilityForm({
const onAvailablityChange = async (e: ChangeEvent<HTMLInputElement>) => {
const element = e.currentTarget;
const v = element.value;
const unit = AvailabilityUtils.unitValue(availability.totalSizeUnit);
onAvailabilityChange({
totalSize: parseFloat(v) * unit,
totalSize: parseFloat(v),
});
};
@ -87,7 +82,12 @@ export function AvailabilityForm({
const available = AvailabilityUtils.maxValue(space);
onAvailabilityChange({
totalSize: available,
totalSize:
Math.floor(
((available - 1) /
AvailabilityUtils.unitValue(availability.totalSizeUnit)) *
10
) / 10,
});
};
@ -96,20 +96,17 @@ export function AvailabilityForm({
available += editAvailabilityValue;
}
const isValid =
availability.totalSize > 0 && available >= availability.totalSize;
const totalSizeInBytes =
availability.totalSize *
AvailabilityUtils.unitValue(availability.totalSizeUnit);
const isValid = totalSizeInBytes > 0 && available >= totalSizeInBytes;
const helper = isValid
? "Total size of sale's storage in bytes"
: "The total size cannot exceed the space available.";
const value = AvailabilityUtils.toUnit(
availability.totalSize,
availability.totalSizeUnit
).toFixed(2);
const unitValue = Times.value(availability.durationUnit);
const duration = availability.duration / unitValue;
const duration = availability.duration;
return (
<div className="availability-form">
@ -143,13 +140,12 @@ export function AvailabilityForm({
name="totalSize"
type="number"
label="Total size"
min={0.01}
isInvalid={!isValid}
max={available.toFixed(2)}
onChange={onAvailablityChange}
onGroupChange={onTotalSizeUnitChange}
step={"0.01"}
value={value}
value={availability.totalSize.toString()}
min={"0"}
group={[
["gb", "GB"],
// ["tb", "TB"],

View File

@ -39,7 +39,8 @@ export const AvailabilityUtils = {
return bytes / this.unitValue(unit || "gb")
},
maxValue(space: CodexNodeSpace) {
return space.quotaMaxBytes - space.quotaReservedBytes - space.quotaUsedBytes
// Remove 1 byte to allow to create an availability with the max space possible
return space.quotaMaxBytes - space.quotaReservedBytes - space.quotaUsedBytes - 1
},
unitValue(unit: "gb" | "tb") {
return unit === "tb" ? TB : GB

View File

@ -9,6 +9,8 @@ import {
} from "@codex-storage/marketplace-ui-components";
import { CodexSdk } from "../../sdk/codex";
import { CodexAvailabilityCreateResponse } from "@codex-storage/sdk-js";
import { Times } from "../../utils/times";
import { AvailabilityUtils } from "./availability.utils";
export function useAvailabilityMutation(
@ -42,15 +44,15 @@ export function useAvailabilityMutation(
return fn({
...input,
duration,
totalSize: Math.trunc(totalSize),
duration: Times.value(durationUnit) * duration,
totalSize: Math.trunc(totalSize * AvailabilityUtils.unitValue(totalSizeUnit)),
});
},
onSuccess: (res, body) => {
queryClient.invalidateQueries({ queryKey: ["availabilities"] });
queryClient.invalidateQueries({ queryKey: ["space"] });
WebStorage.delete("availability");
WebStorage.delete("availability-1");
WebStorage.delete("availability-step");
if (typeof res === "object" && body.name) {