From a61207cf43d5003dc6408fbe5a96a0678b2e37d5 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 27 Nov 2024 09:43:03 +0100 Subject: [PATCH 01/17] Refactory bytes pretty --- .../Availability/AvailabilitiesTable.tsx | 4 +-- .../Availability/AvailabilityDiskRow.tsx | 4 +-- .../Availability/AvailabilityIdCell.tsx | 4 +-- src/components/Availability/SlotRow.tsx | 4 +-- src/components/Availability/Sunburst.tsx | 8 +++--- src/components/Files/FileDetails.tsx | 4 +-- src/components/Files/Files.tsx | 4 +-- src/routes/dashboard/availabilities.tsx | 6 ++--- src/utils/bytes.ts | 27 ++++++++++--------- 9 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/components/Availability/AvailabilitiesTable.tsx b/src/components/Availability/AvailabilitiesTable.tsx index 776f57a..531e050 100644 --- a/src/components/Availability/AvailabilitiesTable.tsx +++ b/src/components/Availability/AvailabilitiesTable.tsx @@ -4,7 +4,7 @@ import { Table, TabSortState, } from "@codex-storage/marketplace-ui-components"; -import { PrettyBytes } from "../../utils/bytes"; +import { Bytes } from "../../utils/bytes"; import { AvailabilityActionsCell } from "./AvailabilityActionsCell"; import { CodexAvailability, CodexNodeSpace } from "@codex-storage/sdk-js/async"; import { Times } from "../../utils/times"; @@ -86,7 +86,7 @@ export function AvailabilitiesTable({ availabilities, space }: Props) { )} , , - {PrettyBytes(a.totalSize)}, + {Bytes.pretty(a.totalSize)}, {Times.pretty(a.duration)}, {a.minPrice.toString()}, {a.maxCollateral.toString()}, diff --git a/src/components/Availability/AvailabilityDiskRow.tsx b/src/components/Availability/AvailabilityDiskRow.tsx index 9ee444d..e5f4aca 100644 --- a/src/components/Availability/AvailabilityDiskRow.tsx +++ b/src/components/Availability/AvailabilityDiskRow.tsx @@ -1,5 +1,5 @@ import { Cell, Row } from "@codex-storage/marketplace-ui-components"; -import { PrettyBytes } from "../../utils/bytes"; +import { Bytes } from "../../utils/bytes"; import { classnames } from "../../utils/classnames"; import HardriveIcon from "../../assets/icons/hardrive.svg?react"; @@ -19,7 +19,7 @@ export function AvailabilityDiskRow({ bytes }: Props) {
Node - {PrettyBytes(bytes)} allocated for the node + {Bytes.pretty(bytes)} allocated for the node
, diff --git a/src/components/Availability/AvailabilityIdCell.tsx b/src/components/Availability/AvailabilityIdCell.tsx index aec2721..f2c1478 100644 --- a/src/components/Availability/AvailabilityIdCell.tsx +++ b/src/components/Availability/AvailabilityIdCell.tsx @@ -1,6 +1,6 @@ import { Strings } from "../../utils/strings"; import { Cell } from "@codex-storage/marketplace-ui-components"; -import { PrettyBytes } from "../../utils/bytes"; +import { Bytes } from "../../utils/bytes"; import { AvailabilityWithSlots } from "./types"; import AvailbilityIcon from "../../assets/icons/availability.svg?react"; @@ -18,7 +18,7 @@ export function AvailabilityIdCell({ value }: Props) { {value.name || Strings.shortId(value.id)} - {PrettyBytes(value.totalSize)} allocated for the availability + {Bytes.pretty(value.totalSize)} allocated for the availability
diff --git a/src/components/Availability/SlotRow.tsx b/src/components/Availability/SlotRow.tsx index 7cc2793..c78b450 100644 --- a/src/components/Availability/SlotRow.tsx +++ b/src/components/Availability/SlotRow.tsx @@ -1,5 +1,5 @@ import { Cell, Row } from "@codex-storage/marketplace-ui-components"; -import { PrettyBytes } from "../../utils/bytes"; +import { Bytes } from "../../utils/bytes"; import "./SlotRow.css"; import { classnames } from "../../utils/classnames"; import { attributes } from "../../utils/attributes"; @@ -47,7 +47,7 @@ export function SlotRow({ bytes, active, id }: Props) {
Slot {id} - {PrettyBytes(bytes)} allocated for the slot + {Bytes.pretty(bytes)} allocated for the slot
, diff --git a/src/components/Availability/Sunburst.tsx b/src/components/Availability/Sunburst.tsx index c0950c5..6ae7422 100644 --- a/src/components/Availability/Sunburst.tsx +++ b/src/components/Availability/Sunburst.tsx @@ -1,7 +1,7 @@ import { CodexNodeSpace } from "@codex-storage/sdk-js"; import { Times } from "../../utils/times"; import { Strings } from "../../utils/strings"; -import { PrettyBytes } from "../../utils/bytes"; +import { Bytes } from "../../utils/bytes"; import { useEffect, useRef, useState } from "react"; import { CallbackDataParams, ECBasicOption } from "echarts/types/dist/shared"; import * as echarts from "echarts/core"; @@ -65,7 +65,7 @@ export function Sunburst({ availabilities, space }: Props) { a.minPrice + "
" + "Size " + - PrettyBytes(a.totalSize) + Bytes.pretty(a.totalSize) ); }, }, @@ -87,7 +87,7 @@ export function Sunburst({ availabilities, space }: Props) { params.marker + "Slot " + slot.id + - PrettyBytes(parseFloat(slot.size)) + Bytes.pretty(parseFloat(slot.size)) ); }, }, @@ -121,7 +121,7 @@ export function Sunburst({ availabilities, space }: Props) { return ( params.marker + " Space remaining " + - PrettyBytes( + Bytes.pretty( space.quotaMaxBytes - space.quotaReservedBytes - space.quotaUsedBytes diff --git a/src/components/Files/FileDetails.tsx b/src/components/Files/FileDetails.tsx index d9824f6..8d0b1d0 100644 --- a/src/components/Files/FileDetails.tsx +++ b/src/components/Files/FileDetails.tsx @@ -5,7 +5,7 @@ import { WebFileIcon, } from "@codex-storage/marketplace-ui-components"; import { CodexDataContent, CodexPurchase } from "@codex-storage/sdk-js"; -import { PrettyBytes } from "../../utils/bytes"; +import { Bytes } from "../../utils/bytes"; import { CidCopyButton } from "./CidCopyButton"; import "./FileDetails.css"; import { CodexSdk } from "../../sdk/codex"; @@ -129,7 +129,7 @@ export function FileDetails({ onClose, details }: Props) {
  • Size:

    -

    {PrettyBytes(details.manifest.datasetSize)}

    +

    {Bytes.pretty(details.manifest.datasetSize)}

  • diff --git a/src/components/Files/Files.tsx b/src/components/Files/Files.tsx index 83d0e85..172e727 100644 --- a/src/components/Files/Files.tsx +++ b/src/components/Files/Files.tsx @@ -1,5 +1,5 @@ import { ChangeEvent, useEffect, useState } from "react"; -import { PrettyBytes } from "../../utils/bytes"; +import { Bytes } from "../../utils/bytes"; import "./Files.css"; import { Tabs, @@ -164,7 +164,7 @@ export function Files({ limit }: Props) { , - {PrettyBytes(c.manifest.datasetSize)}, + {Bytes.pretty(c.manifest.datasetSize)}, {FilesUtils.formatDate(c.manifest.uploadedAt).toString()} , diff --git a/src/routes/dashboard/availabilities.tsx b/src/routes/dashboard/availabilities.tsx index e1a960f..20d83f1 100644 --- a/src/routes/dashboard/availabilities.tsx +++ b/src/routes/dashboard/availabilities.tsx @@ -10,7 +10,7 @@ import "./availabilities.css"; import { AvailabilitiesTable } from "../../components/Availability/AvailabilitiesTable"; import { AvailabilityEdit } from "../../components/Availability/AvailabilityEdit"; import { Strings } from "../../utils/strings"; -import { PrettyBytes } from "../../utils/bytes"; +import { Bytes } from "../../utils/bytes"; import { Sunburst } from "../../components/Availability/Sunburst"; import { Errors } from "../../utils/errors"; import { AvailabilityWithSlots } from "../../components/Availability/types"; @@ -111,7 +111,7 @@ export function AvailabilitiesRoute() { (a, index) => ({ title: Strings.shortId(a.id), size: a.totalSize, - tooltip: a.id + "\u000D\u000A" + PrettyBytes(a.totalSize), + tooltip: a.id + "\u000D\u000A" + Bytes.pretty(a.totalSize), color: AvailabilityUtils.availabilityColors[index], }) ); @@ -170,7 +170,7 @@ export function AvailabilitiesRoute() {
    Node - {PrettyBytes(space.quotaMaxBytes)} allocated for the node + {Bytes.pretty(space.quotaMaxBytes)} allocated for the node
    diff --git a/src/utils/bytes.ts b/src/utils/bytes.ts index 7cf307a..9bc5a22 100644 --- a/src/utils/bytes.ts +++ b/src/utils/bytes.ts @@ -1,14 +1,17 @@ -export const PrettyBytes = (bytes: number) => { - const sizes = ["bytes", "KB", "MB", "GB", "TB"]; - if (bytes == 0) { - return "0 b"; +export const Bytes = { + pretty(bytes: number) { + const sizes = ["bytes", "KB", "MB", "GB", "TB"]; + if (bytes == 0) { + return "0 b"; + } + + const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)).toString()); + + if (i == 0) { + return bytes + " " + sizes[i]; + } + + return (bytes / Math.pow(1024, i)).toFixed(1) + " " + sizes[i]; } - const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)).toString()); - - if (i == 0) { - return bytes + " " + sizes[i]; - } - - return (bytes / Math.pow(1024, i)).toFixed(1) + " " + sizes[i]; -}; +} \ No newline at end of file From dc8b42c03ae18b6a2ec078dcbceda6bd7a67dbd7 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 27 Nov 2024 11:01:13 +0100 Subject: [PATCH 02/17] Use slots instead of purchases to check marketplace in order to avoid to make multiple request --- src/hooks/usePersistence.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/usePersistence.tsx b/src/hooks/usePersistence.tsx index b66d68d..7546f11 100644 --- a/src/hooks/usePersistence.tsx +++ b/src/hooks/usePersistence.tsx @@ -9,7 +9,7 @@ export function usePersistence(isCodexOnline: boolean) { queryKey: [], queryFn: async () => { return CodexSdk.marketplace() - .purchases() + .activeSlots() .then((data) => Promises.rejectOnError(data, report)); }, From b90b5bd9fcdcf21342d10acbc4035737905fb03a Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 27 Nov 2024 11:06:02 +0100 Subject: [PATCH 03/17] Allow to create a storage request for days and months --- e2e/storage-requests.spec.ts | 46 +++++++++++ .../Availability/AvailabilityEdit.tsx | 4 +- .../Availability/AvailabilityForm.tsx | 6 +- .../Availability/availability.utils.ts | 2 +- src/components/Files/files.utils.test.ts | 4 +- .../StorageRequestSetup/Commitment.css | 7 ++ .../StorageRequestSetup/Commitment.tsx | 76 +++++++++++++++++++ .../StorageRequestCreate.tsx | 12 ++- .../StorageRequestReview.tsx | 34 +-------- src/components/StorageRequestSetup/types.ts | 15 ---- .../useStorageRequestMutation.ts | 2 +- src/utils/bytes.test.ts | 12 +++ src/utils/bytes.ts | 6 +- src/utils/times.test.ts | 28 +++++++ src/utils/times.ts | 25 +++--- 15 files changed, 203 insertions(+), 76 deletions(-) create mode 100644 src/components/StorageRequestSetup/Commitment.css create mode 100644 src/components/StorageRequestSetup/Commitment.tsx create mode 100644 src/utils/bytes.test.ts create mode 100644 src/utils/times.test.ts diff --git a/e2e/storage-requests.spec.ts b/e2e/storage-requests.spec.ts index 23be51f..55300e8 100644 --- a/e2e/storage-requests.spec.ts +++ b/e2e/storage-requests.spec.ts @@ -76,3 +76,49 @@ test('remove the CID when the file is deleted', async ({ page }) => { await page.locator('.button-icon--small').nth(1).click(); await expect(page.locator('#cid')).toBeEmpty() }) + +test('create a storage request by using decimal values', async ({ page }) => { + await page.goto('/dashboard'); + await page.locator('a').filter({ hasText: 'Purchases' }).click(); + await page.getByRole('button', { name: 'Storage Request' }).click(); + + await page.locator('div').getByTestId("upload").setInputFiles([ + path.join(__dirname, "assets", 'chatgpt.jpg'), + ]); + await expect(page.locator('#cid')).not.toBeEmpty() + await expect(page.getByText('Success, the CID has been')).toBeVisible(); + await page.getByRole('button', { name: 'Next' }).click(); + + const value = (Math.random() * 10); + await page.getByLabel("Full period of the contract").fill(value.toFixed(1)) + + await page.getByRole('button', { name: 'Next' }).click(); + await expect(page.getByText('Your request is being processed.')).toBeVisible(); + await page.getByRole('button', { name: 'Finish' }).click(); + await expect(page.getByText('No data.')).not.toBeVisible(); + await expect(page.getByText(value.toFixed(1) + " days").first()).toBeVisible(); +}) + +test('create a storage request by using months', async ({ page }) => { + await page.goto('/dashboard'); + await page.locator('a').filter({ hasText: 'Purchases' }).click(); + await page.getByRole('button', { name: 'Storage Request' }).click(); + + await page.locator('div').getByTestId("upload").setInputFiles([ + path.join(__dirname, "assets", 'chatgpt.jpg'), + ]); + await expect(page.locator('#cid')).not.toBeEmpty() + await expect(page.getByText('Success, the CID has been')).toBeVisible(); + await page.getByRole('button', { name: 'Next' }).click(); + + await page.getByLabel("Full period of the contract").fill("3") + await page.getByRole('combobox').selectOption('months'); + await expect(page.getByLabel("Full period of the contract")).toHaveValue("1") + await page.getByLabel("Full period of the contract").fill("2") + + await page.getByRole('button', { name: 'Next' }).click(); + await expect(page.getByText('Your request is being processed.')).toBeVisible(); + await page.getByRole('button', { name: 'Finish' }).click(); + await expect(page.getByText('No data.')).not.toBeVisible(); + await expect(page.getByText("2 months").first()).toBeVisible(); +}) \ No newline at end of file diff --git a/src/components/Availability/AvailabilityEdit.tsx b/src/components/Availability/AvailabilityEdit.tsx index 1389663..c6780b1 100644 --- a/src/components/Availability/AvailabilityEdit.tsx +++ b/src/components/Availability/AvailabilityEdit.tsx @@ -29,7 +29,7 @@ const CONFIRM_STATE = 2; const defaultAvailabilityData: AvailabilityState = { totalSize: 0.5 * GB, - duration: Times.unitValue("days"), + duration: Times.value("days"), minPrice: 0, maxCollateral: 0, totalSizeUnit: "gb", @@ -62,7 +62,7 @@ export function AvailabilityEdit({ } if (a) { - setAvailability(a); + setAvailability({ ...defaultAvailabilityData, ...a }); } }); }, [dispatch]); diff --git a/src/components/Availability/AvailabilityForm.tsx b/src/components/Availability/AvailabilityForm.tsx index 315dcf1..f8b1a7b 100644 --- a/src/components/Availability/AvailabilityForm.tsx +++ b/src/components/Availability/AvailabilityForm.tsx @@ -46,7 +46,7 @@ export function AvailabilityForm({ const onDurationChange = async (e: ChangeEvent) => { const element = e.currentTarget; - const unitValue = Times.unitValue(availability.durationUnit); + const unitValue = Times.value(availability.durationUnit); onAvailabilityChange({ duration: parseInt(element.value) * unitValue, @@ -56,7 +56,7 @@ export function AvailabilityForm({ const onDurationUnitChange = async (e: ChangeEvent) => { const element = e.currentTarget; const unit = element.value as "hours" | "days" | "months"; - const unitValue = Times.unitValue(unit); + const unitValue = Times.value(unit); onAvailabilityChange({ duration: unitValue, @@ -108,7 +108,7 @@ export function AvailabilityForm({ availability.totalSizeUnit ).toFixed(2); - const unitValue = Times.unitValue(availability.durationUnit); + const unitValue = Times.value(availability.durationUnit); const duration = availability.duration / unitValue; return ( diff --git a/src/components/Availability/availability.utils.ts b/src/components/Availability/availability.utils.ts index 464c600..e73b1ee 100644 --- a/src/components/Availability/availability.utils.ts +++ b/src/components/Availability/availability.utils.ts @@ -88,5 +88,5 @@ export const AvailabilityUtils = { "#D2493C22", "#D2493C11", "#D2493C00", - ] + ], } \ No newline at end of file diff --git a/src/components/Files/files.utils.test.ts b/src/components/Files/files.utils.test.ts index 4b8fd9d..7e43cc6 100644 --- a/src/components/Files/files.utils.test.ts +++ b/src/components/Files/files.utils.test.ts @@ -294,8 +294,8 @@ describe("files", () => { }); it("formats date", async () => { + const utcDate = new Date(Date.UTC(2024, 10, 20, 11, 36)); - assert.equal(FilesUtils.formatDate(1732102577), "20 Nov 2024, 11:36"); - + assert.equal(FilesUtils.formatDate(1732102577), "20 Nov 2024, " + utcDate.getHours() + ":" + utcDate.getMinutes()); }) }) \ No newline at end of file diff --git a/src/components/StorageRequestSetup/Commitment.css b/src/components/StorageRequestSetup/Commitment.css new file mode 100644 index 0000000..75837d4 --- /dev/null +++ b/src/components/StorageRequestSetup/Commitment.css @@ -0,0 +1,7 @@ +.commitment { + --codex-input-group-background-color: transparent; + + span { + right: 155px; + } +} diff --git a/src/components/StorageRequestSetup/Commitment.tsx b/src/components/StorageRequestSetup/Commitment.tsx new file mode 100644 index 0000000..f6c2f4a --- /dev/null +++ b/src/components/StorageRequestSetup/Commitment.tsx @@ -0,0 +1,76 @@ +import { InputGroup, Tooltip } from "@codex-storage/marketplace-ui-components"; +import "../CardNumbers/CardNumbers.css"; +import "./Commitment.css"; + +import { ChangeEvent, useState } from "react"; +import { classnames } from "../../utils/classnames"; +import InfoIcon from "../../assets/icons/info.svg?react"; +import { attributes } from "../../utils/attributes"; +import { Times } from "../../utils/times"; + +type Props = { + value: string; + onChange: (value: string) => void; + onValidation?: (value: string) => string; +}; + +export function Commitment({ value, onValidation, onChange }: Props) { + const [error, setError] = useState(""); + + const unitValue = Times.unit(parseFloat(value)); + const val = parseFloat(value) / Times.value(unitValue); + + const onValueChange = (e: ChangeEvent) => { + console.info("e.currentTarget.value", e.currentTarget.value); + onValueOrUnitChange( + (parseFloat(e.currentTarget.value) * Times.value(unitValue)).toFixed(1) + ); + }; + const onUnitChange = (e: ChangeEvent) => { + onValueOrUnitChange( + Times.value(e.currentTarget.value as "days" | "months").toFixed(1) + ); + }; + + const onValueOrUnitChange = (val: string) => { + onChange(val); + + const msg = onValidation?.(val); + + if (msg) { + setError(msg); + return; + } + + setError(""); + }; + + console.info(val); + + return ( +
    + + + + + + {"Contract duration"} +
    + ); +} diff --git a/src/components/StorageRequestSetup/StorageRequestCreate.tsx b/src/components/StorageRequestSetup/StorageRequestCreate.tsx index 972f00c..9378f54 100644 --- a/src/components/StorageRequestSetup/StorageRequestCreate.tsx +++ b/src/components/StorageRequestSetup/StorageRequestCreate.tsx @@ -22,8 +22,7 @@ const CONFIRM_STATE = 2; const defaultStorageRequest: StorageRequest = { cid: "", - availabilityUnit: "months", - availability: 1, + availability: Times.value("days"), tolerance: 1, proofProbability: 1, nodes: 3, @@ -43,7 +42,7 @@ export function StorageRequestCreate() { useEffect(() => { Promise.all([ WebStorage.get("storage-request-step"), - WebStorage.get("storage-request"), + WebStorage.get("storage-request-2"), ]).then(([s, data]) => { if (s) { dispatch({ @@ -83,11 +82,10 @@ export function StorageRequestCreate() { WebStorage.set("storage-request-step", step); if (step == CONFIRM_STATE) { - const { availabilityUnit, availability, expiration, ...rest } = - storageRequest; + const { availability, expiration, ...rest } = storageRequest; mutateAsync({ ...rest, - duration: Times.toSeconds(availability, availabilityUnit), + duration: availability, expiry: expiration * 60, }); } else { @@ -101,7 +99,7 @@ export function StorageRequestCreate() { const onStorageRequestChange = (data: Partial) => { const val = { ...storageRequest, ...data }; - WebStorage.set("storage-request", val); + WebStorage.set("storage-request-2", val); setStorageRequest(val); }; diff --git a/src/components/StorageRequestSetup/StorageRequestReview.tsx b/src/components/StorageRequestSetup/StorageRequestReview.tsx index 639a0f7..90e64be 100644 --- a/src/components/StorageRequestSetup/StorageRequestReview.tsx +++ b/src/components/StorageRequestSetup/StorageRequestReview.tsx @@ -14,6 +14,7 @@ import CommitmentIcon from "../../assets/icons/commitment.svg?react"; import RequestDurationIcon from "../../assets/icons/request-duration.svg?react"; import { attributes } from "../../utils/attributes"; import { Strings } from "../../utils/strings"; +import { Commitment } from "./Commitment"; type Durability = { nodes: number; @@ -139,14 +140,6 @@ export function StorageRequestReview({ return error; } - // if (!unit.endsWith("s")) { - // unit += "s"; - // } - - // if (!units.includes(unit)) { - // return "Invalid unit must one of: minutes, hours, days, months, years"; - // } - return ""; }; @@ -165,13 +158,8 @@ export function StorageRequestReview({ const onAvailabilityChange = (value: string) => { const [availability] = value.split(" "); - // if (!availabilityUnit.endsWith("s")) { - // availabilityUnit += "s"; - // } - onStorageRequestChange({ availability: Number(availability), - availabilityUnit: "months", }); }; @@ -184,18 +172,6 @@ export function StorageRequestReview({ const onCollateralChange = (value: string) => onStorageRequestChange({ collateral: Number(value) }); - // const pluralizeUnit = () => { - // if (data.availability > 1 && !data.availabilityUnit.endsWith("s")) { - // return data.availability + " " +data.availabilityUnit + "s"; - // } - - // if (data.availability <= 1 && data.availabilityUnit.endsWith("s")) { - // return data.availabilityUnit.slice(0, -1); - // } - - // return data.availabilityUnit; - // }; - const availability = storageRequest.availability; return ( @@ -286,14 +262,10 @@ export function StorageRequestReview({
    - + onValidation={isInvalidAvailability}> { + it("display the bytes", async () => { + assert.equal(Bytes.pretty(0), "0 B"); + assert.equal(Bytes.pretty(512), "512.0 B"); + assert.equal(Bytes.pretty(1025), "1.0 KB"); + assert.equal(Bytes.pretty(GB), "1.0 GB"); + }); +}) \ No newline at end of file diff --git a/src/utils/bytes.ts b/src/utils/bytes.ts index 9bc5a22..a3171fe 100644 --- a/src/utils/bytes.ts +++ b/src/utils/bytes.ts @@ -1,14 +1,14 @@ export const Bytes = { pretty(bytes: number) { - const sizes = ["bytes", "KB", "MB", "GB", "TB"]; + const sizes = ["B", "KB", "MB", "GB", "TB"]; if (bytes == 0) { - return "0 b"; + return "0 B"; } const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)).toString()); if (i == 0) { - return bytes + " " + sizes[i]; + return bytes.toFixed(1) + " " + sizes[i]; } return (bytes / Math.pow(1024, i)).toFixed(1) + " " + sizes[i]; diff --git a/src/utils/times.test.ts b/src/utils/times.test.ts new file mode 100644 index 0000000..e2cb3e2 --- /dev/null +++ b/src/utils/times.test.ts @@ -0,0 +1,28 @@ +import { assert, describe, it } from "vitest"; +import { Times } from "./times"; + +describe("times", () => { + it("display the times", async () => { + assert.equal(Times.pretty(0), "0 second"); + assert.equal(Times.pretty(2), "2 seconds"); + assert.equal(Times.pretty(60), "1 minute"); + assert.equal(Times.pretty(90), "1.5 minutes"); + assert.equal(Times.pretty(3600), "1 hour"); + assert.equal(Times.pretty(3600 * 2), "2 hours"); + assert.equal(Times.pretty(3600 * 24), "1 day"); + assert.equal(Times.pretty(3600 * 36), "1.5 days"); + assert.equal(Times.pretty(3600 * 24 * 30), "1 month"); + }); + + it("guess the time unit", async () => { + assert.equal(Times.unit(0), "hours"); + assert.equal(Times.unit(3600 * 24), "days"); + assert.equal(Times.unit(3600 * 24 * 30), "months"); + }) + + it("get the seconds for a time unit given", async () => { + assert.equal(Times.value("hours"), 3600); + assert.equal(Times.value("days"), 3600 * 24); + assert.equal(Times.value("months"), 3600 * 24 * 30); + }) +}) \ No newline at end of file diff --git a/src/utils/times.ts b/src/utils/times.ts index 1ff5c4d..055513b 100644 --- a/src/utils/times.ts +++ b/src/utils/times.ts @@ -6,31 +6,33 @@ export type TimesUnit = | "hours" | "seconds"; -const plural = (value: number, unit: TimesUnit) => - value > 1 ? value + ` ${unit}` : value + ` ${unit.slice(0, -1)}`; +const plural = (value: number, unit: TimesUnit) => { + const val = Number.isInteger(value) ? value : value.toFixed(1) + return value > 1 ? val + ` ${unit}` : val + ` ${unit.slice(0, -1)}`; +} export const Times = { toSeconds(value: number, unit: TimesUnit) { - let seconds = value; + let val = value; /* eslint-disable no-fallthrough */ switch (unit) { // @ts-expect-error - We don't want to break case "years": - seconds *= 365; + val *= 365; // @ts-expect-error - We don't want to break case "months": - seconds *= 30; + val *= 30; // @ts-expect-error - We don't want to break case "days": - seconds *= 24; + val *= 24; // @ts-expect-error - We don't want to break case "hours": - seconds *= 60; + val *= 60; case "minutes": - seconds *= 60; + val *= 60; } - return seconds; + return val; }, pretty(value: number) { @@ -62,7 +64,7 @@ export const Times = { return plural(value, "seconds"); }, - unit(value: number) { + unit(value: number): "months" | "days" | "hours" { let seconds = 30 * 24 * 60 * 60; if (value >= seconds) { @@ -77,7 +79,7 @@ export const Times = { return "hours" }, - unitValue(unit: "hours" | "days" | "months") { + value(unit: "hours" | "days" | "months") { switch (unit) { case "months": { return 30 * 24 * 60 * 60 @@ -90,4 +92,5 @@ export const Times = { } } } + }; From cd35972992270d980021ceb9945ea6f52ff597ea Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 27 Nov 2024 11:41:37 +0100 Subject: [PATCH 04/17] Fix the contract duration unit --- e2e/storage-requests.spec.ts | 7 +++-- .../StorageRequestSetup/Commitment.tsx | 28 ++++++------------- .../StorageRequestCreate.tsx | 12 ++++---- .../StorageRequestReview.tsx | 4 ++- src/components/StorageRequestSetup/types.ts | 2 +- .../useStorageRequestMutation.ts | 2 +- 6 files changed, 25 insertions(+), 30 deletions(-) diff --git a/e2e/storage-requests.spec.ts b/e2e/storage-requests.spec.ts index 55300e8..c6f097c 100644 --- a/e2e/storage-requests.spec.ts +++ b/e2e/storage-requests.spec.ts @@ -89,8 +89,10 @@ test('create a storage request by using decimal values', async ({ page }) => { await expect(page.getByText('Success, the CID has been')).toBeVisible(); await page.getByRole('button', { name: 'Next' }).click(); + await expect(page.getByRole('combobox')).toHaveValue("days"); const value = (Math.random() * 10); await page.getByLabel("Full period of the contract").fill(value.toFixed(1)) + await expect(page.getByRole('combobox')).toHaveValue("days"); await page.getByRole('button', { name: 'Next' }).click(); await expect(page.getByText('Your request is being processed.')).toBeVisible(); @@ -113,12 +115,11 @@ test('create a storage request by using months', async ({ page }) => { await page.getByLabel("Full period of the contract").fill("3") await page.getByRole('combobox').selectOption('months'); - await expect(page.getByLabel("Full period of the contract")).toHaveValue("1") - await page.getByLabel("Full period of the contract").fill("2") + await expect(page.getByLabel("Full period of the contract")).toHaveValue("3") await page.getByRole('button', { name: 'Next' }).click(); await expect(page.getByText('Your request is being processed.')).toBeVisible(); await page.getByRole('button', { name: 'Finish' }).click(); await expect(page.getByText('No data.')).not.toBeVisible(); - await expect(page.getByText("2 months").first()).toBeVisible(); + await expect(page.getByText("3 months").first()).toBeVisible(); }) \ No newline at end of file diff --git a/src/components/StorageRequestSetup/Commitment.tsx b/src/components/StorageRequestSetup/Commitment.tsx index f6c2f4a..2a3cc88 100644 --- a/src/components/StorageRequestSetup/Commitment.tsx +++ b/src/components/StorageRequestSetup/Commitment.tsx @@ -6,34 +6,26 @@ import { ChangeEvent, useState } from "react"; import { classnames } from "../../utils/classnames"; import InfoIcon from "../../assets/icons/info.svg?react"; import { attributes } from "../../utils/attributes"; -import { Times } from "../../utils/times"; type Props = { + unit: "months" | "days"; value: string; - onChange: (value: string) => void; + onChange: (value: string, unit: "months" | "days") => void; onValidation?: (value: string) => string; }; -export function Commitment({ value, onValidation, onChange }: Props) { +export function Commitment({ unit, value, onValidation, onChange }: Props) { const [error, setError] = useState(""); - const unitValue = Times.unit(parseFloat(value)); - const val = parseFloat(value) / Times.value(unitValue); - const onValueChange = (e: ChangeEvent) => { - console.info("e.currentTarget.value", e.currentTarget.value); - onValueOrUnitChange( - (parseFloat(e.currentTarget.value) * Times.value(unitValue)).toFixed(1) - ); + onValueOrUnitChange(e.currentTarget.value, unit); }; const onUnitChange = (e: ChangeEvent) => { - onValueOrUnitChange( - Times.value(e.currentTarget.value as "days" | "months").toFixed(1) - ); + onValueOrUnitChange(value, e.currentTarget.value as "months" | "days"); }; - const onValueOrUnitChange = (val: string) => { - onChange(val); + const onValueOrUnitChange = (val: string, unit: "months" | "days") => { + onChange(val, unit); const msg = onValidation?.(val); @@ -45,8 +37,6 @@ export function Commitment({ value, onValidation, onChange }: Props) { setError(""); }; - console.info(val); - return (
    diff --git a/src/components/StorageRequestSetup/StorageRequestCreate.tsx b/src/components/StorageRequestSetup/StorageRequestCreate.tsx index 9378f54..7367022 100644 --- a/src/components/StorageRequestSetup/StorageRequestCreate.tsx +++ b/src/components/StorageRequestSetup/StorageRequestCreate.tsx @@ -22,7 +22,8 @@ const CONFIRM_STATE = 2; const defaultStorageRequest: StorageRequest = { cid: "", - availability: Times.value("days"), + availability: 1, + availabilityUnit: "days", tolerance: 1, proofProbability: 1, nodes: 3, @@ -42,7 +43,7 @@ export function StorageRequestCreate() { useEffect(() => { Promise.all([ WebStorage.get("storage-request-step"), - WebStorage.get("storage-request-2"), + WebStorage.get("storage-request-3"), ]).then(([s, data]) => { if (s) { dispatch({ @@ -82,10 +83,11 @@ export function StorageRequestCreate() { WebStorage.set("storage-request-step", step); if (step == CONFIRM_STATE) { - const { availability, expiration, ...rest } = storageRequest; + const { availability, availabilityUnit, expiration, ...rest } = + storageRequest; mutateAsync({ ...rest, - duration: availability, + duration: availability * Times.value(availabilityUnit), expiry: expiration * 60, }); } else { @@ -99,7 +101,7 @@ export function StorageRequestCreate() { const onStorageRequestChange = (data: Partial) => { const val = { ...storageRequest, ...data }; - WebStorage.set("storage-request-2", val); + WebStorage.set("storage-request-3", val); setStorageRequest(val); }; diff --git a/src/components/StorageRequestSetup/StorageRequestReview.tsx b/src/components/StorageRequestSetup/StorageRequestReview.tsx index 90e64be..cc0008e 100644 --- a/src/components/StorageRequestSetup/StorageRequestReview.tsx +++ b/src/components/StorageRequestSetup/StorageRequestReview.tsx @@ -155,11 +155,12 @@ export function StorageRequestReview({ const onProofProbabilityChange = (value: string) => onUpdateDurability({ proofProbability: Number(value) }); - const onAvailabilityChange = (value: string) => { + const onAvailabilityChange = (value: string, unit: "days" | "months") => { const [availability] = value.split(" "); onStorageRequestChange({ availability: Number(availability), + availabilityUnit: unit, }); }; @@ -263,6 +264,7 @@ export function StorageRequestReview({
    diff --git a/src/components/StorageRequestSetup/types.ts b/src/components/StorageRequestSetup/types.ts index b03d333..c459698 100644 --- a/src/components/StorageRequestSetup/types.ts +++ b/src/components/StorageRequestSetup/types.ts @@ -20,10 +20,10 @@ export type StorageAvailabilityValue = { value: number; }; - export type StorageRequest = { cid: string; availability: number; + availabilityUnit: "months" | "days"; tolerance: number; proofProbability: number; nodes: number; diff --git a/src/components/StorageRequestSetup/useStorageRequestMutation.ts b/src/components/StorageRequestSetup/useStorageRequestMutation.ts index bee2692..322a865 100644 --- a/src/components/StorageRequestSetup/useStorageRequestMutation.ts +++ b/src/components/StorageRequestSetup/useStorageRequestMutation.ts @@ -29,7 +29,7 @@ export function useStorageRequestMutation( // } WebStorage.delete("storage-request-step"); - WebStorage.delete("storage-request-2"); + WebStorage.delete("storage-request-3"); setError(null); From 174dcf78d9d076abd9e9a7e4767f8b415b38a023 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 27 Nov 2024 13:40:38 +0100 Subject: [PATCH 05/17] Update the availability duration and size input --- e2e/availabilities.spec.ts | 69 ++++++++++++++++++- .../Availability/AvailabilityEdit.tsx | 28 ++------ .../Availability/AvailabilityForm.tsx | 38 +++++----- .../Availability/availability.utils.ts | 3 +- .../Availability/useAvailabilityMutation.ts | 8 ++- 5 files changed, 95 insertions(+), 51 deletions(-) diff --git a/e2e/availabilities.spec.ts b/e2e/availabilities.spec.ts index c64a67d..6203df7 100644 --- a/e2e/availabilities.spec.ts +++ b/e2e/availabilities.spec.ts @@ -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(); +}) \ No newline at end of file diff --git a/src/components/Availability/AvailabilityEdit.tsx b/src/components/Availability/AvailabilityEdit.tsx index c6780b1..260bf73 100644 --- a/src/components/Availability/AvailabilityEdit.tsx +++ b/src/components/Availability/AvailabilityEdit.tsx @@ -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("availability-step"), - WebStorage.get("availability"), + WebStorage.get("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); diff --git a/src/components/Availability/AvailabilityForm.tsx b/src/components/Availability/AvailabilityForm.tsx index f8b1a7b..fcf42a3 100644 --- a/src/components/Availability/AvailabilityForm.tsx +++ b/src/components/Availability/AvailabilityForm.tsx @@ -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) => { 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) => { 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) => { 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 (
    @@ -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"], diff --git a/src/components/Availability/availability.utils.ts b/src/components/Availability/availability.utils.ts index e73b1ee..2e3ac42 100644 --- a/src/components/Availability/availability.utils.ts +++ b/src/components/Availability/availability.utils.ts @@ -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 diff --git a/src/components/Availability/useAvailabilityMutation.ts b/src/components/Availability/useAvailabilityMutation.ts index 64a8838..b79b2aa 100644 --- a/src/components/Availability/useAvailabilityMutation.ts +++ b/src/components/Availability/useAvailabilityMutation.ts @@ -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) { From d5760201c697198a233a447c8288ab29732f0b0a Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 27 Nov 2024 13:41:53 +0100 Subject: [PATCH 06/17] Fix lint --- src/components/Availability/AvailabilityEdit.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Availability/AvailabilityEdit.tsx b/src/components/Availability/AvailabilityEdit.tsx index 260bf73..46acddd 100644 --- a/src/components/Availability/AvailabilityEdit.tsx +++ b/src/components/Availability/AvailabilityEdit.tsx @@ -10,7 +10,7 @@ import { CodexNodeSpace } from "@codex-storage/sdk-js"; import { AvailabilityConfirm } from "./AvailabilityConfirmation"; import { WebStorage } from "../../utils/web-storage"; import { AvailabilityState } from "./types"; -import { GB, STEPPER_DURATION } from "../../utils/constants"; +import { STEPPER_DURATION } from "../../utils/constants"; import { useAvailabilityMutation } from "./useAvailabilityMutation"; import { AvailabilitySuccess } from "./AvailabilitySuccess"; import { AvailabilityError } from "./AvailabilityError"; From 492e2587cbc18eee6876e9c6bb835552c35cebfb Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 27 Nov 2024 13:43:51 +0100 Subject: [PATCH 07/17] Fix unit test --- src/components/Availability/availability.utils.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Availability/availability.utils.test.ts b/src/components/Availability/availability.utils.test.ts index 645ed75..3e11349 100644 --- a/src/components/Availability/availability.utils.test.ts +++ b/src/components/Availability/availability.utils.test.ts @@ -173,7 +173,7 @@ describe("files", () => { quotaUsedBytes: GB, totalBlocks: 0 } - assert.deepEqual(AvailabilityUtils.maxValue(space), 5 * GB); + assert.deepEqual(AvailabilityUtils.maxValue(space), 5 * GB - 1); }) it("checks the availability max value", async () => { From 1b9bc616474d5430e3a3e7493eb57a857a2b539e Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 27 Nov 2024 13:55:29 +0100 Subject: [PATCH 08/17] Update codex version --- .github/workflows/playwright.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 08f81bb..ed7f7de 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -8,9 +8,9 @@ on: workflow_dispatch: env: - codex_version: v0.1.8 - circuit_version: v0.1.8 - marketplace_address: "0x5Bd66fA15Eb0E546cd26808248867a572cFF5706" + codex_version: v0.1.9 + circuit_version: v0.1.9 + marketplace_address: "0xAB03b6a58C5262f530D54146DA2a552B1C0F7648" eth_provider: "https://rpc.testnet.codex.storage" VITE_CODEX_API_URL: ${{ secrets.VITE_CODEX_API_URL }} VITE_GEO_IP_URL: ${{ secrets.VITE_GEO_IP_URL }} From 30726228bd243ee3c1dc42285512c66b32f365fa Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 27 Nov 2024 15:38:07 +0100 Subject: [PATCH 09/17] Fix stepper validation issue --- e2e/availabilities.spec.ts | 4 ++-- src/components/Availability/availability.utils.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/e2e/availabilities.spec.ts b/e2e/availabilities.spec.ts index 6203df7..ff43c45 100644 --- a/e2e/availabilities.spec.ts +++ b/e2e/availabilities.spec.ts @@ -8,9 +8,9 @@ test('create an availability', async ({ page }) => { await page.locator('.availability-edit button').first().click(); await page.getByLabel('Total size').click(); - const value = (Math.random() * 0.5); + const value = (Math.random() * 0.5) + 0.1; - await page.getByLabel('Total size').fill(value.toFixed(2)); + 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(); diff --git a/src/components/Availability/availability.utils.ts b/src/components/Availability/availability.utils.ts index 2e3ac42..50bf72f 100644 --- a/src/components/Availability/availability.utils.ts +++ b/src/components/Availability/availability.utils.ts @@ -48,7 +48,7 @@ export const AvailabilityUtils = { isValid: ( availability: AvailabilityState, max: number - ) => availability.totalSize > 0 && availability.totalSize <= max + ) => availability.totalSize > 0 && availability.totalSize * AvailabilityUtils.unitValue(availability.totalSizeUnit) <= max , toggle: (arr: Array, value: T) => arr.includes(value) ? arr.filter(i => i !== value) : [...arr, value], From 7bb34cee446200bef74c477f9c081739873a12a4 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 27 Nov 2024 15:43:56 +0100 Subject: [PATCH 10/17] Fix unit test --- src/components/Availability/availability.utils.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Availability/availability.utils.test.ts b/src/components/Availability/availability.utils.test.ts index 3e11349..2e654ff 100644 --- a/src/components/Availability/availability.utils.test.ts +++ b/src/components/Availability/availability.utils.test.ts @@ -178,11 +178,13 @@ describe("files", () => { it("checks the availability max value", async () => { const availability = { - totalSize: GB + totalSizeUnit: "gb", + totalSize: 1 } as AvailabilityState + assert.deepEqual(AvailabilityUtils.isValid(availability, GB * 2), true); assert.deepEqual(AvailabilityUtils.isValid({ ...availability, totalSize: -1 }, GB), false); - assert.deepEqual(AvailabilityUtils.isValid({ ...availability, totalSize: 2 * GB }, GB), false); + assert.deepEqual(AvailabilityUtils.isValid({ ...availability, totalSize: GB }, 2 * GB), false); }) it("toggles item in array", async () => { From cef126a06373efb2819fd06f778d5e8b226383ed Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 27 Nov 2024 17:21:36 +0100 Subject: [PATCH 11/17] Use the week starting dates --- src/components/ConnectedAccount/WalletCard.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/ConnectedAccount/WalletCard.tsx b/src/components/ConnectedAccount/WalletCard.tsx index e88983e..ccfc911 100644 --- a/src/components/ConnectedAccount/WalletCard.tsx +++ b/src/components/ConnectedAccount/WalletCard.tsx @@ -48,9 +48,20 @@ export function WalletCard({ tab }: Props) { }; if (chart.current) { + const today = new Date(); + const startOfWeek = today.getDate() - today.getDay() + 1; + const startDates = []; + + today.setDate(startOfWeek); + + for (let i = 0; i < 5; i++) { + startDates.push(today.toISOString().split("T")[0]); + today.setDate(today.getDate() + 7); + } + const data = { daily: ["MON", "TUE", "WED", "THU", "WED", "SAT", "SUN"], - weekly: ["1", "2", "3", "4", "5", "6"], + weekly: startDates, monthly: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN"], }[tab]; From 1f4e7d075f73cb38b79a9d8785f98ac26526a28c Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 27 Nov 2024 18:53:10 +0100 Subject: [PATCH 12/17] Add other currencies to the wallet --- public/icons/aud-flag.svg | 85 +++++++++++++++++++ public/icons/btc-flag.svg | 28 ++++++ public/icons/cad-flag.svg | 24 ++++++ public/icons/cny-flag.svg | 45 ++++++++++ public/icons/eth-flag.svg | 46 ++++++++++ .../ConnectedAccount/WalletCard.css | 30 +++++-- .../ConnectedAccount/WalletCard.tsx | 19 ++++- 7 files changed, 269 insertions(+), 8 deletions(-) create mode 100644 public/icons/aud-flag.svg create mode 100644 public/icons/btc-flag.svg create mode 100644 public/icons/cad-flag.svg create mode 100644 public/icons/cny-flag.svg create mode 100644 public/icons/eth-flag.svg diff --git a/public/icons/aud-flag.svg b/public/icons/aud-flag.svg new file mode 100644 index 0000000..e7917dc --- /dev/null +++ b/public/icons/aud-flag.svg @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/btc-flag.svg b/public/icons/btc-flag.svg new file mode 100644 index 0000000..30f600e --- /dev/null +++ b/public/icons/btc-flag.svg @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/public/icons/cad-flag.svg b/public/icons/cad-flag.svg new file mode 100644 index 0000000..6b1ea17 --- /dev/null +++ b/public/icons/cad-flag.svg @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/public/icons/cny-flag.svg b/public/icons/cny-flag.svg new file mode 100644 index 0000000..053f46c --- /dev/null +++ b/public/icons/cny-flag.svg @@ -0,0 +1,45 @@ + + + + + + + + diff --git a/public/icons/eth-flag.svg b/public/icons/eth-flag.svg new file mode 100644 index 0000000..3ad1c3e --- /dev/null +++ b/public/icons/eth-flag.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + diff --git a/src/components/ConnectedAccount/WalletCard.css b/src/components/ConnectedAccount/WalletCard.css index b4b6b28..662887a 100644 --- a/src/components/ConnectedAccount/WalletCard.css +++ b/src/components/ConnectedAccount/WalletCard.css @@ -147,18 +147,38 @@ box-shadow: 0 0 0 3px rgba(150, 150, 150, 0.2); } - &:has(option[value="USD"]:checked) { - background-image: url(/icons/us-flag.svg); + &:has(option:checked) { background-position: 10px; background-repeat: no-repeat; background-size: 16px; } + &:has(option[value="USD"]:checked) { + background-image: url(/icons/us-flag.svg); + } + &:has(option[value="EUR"]:checked) { background-image: url(/icons/euro-flag.svg); - background-position: 10px; - background-repeat: no-repeat; - background-size: 16px; + } + + &:has(option[value="AUD"]:checked) { + background-image: url(/icons/aud-flag.svg); + } + + &:has(option[value="CAD"]:checked) { + background-image: url(/icons/cad-flag.svg); + } + + &:has(option[value="CNY"]:checked) { + background-image: url(/icons/cny-flag.svg); + } + + &:has(option[value="BTC"]:checked) { + background-image: url(/icons/btc-flag.svg); + } + + &:has(option[value="ETH"]:checked) { + background-image: url(/icons/eth-flag.svg); } option { diff --git a/src/components/ConnectedAccount/WalletCard.tsx b/src/components/ConnectedAccount/WalletCard.tsx index ccfc911..6f2d3e7 100644 --- a/src/components/ConnectedAccount/WalletCard.tsx +++ b/src/components/ConnectedAccount/WalletCard.tsx @@ -39,11 +39,18 @@ export function WalletCard({ tab }: Props) { if (value === "USD") { setTokenValue(1540); - } else { + } else if (["BTC", "ETH"].includes(value) === false) { const json = await fetch( "https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/usd.json" ).then((res) => res.json()); - setTokenValue(defaultTokenValue * json.usd.eur); + setTokenValue(defaultTokenValue * json.usd[value.toLocaleLowerCase()]); + } else { + const json = await fetch( + "https://api.coinbase.com/v2/prices/" + + value.toLocaleLowerCase() + + "-USD/spot.json" + ).then((res) => res.json()); + setTokenValue(defaultTokenValue / json.data.amount); } }; @@ -135,14 +142,20 @@ export function WalletCard({ tab }: Props) { TOKEN
    - {tokenValue.toFixed(2)} {currency} + {tokenValue.toFixed(["BTC", "ETH"].includes(currency) ? 8 : 2)}{" "} + {currency} + 5%
    From e6e7730d920a2c55193a394c3e3cdd0b1a313e6c Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 27 Nov 2024 19:59:02 +0100 Subject: [PATCH 13/17] Limit the contract duration to 7 for the testnet --- e2e/storage-requests.spec.ts | 50 +++++++++++-------- .../StorageRequestSetup/Commitment.tsx | 6 ++- .../StorageRequestReview.tsx | 23 +++++++-- 3 files changed, 52 insertions(+), 27 deletions(-) diff --git a/e2e/storage-requests.spec.ts b/e2e/storage-requests.spec.ts index c6f097c..999d057 100644 --- a/e2e/storage-requests.spec.ts +++ b/e2e/storage-requests.spec.ts @@ -89,10 +89,18 @@ test('create a storage request by using decimal values', async ({ page }) => { await expect(page.getByText('Success, the CID has been')).toBeVisible(); await page.getByRole('button', { name: 'Next' }).click(); - await expect(page.getByRole('combobox')).toHaveValue("days"); + await page.getByLabel("Full period of the contract").fill("10") + await expect(page.locator('footer .button--primary')).toHaveAttribute("disabled"); + + await page.getByLabel("Full period of the contract").fill("1") + await expect(page.locator('footer .button--primary')).not.toHaveAttribute("disabled"); + + await page.getByLabel("Full period of the contract").fill("0") + await expect(page.locator('footer .button--primary')).toHaveAttribute("disabled"); + const value = (Math.random() * 10); await page.getByLabel("Full period of the contract").fill(value.toFixed(1)) - await expect(page.getByRole('combobox')).toHaveValue("days"); + await expect(page.locator('footer .button--primary')).not.toHaveAttribute("disabled"); await page.getByRole('button', { name: 'Next' }).click(); await expect(page.getByText('Your request is being processed.')).toBeVisible(); @@ -101,25 +109,25 @@ test('create a storage request by using decimal values', async ({ page }) => { await expect(page.getByText(value.toFixed(1) + " days").first()).toBeVisible(); }) -test('create a storage request by using months', async ({ page }) => { - await page.goto('/dashboard'); - await page.locator('a').filter({ hasText: 'Purchases' }).click(); - await page.getByRole('button', { name: 'Storage Request' }).click(); +// test('create a storage request by using months', async ({ page }) => { +// await page.goto('/dashboard'); +// await page.locator('a').filter({ hasText: 'Purchases' }).click(); +// await page.getByRole('button', { name: 'Storage Request' }).click(); - await page.locator('div').getByTestId("upload").setInputFiles([ - path.join(__dirname, "assets", 'chatgpt.jpg'), - ]); - await expect(page.locator('#cid')).not.toBeEmpty() - await expect(page.getByText('Success, the CID has been')).toBeVisible(); - await page.getByRole('button', { name: 'Next' }).click(); +// await page.locator('div').getByTestId("upload").setInputFiles([ +// path.join(__dirname, "assets", 'chatgpt.jpg'), +// ]); +// await expect(page.locator('#cid')).not.toBeEmpty() +// await expect(page.getByText('Success, the CID has been')).toBeVisible(); +// await page.getByRole('button', { name: 'Next' }).click(); - await page.getByLabel("Full period of the contract").fill("3") - await page.getByRole('combobox').selectOption('months'); - await expect(page.getByLabel("Full period of the contract")).toHaveValue("3") +// await page.getByLabel("Full period of the contract").fill("3") +// await page.getByRole('combobox').selectOption('months'); +// await expect(page.getByLabel("Full period of the contract")).toHaveValue("3") - await page.getByRole('button', { name: 'Next' }).click(); - await expect(page.getByText('Your request is being processed.')).toBeVisible(); - await page.getByRole('button', { name: 'Finish' }).click(); - await expect(page.getByText('No data.')).not.toBeVisible(); - await expect(page.getByText("3 months").first()).toBeVisible(); -}) \ No newline at end of file +// await page.getByRole('button', { name: 'Next' }).click(); +// await expect(page.getByText('Your request is being processed.')).toBeVisible(); +// await page.getByRole('button', { name: 'Finish' }).click(); +// await expect(page.getByText('No data.')).not.toBeVisible(); +// await expect(page.getByText("3 months").first()).toBeVisible(); +// }) \ No newline at end of file diff --git a/src/components/StorageRequestSetup/Commitment.tsx b/src/components/StorageRequestSetup/Commitment.tsx index 2a3cc88..74c3b50 100644 --- a/src/components/StorageRequestSetup/Commitment.tsx +++ b/src/components/StorageRequestSetup/Commitment.tsx @@ -14,6 +14,8 @@ type Props = { onValidation?: (value: string) => string; }; +const TESTNET_MAX_VALUE = 7; + export function Commitment({ unit, value, onValidation, onChange }: Props) { const [error, setError] = useState(""); @@ -50,9 +52,11 @@ export function Commitment({ unit, value, onValidation, onChange }: Props) { onChange={onValueChange} onGroupChange={onUnitChange} value={value} + min={0} + max={TESTNET_MAX_VALUE} group={[ ["days", "days"], - ["months", "months"], + // ["months", "months"], ]} groupValue={unit} /> diff --git a/src/components/StorageRequestSetup/StorageRequestReview.tsx b/src/components/StorageRequestSetup/StorageRequestReview.tsx index cc0008e..81daec1 100644 --- a/src/components/StorageRequestSetup/StorageRequestReview.tsx +++ b/src/components/StorageRequestSetup/StorageRequestReview.tsx @@ -28,6 +28,8 @@ const durabilities = [ { nodes: 5, tolerance: 2, proofProbability: 4 }, ]; +const TESTNET_MAX_VALUE = 7; + const findDurabilityIndex = (d: Durability) => { const s = JSON.stringify({ nodes: d.nodes, @@ -58,10 +60,10 @@ export function StorageRequestReview({ ); useEffect(() => { - const invalid = isInvalidConstrainst( - storageRequest.nodes, - storageRequest.tolerance - ); + const invalid = + isInvalidConstrainst(storageRequest.nodes, storageRequest.tolerance) || + storageRequest.availability > TESTNET_MAX_VALUE || + storageRequest.availability == 0; dispatch({ type: "toggle-buttons", @@ -134,7 +136,10 @@ export function StorageRequestReview({ const isInvalidAvailability = (data: string) => { const [value] = data.split(" "); - const error = isInvalidNumber(value); + const error = + isInvalidNumber(value) || + isInvalidAvailabilityNumber(value) || + isRequiredNumber(value); if (error) { return error; @@ -146,6 +151,14 @@ export function StorageRequestReview({ const isInvalidNumber = (value: string) => isNaN(Number(value)) ? "The value is not a number" : ""; + const isRequiredNumber = (value: string) => + value == "0" ? "The value has to be more than 0." : ""; + + const isInvalidAvailabilityNumber = (value: string) => + parseInt(value, 10) > TESTNET_MAX_VALUE + ? "The maximum value is on the current Testnet is 7 days" + : ""; + const onNodesChange = (value: string) => onUpdateDurability({ nodes: Number(value) }); From 40c0b84e5cfca6e5a242b84fc270cafe8b068655 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 27 Nov 2024 20:32:27 +0100 Subject: [PATCH 14/17] Fix tests --- e2e/storage-requests.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/storage-requests.spec.ts b/e2e/storage-requests.spec.ts index 999d057..a04263d 100644 --- a/e2e/storage-requests.spec.ts +++ b/e2e/storage-requests.spec.ts @@ -98,7 +98,7 @@ test('create a storage request by using decimal values', async ({ page }) => { await page.getByLabel("Full period of the contract").fill("0") await expect(page.locator('footer .button--primary')).toHaveAttribute("disabled"); - const value = (Math.random() * 10); + const value = (Math.random() * 7); await page.getByLabel("Full period of the contract").fill(value.toFixed(1)) await expect(page.locator('footer .button--primary')).not.toHaveAttribute("disabled"); From 333cd5b71e37d5388c194046061a24189488c74b Mon Sep 17 00:00:00 2001 From: Arnaud Date: Thu, 28 Nov 2024 16:16:50 +0100 Subject: [PATCH 15/17] Fix misleading title #88 --- src/components/StorageRequestSetup/StorageRequestReview.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/StorageRequestSetup/StorageRequestReview.tsx b/src/components/StorageRequestSetup/StorageRequestReview.tsx index 81daec1..787e382 100644 --- a/src/components/StorageRequestSetup/StorageRequestReview.tsx +++ b/src/components/StorageRequestSetup/StorageRequestReview.tsx @@ -282,13 +282,13 @@ export function StorageRequestReview({ onChange={onAvailabilityChange} onValidation={isInvalidAvailability}> + title="Penality tokens"> + title="Reward tokens for hosts">
    From 6d843d08bd2eb463bf7d431c4cce85734dbcaf1f Mon Sep 17 00:00:00 2001 From: Arnaud Date: Thu, 28 Nov 2024 16:17:07 +0100 Subject: [PATCH 16/17] Fix response style --- package-lock.json | 8 +++--- package.json | 4 +-- src/components/AppBar/AppBar.tsx | 4 +-- src/components/AppBar/appBar.css | 8 +++++- src/components/Availability/Sunburst.tsx | 13 +++++++++- src/components/Card/Card.css | 7 ++++++ .../ConnectedAccount/WalletCard.css | 1 - .../ConnectedAccount/WalletCard.tsx | 25 +++++++++++++++++-- src/components/LogLevel/LogLevel.css | 12 +++++++++ src/components/Menu/Menu.tsx | 11 +++++--- src/components/Menu/menu.css | 1 + .../OnBoarding/OnBoardingLayout.css | 12 +++++++++ src/components/Peers/PeersCard.css | 6 +++++ src/routes/dashboard/availabilities.css | 6 +++++ src/routes/dashboard/logs.css | 13 +++++++++- src/routes/dashboard/peers.css | 8 ++++++ src/routes/root.css | 4 +-- 17 files changed, 123 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 99cc45e..fa9b331 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.13", "license": "MIT", "dependencies": { - "@codex-storage/marketplace-ui-components": "^0.0.50", + "@codex-storage/marketplace-ui-components": "^0.0.51", "@codex-storage/sdk-js": "^0.0.16", "@sentry/browser": "^8.32.0", "@sentry/react": "^8.31.0", @@ -395,9 +395,9 @@ } }, "node_modules/@codex-storage/marketplace-ui-components": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.50.tgz", - "integrity": "sha512-pijYw8Wd/ns64Q7iSLQ444U5Q7Ql0U88OuxBycuxIOVp3obITJk6SBgdZpxHIj5kWShsUvXU/nJl0V0oKt9ieQ==", + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.51.tgz", + "integrity": "sha512-KPPFlcpx3a83WCBSLbRONrF/yr4J/ctyTfFPxMaRSMTRD1LtfIE0uPy3QxtHs6tigOts2h4DEz6Kn2ynHdfKPg==", "engines": { "node": ">=18" }, diff --git a/package.json b/package.json index 4415bb3..ec30af5 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "version": "0.0.13", "type": "module", "scripts": { - "dev": "vite --host 127.0.0.1 --port 5173", + "dev": "vite --host 0.0.0.0 --port 5173", "compile": "tsc --noEmit", "build": "tsc -b && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", @@ -26,7 +26,7 @@ "React" ], "dependencies": { - "@codex-storage/marketplace-ui-components": "^0.0.50", + "@codex-storage/marketplace-ui-components": "^0.0.51", "@codex-storage/sdk-js": "^0.0.16", "@sentry/browser": "^8.32.0", "@sentry/react": "^8.31.0", diff --git a/src/components/AppBar/AppBar.tsx b/src/components/AppBar/AppBar.tsx index 9030404..d2b6341 100644 --- a/src/components/AppBar/AppBar.tsx +++ b/src/components/AppBar/AppBar.tsx @@ -28,7 +28,7 @@ type Props = { }; const icons: Record = { - dashboard: , + dashboard: , peers: , settings: , files: , @@ -83,7 +83,7 @@ export function AppBar({ onIconClick, onExpanded }: Props) { : "var(--codex-input-color-warning)"; const icon = isMobile ? ( - onExpanded(true)}> + onExpanded(true)} width={30}> ) : ( icons[title] ); diff --git a/src/components/AppBar/appBar.css b/src/components/AppBar/appBar.css index b7201bd..6e8aa59 100644 --- a/src/components/AppBar/appBar.css +++ b/src/components/AppBar/appBar.css @@ -45,6 +45,12 @@ sans-serif; letter-spacing: -0.006em; color: rgba(150, 150, 150, 0.8); + + @media (max-width: 800px) { + & { + display: none; + } + } } > div { @@ -88,7 +94,7 @@ } @media (max-width: 800px) { - aside { + .wallet-login { display: none; } } diff --git a/src/components/Availability/Sunburst.tsx b/src/components/Availability/Sunburst.tsx index 6ae7422..8febd07 100644 --- a/src/components/Availability/Sunburst.tsx +++ b/src/components/Availability/Sunburst.tsx @@ -36,6 +36,16 @@ export function Sunburst({ availabilities, space }: Props) { } }, [chart, div]); + useEffect(() => { + const refresh = () => chart.current?.resize(); + + window.addEventListener("resize", refresh); + + return () => { + window.removeEventListener("resize", refresh); + }; + }, [chart.current]); + const data = availabilities.map((a, index) => { return { name: Strings.shortId(a.id), @@ -170,6 +180,8 @@ export function Sunburst({ availabilities, space }: Props) { }; chart.current.setOption(option); + chart.current?.resize(); + // chart.current.off("click"); // chart.current.on("click", function (params) { // // console.info(params.componentIndex); @@ -196,7 +208,6 @@ export function Sunburst({ availabilities, space }: Props) { ref={div} className="sunburst" style={{ - width: size, height: size, }}>
    ); diff --git a/src/components/Card/Card.css b/src/components/Card/Card.css index 4584204..e3e4201 100644 --- a/src/components/Card/Card.css +++ b/src/components/Card/Card.css @@ -4,6 +4,13 @@ padding: 16px; background-color: rgb(35, 35, 35); + @media (max-width: 800px) { + &, + td:nth-child(2) { + padding: 8px; + } + } + > header { display: flex; align-items: center; diff --git a/src/components/ConnectedAccount/WalletCard.css b/src/components/ConnectedAccount/WalletCard.css index 662887a..f8f4b57 100644 --- a/src/components/ConnectedAccount/WalletCard.css +++ b/src/components/ConnectedAccount/WalletCard.css @@ -225,6 +225,5 @@ .lines { height: 200; - transform: scale(1.25); } } diff --git a/src/components/ConnectedAccount/WalletCard.tsx b/src/components/ConnectedAccount/WalletCard.tsx index 6f2d3e7..ef2b472 100644 --- a/src/components/ConnectedAccount/WalletCard.tsx +++ b/src/components/ConnectedAccount/WalletCard.tsx @@ -33,6 +33,16 @@ export function WalletCard({ tab }: Props) { } }, [chart, div]); + useEffect(() => { + const refresh = () => chart.current?.resize(); + + window.addEventListener("resize", refresh); + + return () => { + window.removeEventListener("resize", refresh); + }; + }, [chart.current]); + const onCurrencyChange = async (e: ChangeEvent) => { const value = e.currentTarget.value; setCurrency(value); @@ -90,6 +100,14 @@ export function WalletCard({ tab }: Props) { type: "value", show: false, }, + grid: [ + { + left: 0, + right: 0, + top: 50, + bottom: 50, + }, + ], series: [ { data: [220, 932, 401, 934, 1290, 1330, 1450].slice(0, data.length), @@ -107,7 +125,7 @@ export function WalletCard({ tab }: Props) { }, }; - chart.current.setOption(option); + chart.current.setOption(option, true); } return ( @@ -133,7 +151,10 @@ export function WalletCard({ tab }: Props) {
    {/* */} {/* */} -
    +
    diff --git a/src/components/LogLevel/LogLevel.css b/src/components/LogLevel/LogLevel.css index b225a22..42bca48 100644 --- a/src/components/LogLevel/LogLevel.css +++ b/src/components/LogLevel/LogLevel.css @@ -7,6 +7,12 @@ > div:first-child { position: relative; + @media (max-width: 800px) { + & { + flex: 1; + } + } + svg { position: absolute; top: 11px; @@ -22,5 +28,11 @@ select { border-color: rgba(150, 150, 150, 1); padding-left: 40px; + + @media (max-width: 800px) { + & { + width: 100%; + } + } } } diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 0f5f423..e4abd90 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -16,6 +16,7 @@ import PurchaseIcon from "../../assets/icons/purchase.svg?react"; import HostIcon from "../../assets/icons/host.svg?react"; import LogsIcon from "../../assets/icons/logs.svg?react"; import SettingsIcon from "../../assets/icons/settings.svg?react"; +import CloseIcon from "../../assets/icons/close.svg?react"; import HelpIcon from "../../assets/icons/help.svg?react"; import DisclaimerIcon from "../../assets/icons/disclaimer.svg?react"; import { NavLink } from "react-router-dom"; @@ -65,6 +66,8 @@ export function Menu({ isExpanded, onExpanded }: Props) { } }; + const Icon = isMobile ? CloseIcon : ExpandIcon; + return ( <>
    - + ); diff --git a/src/components/Files/FileDetails.css b/src/components/Files/FileDetails.css index 4a8bae5..6db82d7 100644 --- a/src/components/Files/FileDetails.css +++ b/src/components/Files/FileDetails.css @@ -21,17 +21,6 @@ span { flex-grow: 1; } - - .button-icon { - background-color: rgb(47, 47, 47); - border: 1px solid rgba(150, 150, 150, 0.2); - - svg { - position: relative; - left: -3px; - top: -1px; - } - } } .preview {