From b9d5afbac5a37abf90c7ec9630bc5a12dc67189e Mon Sep 17 00:00:00 2001 From: Hussain Khalil Date: Thu, 2 Jan 2025 17:15:40 -0500 Subject: [PATCH] More cleanup --- .../src/assets/addnewmenu/domain.svg | 30 - .../src/assets/addnewmenu/kubernetes.svg | 37 -- .../src/assets/addnewmenu/linode.svg | 42 -- .../src/assets/addnewmenu/nodebalancer.svg | 30 - .../src/assets/addnewmenu/oneclick.svg | 32 - .../src/assets/addnewmenu/stackscripts.svg | 28 - .../src/assets/addnewmenu/volume.svg | 29 - .../src/assets/icons/LKEminusSign.svg | 11 - .../src/assets/icons/LKEplusSign.svg | 12 - .../src/assets/icons/ResizeWindow.svg | 4 - .../src/assets/icons/account.svg | 1 - .../src/assets/icons/arrow_down.svg | 4 - .../src/assets/icons/arrow_right.svg | 4 - .../src/assets/icons/caution.svg | 6 - .../volume-create/src/assets/icons/chat.svg | 9 - .../src/assets/icons/checkmark-enabled.svg | 11 - .../src/assets/icons/cloud-init.svg | 4 - .../src/assets/icons/code-file.svg | 6 - .../src/assets/icons/community.svg | 13 - .../src/assets/icons/community_nav.svg | 1 - .../volume-create/src/assets/icons/copy.svg | 1 - .../src/assets/icons/credit-card.svg | 5 - .../src/assets/icons/db-logo-white.svg | 23 - .../src/assets/icons/db-logo.svg | 23 - .../src/assets/icons/diagonalArrow.svg | 12 - .../src/assets/icons/divider-vertical.svg | 3 - .../volume-create/src/assets/icons/docs.svg | 5 - .../src/assets/icons/document.svg | 11 - .../src/assets/icons/download.svg | 11 - .../src/assets/icons/drag-indicator.svg | 10 - .../src/assets/icons/emptynotification.svg | 8 - .../src/assets/icons/entityIcons/beta.svg | 1 - .../src/assets/icons/entityIcons/bucket.svg | 8 - .../src/assets/icons/entityIcons/database.svg | 6 - .../src/assets/icons/entityIcons/domain.svg | 8 - .../src/assets/icons/entityIcons/firewall.svg | 3 - .../src/assets/icons/entityIcons/folder.svg | 7 - .../src/assets/icons/entityIcons/iam.svg | 3 - .../src/assets/icons/entityIcons/image.svg | 4 - .../assets/icons/entityIcons/kubernetes.svg | 8 - .../src/assets/icons/entityIcons/linode.svg | 3 - .../src/assets/icons/entityIcons/managed.svg | 3 - .../src/assets/icons/entityIcons/monitor.svg | 6 - .../assets/icons/entityIcons/nodebalancer.svg | 8 - .../src/assets/icons/entityIcons/object.svg | 6 - .../src/assets/icons/entityIcons/oneclick.svg | 8 - .../icons/entityIcons/placement-groups.svg | 5 - .../assets/icons/entityIcons/stackscript.svg | 8 - .../src/assets/icons/entityIcons/volume.svg | 10 - .../src/assets/icons/entityIcons/vpc.svg | 4 - .../src/assets/icons/error-state-cloud.svg | 10 - .../volume-create/src/assets/icons/error.svg | 1 - .../src/assets/icons/external-link.svg | 6 - .../src/assets/icons/fileUploadComplete.svg | 3 - .../volume-create/src/assets/icons/flag.svg | 17 - .../src/assets/icons/get_help.svg | 1 - .../src/assets/icons/grid-view.svg | 1 - .../src/assets/icons/group-by-tag.svg | 1 - .../volume-create/src/assets/icons/guides.svg | 20 - .../volume-create/src/assets/icons/help.svg | 24 - .../src/assets/icons/icon-feedback.svg | 5 - .../volume-create/src/assets/icons/info.svg | 6 - .../volume-create/src/assets/icons/kebab.svg | 5 - .../src/assets/icons/lke-download.svg | 5 - .../volume-create/src/assets/icons/lock.svg | 4 - .../src/assets/icons/longview.svg | 8 - .../src/assets/icons/longview/cpu-icon.svg | 6 - .../src/assets/icons/longview/disk.svg | 7 - .../assets/icons/longview/package-icon.svg | 6 - .../src/assets/icons/longview/ram-sticks.svg | 10 - .../src/assets/icons/longview/server-icon.svg | 6 - .../src/assets/icons/marketplace.svg | 11 - .../src/assets/icons/mongodb.svg | 9 - .../src/assets/icons/monitor-disabled.svg | 6 - .../src/assets/icons/monitor-failed.svg | 9 - .../src/assets/icons/monitor-ok.svg | 9 - .../volume-create/src/assets/icons/more.svg | 8 - .../volume-create/src/assets/icons/mysql.svg | 12 - .../src/assets/icons/notification.svg | 1 - .../src/assets/icons/parent-child.svg | 1 - .../src/assets/icons/payment/amex.svg | 1 - .../src/assets/icons/payment/discover.svg | 1 - .../src/assets/icons/payment/gPayButton.svg | 2 - .../src/assets/icons/payment/googlePay.svg | 17 - .../src/assets/icons/payment/jcb.svg | 67 -- .../src/assets/icons/payment/mastercard.svg | 11 - .../src/assets/icons/payment/payPal.svg | 1 - .../src/assets/icons/payment/visa.svg | 1 - .../src/assets/icons/pending.svg | 9 - .../src/assets/icons/pointer.svg | 7 - .../src/assets/icons/postgresql.svg | 22 - .../promotionalOffers/heavenly-bucket.svg | 20 - .../providers/akamai-logo-rgb-waveOnly.svg | 9 - .../assets/icons/providers/github-logo.svg | 14 - .../assets/icons/providers/google-logo.svg | 12 - .../src/assets/icons/refresh.svg | 6 - .../volume-create/src/assets/icons/reset.svg | 10 - .../src/assets/icons/sort-up.svg | 3 - .../src/assets/icons/ssh-key.svg | 7 - .../volume-create/src/assets/icons/status.svg | 15 - .../src/assets/icons/support.svg | 16 - .../src/assets/icons/swapSmall.svg | 3 - .../volume-create/src/assets/icons/ticket.svg | 3 - .../volume-create/src/assets/icons/undo.svg | 6 - .../volume-create/src/assets/icons/unlock.svg | 5 - .../src/assets/icons/unsorted.svg | 6 - .../volume-create/src/assets/icons/view.svg | 22 - .../src/assets/icons/visibilityHide.svg | 4 - .../src/assets/icons/visibilityShow.svg | 4 - .../src/assets/icons/youtube.svg | 11 - .../volume-create/src/assets/icons/zoomin.svg | 10 - .../src/assets/icons/zoomout.svg | 10 - .../src/assets/logo/akamai-logo-color.svg | 19 - .../src/assets/logo/akamai-logo.svg | 19 - .../src/assets/logo/akamai-wave.svg | 3 - .../src/assets/referrals/step-1.svg | 21 - .../src/assets/referrals/step-2.svg | 39 -- .../src/assets/referrals/step-3.svg | 87 --- .../src/assets/weblish/weblish-fonts.css | 52 -- .../src/assets/weblish/weblish.css | 17 - .../src/assets/weblish/xterm.css | 159 ----- .../EnhancedSelect/EnhancedSelect.css | 36 -- .../HighlightedMarkdown.test.tsx.snap | 55 -- .../LandingHeader/LandingHeader.tsx | 4 - .../volume-create/src/components/OrderBy.md | 1 - .../volume-create/src/components/Paginated.md | 1 - .../RegionSelect/RegionSelect.styles.ts | 31 +- .../RegionSelect/RegionSelect.types.ts | 28 - .../RegionSelect/RegionSelect.utils.tsx | 30 - .../Tabs/__snapshots__/TabList.test.tsx.snap | 17 - .../volume-create/src/components/intro.mdx | 16 - packages/volume-create/src/constants.ts | 265 -------- .../volume-create/src/dev-tools/dev-tools.css | 601 ------------------ .../Billing/PdfGenerator/LinodeLogo.png | Bin 17046 -> 0 bytes .../Billing/PdfGenerator/akamai-logo.png | Bin 37095 -> 0 bytes .../features/Linodes/LinodeCreate/types.ts | 7 - .../src/features/Longview/LONGVIEW.md | 204 ------ .../src/features/Longview/shared/USAGE.md | 56 -- .../src/features/Search/searchLanding.css | 18 - packages/volume-create/src/queries/base.ts | 271 -------- .../volume-create/src/queries/firewalls.ts | 485 -------------- .../src/queries/linodes/configs.ts | 49 +- .../src/queries/linodes/linodes.ts | 364 +---------- .../src/queries/linodes/requests.ts | 15 - .../src/queries/nodebalancers.ts | 345 ---------- .../src/queries/placementGroups.ts | 283 --------- .../src/queries/profile/profile.ts | 14 - .../src/queries/regions/regions.ts | 29 +- packages/volume-create/src/queries/vlans.ts | 40 -- .../src/queries/volumes/volumes.ts | 212 +----- .../src/queries/vpcs/requests.ts | 8 - .../volume-create/src/queries/vpcs/vpcs.ts | 202 ------ .../volume-create/src/utilities/configs.ts | 21 - packages/volume-create/src/utilities/env.ts | 12 - .../volume-create/src/utilities/errorUtils.ts | 62 -- .../src/utilities/formatRegion.ts | 71 +-- .../src/utilities/formikErrorUtils.ts | 101 --- .../volume-create/src/utilities/getAll.ts | 6 - .../src/utilities/pricing/constants.ts | 18 - .../src/utilities/pricing/dynamicPricing.ts | 63 -- 160 files changed, 10 insertions(+), 5535 deletions(-) delete mode 100644 packages/volume-create/src/assets/addnewmenu/domain.svg delete mode 100644 packages/volume-create/src/assets/addnewmenu/kubernetes.svg delete mode 100644 packages/volume-create/src/assets/addnewmenu/linode.svg delete mode 100644 packages/volume-create/src/assets/addnewmenu/nodebalancer.svg delete mode 100644 packages/volume-create/src/assets/addnewmenu/oneclick.svg delete mode 100644 packages/volume-create/src/assets/addnewmenu/stackscripts.svg delete mode 100644 packages/volume-create/src/assets/addnewmenu/volume.svg delete mode 100644 packages/volume-create/src/assets/icons/LKEminusSign.svg delete mode 100644 packages/volume-create/src/assets/icons/LKEplusSign.svg delete mode 100644 packages/volume-create/src/assets/icons/ResizeWindow.svg delete mode 100644 packages/volume-create/src/assets/icons/account.svg delete mode 100644 packages/volume-create/src/assets/icons/arrow_down.svg delete mode 100644 packages/volume-create/src/assets/icons/arrow_right.svg delete mode 100644 packages/volume-create/src/assets/icons/caution.svg delete mode 100644 packages/volume-create/src/assets/icons/chat.svg delete mode 100644 packages/volume-create/src/assets/icons/checkmark-enabled.svg delete mode 100644 packages/volume-create/src/assets/icons/cloud-init.svg delete mode 100755 packages/volume-create/src/assets/icons/code-file.svg delete mode 100644 packages/volume-create/src/assets/icons/community.svg delete mode 100644 packages/volume-create/src/assets/icons/community_nav.svg delete mode 100644 packages/volume-create/src/assets/icons/copy.svg delete mode 100755 packages/volume-create/src/assets/icons/credit-card.svg delete mode 100644 packages/volume-create/src/assets/icons/db-logo-white.svg delete mode 100644 packages/volume-create/src/assets/icons/db-logo.svg delete mode 100644 packages/volume-create/src/assets/icons/diagonalArrow.svg delete mode 100644 packages/volume-create/src/assets/icons/divider-vertical.svg delete mode 100644 packages/volume-create/src/assets/icons/docs.svg delete mode 100644 packages/volume-create/src/assets/icons/document.svg delete mode 100644 packages/volume-create/src/assets/icons/download.svg delete mode 100644 packages/volume-create/src/assets/icons/drag-indicator.svg delete mode 100644 packages/volume-create/src/assets/icons/emptynotification.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/beta.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/bucket.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/database.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/domain.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/firewall.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/folder.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/iam.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/image.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/kubernetes.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/linode.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/managed.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/monitor.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/nodebalancer.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/object.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/oneclick.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/placement-groups.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/stackscript.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/volume.svg delete mode 100644 packages/volume-create/src/assets/icons/entityIcons/vpc.svg delete mode 100644 packages/volume-create/src/assets/icons/error-state-cloud.svg delete mode 100644 packages/volume-create/src/assets/icons/error.svg delete mode 100644 packages/volume-create/src/assets/icons/external-link.svg delete mode 100644 packages/volume-create/src/assets/icons/fileUploadComplete.svg delete mode 100644 packages/volume-create/src/assets/icons/flag.svg delete mode 100644 packages/volume-create/src/assets/icons/get_help.svg delete mode 100644 packages/volume-create/src/assets/icons/grid-view.svg delete mode 100644 packages/volume-create/src/assets/icons/group-by-tag.svg delete mode 100644 packages/volume-create/src/assets/icons/guides.svg delete mode 100644 packages/volume-create/src/assets/icons/help.svg delete mode 100644 packages/volume-create/src/assets/icons/icon-feedback.svg delete mode 100755 packages/volume-create/src/assets/icons/info.svg delete mode 100644 packages/volume-create/src/assets/icons/kebab.svg delete mode 100755 packages/volume-create/src/assets/icons/lke-download.svg delete mode 100644 packages/volume-create/src/assets/icons/lock.svg delete mode 100644 packages/volume-create/src/assets/icons/longview.svg delete mode 100644 packages/volume-create/src/assets/icons/longview/cpu-icon.svg delete mode 100644 packages/volume-create/src/assets/icons/longview/disk.svg delete mode 100644 packages/volume-create/src/assets/icons/longview/package-icon.svg delete mode 100644 packages/volume-create/src/assets/icons/longview/ram-sticks.svg delete mode 100644 packages/volume-create/src/assets/icons/longview/server-icon.svg delete mode 100644 packages/volume-create/src/assets/icons/marketplace.svg delete mode 100644 packages/volume-create/src/assets/icons/mongodb.svg delete mode 100644 packages/volume-create/src/assets/icons/monitor-disabled.svg delete mode 100644 packages/volume-create/src/assets/icons/monitor-failed.svg delete mode 100644 packages/volume-create/src/assets/icons/monitor-ok.svg delete mode 100644 packages/volume-create/src/assets/icons/more.svg delete mode 100644 packages/volume-create/src/assets/icons/mysql.svg delete mode 100644 packages/volume-create/src/assets/icons/notification.svg delete mode 100644 packages/volume-create/src/assets/icons/parent-child.svg delete mode 100644 packages/volume-create/src/assets/icons/payment/amex.svg delete mode 100644 packages/volume-create/src/assets/icons/payment/discover.svg delete mode 100644 packages/volume-create/src/assets/icons/payment/gPayButton.svg delete mode 100644 packages/volume-create/src/assets/icons/payment/googlePay.svg delete mode 100644 packages/volume-create/src/assets/icons/payment/jcb.svg delete mode 100644 packages/volume-create/src/assets/icons/payment/mastercard.svg delete mode 100644 packages/volume-create/src/assets/icons/payment/payPal.svg delete mode 100644 packages/volume-create/src/assets/icons/payment/visa.svg delete mode 100644 packages/volume-create/src/assets/icons/pending.svg delete mode 100644 packages/volume-create/src/assets/icons/pointer.svg delete mode 100644 packages/volume-create/src/assets/icons/postgresql.svg delete mode 100644 packages/volume-create/src/assets/icons/promotionalOffers/heavenly-bucket.svg delete mode 100644 packages/volume-create/src/assets/icons/providers/akamai-logo-rgb-waveOnly.svg delete mode 100644 packages/volume-create/src/assets/icons/providers/github-logo.svg delete mode 100644 packages/volume-create/src/assets/icons/providers/google-logo.svg delete mode 100644 packages/volume-create/src/assets/icons/refresh.svg delete mode 100644 packages/volume-create/src/assets/icons/reset.svg delete mode 100644 packages/volume-create/src/assets/icons/sort-up.svg delete mode 100644 packages/volume-create/src/assets/icons/ssh-key.svg delete mode 100644 packages/volume-create/src/assets/icons/status.svg delete mode 100644 packages/volume-create/src/assets/icons/support.svg delete mode 100644 packages/volume-create/src/assets/icons/swapSmall.svg delete mode 100644 packages/volume-create/src/assets/icons/ticket.svg delete mode 100644 packages/volume-create/src/assets/icons/undo.svg delete mode 100644 packages/volume-create/src/assets/icons/unlock.svg delete mode 100644 packages/volume-create/src/assets/icons/unsorted.svg delete mode 100644 packages/volume-create/src/assets/icons/view.svg delete mode 100644 packages/volume-create/src/assets/icons/visibilityHide.svg delete mode 100644 packages/volume-create/src/assets/icons/visibilityShow.svg delete mode 100644 packages/volume-create/src/assets/icons/youtube.svg delete mode 100644 packages/volume-create/src/assets/icons/zoomin.svg delete mode 100644 packages/volume-create/src/assets/icons/zoomout.svg delete mode 100644 packages/volume-create/src/assets/logo/akamai-logo-color.svg delete mode 100644 packages/volume-create/src/assets/logo/akamai-logo.svg delete mode 100644 packages/volume-create/src/assets/logo/akamai-wave.svg delete mode 100644 packages/volume-create/src/assets/referrals/step-1.svg delete mode 100644 packages/volume-create/src/assets/referrals/step-2.svg delete mode 100644 packages/volume-create/src/assets/referrals/step-3.svg delete mode 100644 packages/volume-create/src/assets/weblish/weblish-fonts.css delete mode 100644 packages/volume-create/src/assets/weblish/weblish.css delete mode 100644 packages/volume-create/src/assets/weblish/xterm.css delete mode 100644 packages/volume-create/src/components/EnhancedSelect/EnhancedSelect.css delete mode 100644 packages/volume-create/src/components/HighlightedMarkdown/__snapshots__/HighlightedMarkdown.test.tsx.snap delete mode 100644 packages/volume-create/src/components/OrderBy.md delete mode 100644 packages/volume-create/src/components/Paginated.md delete mode 100644 packages/volume-create/src/components/Tabs/__snapshots__/TabList.test.tsx.snap delete mode 100644 packages/volume-create/src/components/intro.mdx delete mode 100644 packages/volume-create/src/dev-tools/dev-tools.css delete mode 100644 packages/volume-create/src/features/Billing/PdfGenerator/LinodeLogo.png delete mode 100644 packages/volume-create/src/features/Billing/PdfGenerator/akamai-logo.png delete mode 100644 packages/volume-create/src/features/Linodes/LinodeCreate/types.ts delete mode 100644 packages/volume-create/src/features/Longview/LONGVIEW.md delete mode 100644 packages/volume-create/src/features/Longview/shared/USAGE.md delete mode 100644 packages/volume-create/src/features/Search/searchLanding.css delete mode 100644 packages/volume-create/src/queries/firewalls.ts delete mode 100644 packages/volume-create/src/queries/nodebalancers.ts delete mode 100644 packages/volume-create/src/queries/placementGroups.ts delete mode 100644 packages/volume-create/src/queries/vlans.ts delete mode 100644 packages/volume-create/src/queries/vpcs/requests.ts delete mode 100644 packages/volume-create/src/queries/vpcs/vpcs.ts delete mode 100644 packages/volume-create/src/utilities/configs.ts delete mode 100644 packages/volume-create/src/utilities/env.ts diff --git a/packages/volume-create/src/assets/addnewmenu/domain.svg b/packages/volume-create/src/assets/addnewmenu/domain.svg deleted file mode 100644 index 1796226df4f..00000000000 --- a/packages/volume-create/src/assets/addnewmenu/domain.svg +++ /dev/null @@ -1,30 +0,0 @@ - - Domains - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/addnewmenu/kubernetes.svg b/packages/volume-create/src/assets/addnewmenu/kubernetes.svg deleted file mode 100644 index a57a1a7b25c..00000000000 --- a/packages/volume-create/src/assets/addnewmenu/kubernetes.svg +++ /dev/null @@ -1,37 +0,0 @@ - - Kubernetes - Kubernetes icon - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/addnewmenu/linode.svg b/packages/volume-create/src/assets/addnewmenu/linode.svg deleted file mode 100644 index 3c8b6157648..00000000000 --- a/packages/volume-create/src/assets/addnewmenu/linode.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - Linodes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/addnewmenu/nodebalancer.svg b/packages/volume-create/src/assets/addnewmenu/nodebalancer.svg deleted file mode 100644 index 5c085b03c0f..00000000000 --- a/packages/volume-create/src/assets/addnewmenu/nodebalancer.svg +++ /dev/null @@ -1,30 +0,0 @@ - - NodeBalancers - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/addnewmenu/oneclick.svg b/packages/volume-create/src/assets/addnewmenu/oneclick.svg deleted file mode 100644 index ae096391ed7..00000000000 --- a/packages/volume-create/src/assets/addnewmenu/oneclick.svg +++ /dev/null @@ -1,32 +0,0 @@ - - One-Click Apps - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/addnewmenu/stackscripts.svg b/packages/volume-create/src/assets/addnewmenu/stackscripts.svg deleted file mode 100644 index 8fb7da5c4ca..00000000000 --- a/packages/volume-create/src/assets/addnewmenu/stackscripts.svg +++ /dev/null @@ -1,28 +0,0 @@ - - StackScripts - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/addnewmenu/volume.svg b/packages/volume-create/src/assets/addnewmenu/volume.svg deleted file mode 100644 index ae7dc732afc..00000000000 --- a/packages/volume-create/src/assets/addnewmenu/volume.svg +++ /dev/null @@ -1,29 +0,0 @@ - - Volumes - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/LKEminusSign.svg b/packages/volume-create/src/assets/icons/LKEminusSign.svg deleted file mode 100644 index b43078ead4a..00000000000 --- a/packages/volume-create/src/assets/icons/LKEminusSign.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - Group 3 - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/LKEplusSign.svg b/packages/volume-create/src/assets/icons/LKEplusSign.svg deleted file mode 100644 index d7e0bc1b4e0..00000000000 --- a/packages/volume-create/src/assets/icons/LKEplusSign.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - Group 19 - - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/ResizeWindow.svg b/packages/volume-create/src/assets/icons/ResizeWindow.svg deleted file mode 100644 index c9ea7c2201a..00000000000 --- a/packages/volume-create/src/assets/icons/ResizeWindow.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/account.svg b/packages/volume-create/src/assets/icons/account.svg deleted file mode 100644 index 31b3352dd0b..00000000000 --- a/packages/volume-create/src/assets/icons/account.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/volume-create/src/assets/icons/arrow_down.svg b/packages/volume-create/src/assets/icons/arrow_down.svg deleted file mode 100644 index 9f1e07f546c..00000000000 --- a/packages/volume-create/src/assets/icons/arrow_down.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/arrow_right.svg b/packages/volume-create/src/assets/icons/arrow_right.svg deleted file mode 100644 index 1791c6569e8..00000000000 --- a/packages/volume-create/src/assets/icons/arrow_right.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/caution.svg b/packages/volume-create/src/assets/icons/caution.svg deleted file mode 100644 index 9d5f060687b..00000000000 --- a/packages/volume-create/src/assets/icons/caution.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/volume-create/src/assets/icons/chat.svg b/packages/volume-create/src/assets/icons/chat.svg deleted file mode 100644 index 3ed66931c0f..00000000000 --- a/packages/volume-create/src/assets/icons/chat.svg +++ /dev/null @@ -1,9 +0,0 @@ - - Linode Support Bot - - - - - - diff --git a/packages/volume-create/src/assets/icons/checkmark-enabled.svg b/packages/volume-create/src/assets/icons/checkmark-enabled.svg deleted file mode 100644 index 3fd216c9113..00000000000 --- a/packages/volume-create/src/assets/icons/checkmark-enabled.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/cloud-init.svg b/packages/volume-create/src/assets/icons/cloud-init.svg deleted file mode 100644 index b348e0fabb2..00000000000 --- a/packages/volume-create/src/assets/icons/cloud-init.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/volume-create/src/assets/icons/code-file.svg b/packages/volume-create/src/assets/icons/code-file.svg deleted file mode 100755 index 9c6bcd734a0..00000000000 --- a/packages/volume-create/src/assets/icons/code-file.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/volume-create/src/assets/icons/community.svg b/packages/volume-create/src/assets/icons/community.svg deleted file mode 100644 index c999ea0ee3a..00000000000 --- a/packages/volume-create/src/assets/icons/community.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/community_nav.svg b/packages/volume-create/src/assets/icons/community_nav.svg deleted file mode 100644 index 49b91bc7edf..00000000000 --- a/packages/volume-create/src/assets/icons/community_nav.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/volume-create/src/assets/icons/copy.svg b/packages/volume-create/src/assets/icons/copy.svg deleted file mode 100644 index 36ad859323b..00000000000 --- a/packages/volume-create/src/assets/icons/copy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/credit-card.svg b/packages/volume-create/src/assets/icons/credit-card.svg deleted file mode 100755 index 3a26b0b5724..00000000000 --- a/packages/volume-create/src/assets/icons/credit-card.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/volume-create/src/assets/icons/db-logo-white.svg b/packages/volume-create/src/assets/icons/db-logo-white.svg deleted file mode 100644 index 0cf1495fd94..00000000000 --- a/packages/volume-create/src/assets/icons/db-logo-white.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/db-logo.svg b/packages/volume-create/src/assets/icons/db-logo.svg deleted file mode 100644 index 5776006d284..00000000000 --- a/packages/volume-create/src/assets/icons/db-logo.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/diagonalArrow.svg b/packages/volume-create/src/assets/icons/diagonalArrow.svg deleted file mode 100644 index 6c6c312aafe..00000000000 --- a/packages/volume-create/src/assets/icons/diagonalArrow.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/divider-vertical.svg b/packages/volume-create/src/assets/icons/divider-vertical.svg deleted file mode 100644 index 79add159022..00000000000 --- a/packages/volume-create/src/assets/icons/divider-vertical.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/volume-create/src/assets/icons/docs.svg b/packages/volume-create/src/assets/icons/docs.svg deleted file mode 100644 index 787f090ffd2..00000000000 --- a/packages/volume-create/src/assets/icons/docs.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/volume-create/src/assets/icons/document.svg b/packages/volume-create/src/assets/icons/document.svg deleted file mode 100644 index 045accc3d63..00000000000 --- a/packages/volume-create/src/assets/icons/document.svg +++ /dev/null @@ -1,11 +0,0 @@ - - Guides and Tutorials - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/download.svg b/packages/volume-create/src/assets/icons/download.svg deleted file mode 100644 index c62d32ea29c..00000000000 --- a/packages/volume-create/src/assets/icons/download.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - Shape - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/drag-indicator.svg b/packages/volume-create/src/assets/icons/drag-indicator.svg deleted file mode 100644 index edf19fc2e9e..00000000000 --- a/packages/volume-create/src/assets/icons/drag-indicator.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/emptynotification.svg b/packages/volume-create/src/assets/icons/emptynotification.svg deleted file mode 100644 index 5181cecf317..00000000000 --- a/packages/volume-create/src/assets/icons/emptynotification.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/entityIcons/beta.svg b/packages/volume-create/src/assets/icons/entityIcons/beta.svg deleted file mode 100644 index a1bef780f26..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/beta.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/entityIcons/bucket.svg b/packages/volume-create/src/assets/icons/entityIcons/bucket.svg deleted file mode 100644 index 2b31a6a55ef..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/bucket.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/entityIcons/database.svg b/packages/volume-create/src/assets/icons/entityIcons/database.svg deleted file mode 100644 index 7a37fe8995c..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/database.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/volume-create/src/assets/icons/entityIcons/domain.svg b/packages/volume-create/src/assets/icons/entityIcons/domain.svg deleted file mode 100644 index f30671f6d98..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/domain.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/entityIcons/firewall.svg b/packages/volume-create/src/assets/icons/entityIcons/firewall.svg deleted file mode 100644 index 3736e993c26..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/firewall.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/volume-create/src/assets/icons/entityIcons/folder.svg b/packages/volume-create/src/assets/icons/entityIcons/folder.svg deleted file mode 100644 index a406989c05d..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/folder.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/entityIcons/iam.svg b/packages/volume-create/src/assets/icons/entityIcons/iam.svg deleted file mode 100644 index 6fc8bf9b920..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/iam.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/entityIcons/image.svg b/packages/volume-create/src/assets/icons/entityIcons/image.svg deleted file mode 100644 index e01047f5b04..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/image.svg +++ /dev/null @@ -1,4 +0,0 @@ - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/entityIcons/kubernetes.svg b/packages/volume-create/src/assets/icons/entityIcons/kubernetes.svg deleted file mode 100644 index 8885dea3841..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/kubernetes.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/entityIcons/linode.svg b/packages/volume-create/src/assets/icons/entityIcons/linode.svg deleted file mode 100644 index e166f137e99..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/linode.svg +++ /dev/null @@ -1,3 +0,0 @@ - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/entityIcons/managed.svg b/packages/volume-create/src/assets/icons/entityIcons/managed.svg deleted file mode 100644 index 6bfaf75ec49..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/managed.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/volume-create/src/assets/icons/entityIcons/monitor.svg b/packages/volume-create/src/assets/icons/entityIcons/monitor.svg deleted file mode 100644 index dacdcb2da9d..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/monitor.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/entityIcons/nodebalancer.svg b/packages/volume-create/src/assets/icons/entityIcons/nodebalancer.svg deleted file mode 100644 index 918a4ec2bae..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/nodebalancer.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/entityIcons/object.svg b/packages/volume-create/src/assets/icons/entityIcons/object.svg deleted file mode 100644 index 65980b18464..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/object.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/volume-create/src/assets/icons/entityIcons/oneclick.svg b/packages/volume-create/src/assets/icons/entityIcons/oneclick.svg deleted file mode 100644 index 94f86fa7c1f..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/oneclick.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/entityIcons/placement-groups.svg b/packages/volume-create/src/assets/icons/entityIcons/placement-groups.svg deleted file mode 100644 index 799f4092e66..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/placement-groups.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/volume-create/src/assets/icons/entityIcons/stackscript.svg b/packages/volume-create/src/assets/icons/entityIcons/stackscript.svg deleted file mode 100644 index 3a8174098b5..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/stackscript.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/entityIcons/volume.svg b/packages/volume-create/src/assets/icons/entityIcons/volume.svg deleted file mode 100644 index 8eb099e2786..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/volume.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/entityIcons/vpc.svg b/packages/volume-create/src/assets/icons/entityIcons/vpc.svg deleted file mode 100644 index c58f06cd668..00000000000 --- a/packages/volume-create/src/assets/icons/entityIcons/vpc.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/volume-create/src/assets/icons/error-state-cloud.svg b/packages/volume-create/src/assets/icons/error-state-cloud.svg deleted file mode 100644 index 43770d38e10..00000000000 --- a/packages/volume-create/src/assets/icons/error-state-cloud.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/error.svg b/packages/volume-create/src/assets/icons/error.svg deleted file mode 100644 index bf6b870a888..00000000000 --- a/packages/volume-create/src/assets/icons/error.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/external-link.svg b/packages/volume-create/src/assets/icons/external-link.svg deleted file mode 100644 index cc35c6ce95f..00000000000 --- a/packages/volume-create/src/assets/icons/external-link.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/volume-create/src/assets/icons/fileUploadComplete.svg b/packages/volume-create/src/assets/icons/fileUploadComplete.svg deleted file mode 100644 index 39c8d4a518e..00000000000 --- a/packages/volume-create/src/assets/icons/fileUploadComplete.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/volume-create/src/assets/icons/flag.svg b/packages/volume-create/src/assets/icons/flag.svg deleted file mode 100644 index 72902081752..00000000000 --- a/packages/volume-create/src/assets/icons/flag.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - diff --git a/packages/volume-create/src/assets/icons/get_help.svg b/packages/volume-create/src/assets/icons/get_help.svg deleted file mode 100644 index 7f6a236913a..00000000000 --- a/packages/volume-create/src/assets/icons/get_help.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/volume-create/src/assets/icons/grid-view.svg b/packages/volume-create/src/assets/icons/grid-view.svg deleted file mode 100644 index f3d7bdaea11..00000000000 --- a/packages/volume-create/src/assets/icons/grid-view.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/volume-create/src/assets/icons/group-by-tag.svg b/packages/volume-create/src/assets/icons/group-by-tag.svg deleted file mode 100644 index 8c81e0f20b6..00000000000 --- a/packages/volume-create/src/assets/icons/group-by-tag.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/volume-create/src/assets/icons/guides.svg b/packages/volume-create/src/assets/icons/guides.svg deleted file mode 100644 index 24458d92ada..00000000000 --- a/packages/volume-create/src/assets/icons/guides.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/help.svg b/packages/volume-create/src/assets/icons/help.svg deleted file mode 100644 index 16e1a087171..00000000000 --- a/packages/volume-create/src/assets/icons/help.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/icon-feedback.svg b/packages/volume-create/src/assets/icons/icon-feedback.svg deleted file mode 100644 index aa987d40471..00000000000 --- a/packages/volume-create/src/assets/icons/icon-feedback.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/volume-create/src/assets/icons/info.svg b/packages/volume-create/src/assets/icons/info.svg deleted file mode 100755 index e98fa54dd89..00000000000 --- a/packages/volume-create/src/assets/icons/info.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/volume-create/src/assets/icons/kebab.svg b/packages/volume-create/src/assets/icons/kebab.svg deleted file mode 100644 index 7a41c5e6c9a..00000000000 --- a/packages/volume-create/src/assets/icons/kebab.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/volume-create/src/assets/icons/lke-download.svg b/packages/volume-create/src/assets/icons/lke-download.svg deleted file mode 100755 index 3422a506036..00000000000 --- a/packages/volume-create/src/assets/icons/lke-download.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/volume-create/src/assets/icons/lock.svg b/packages/volume-create/src/assets/icons/lock.svg deleted file mode 100644 index 62caf228e55..00000000000 --- a/packages/volume-create/src/assets/icons/lock.svg +++ /dev/null @@ -1,4 +0,0 @@ - -Encrypted - - diff --git a/packages/volume-create/src/assets/icons/longview.svg b/packages/volume-create/src/assets/icons/longview.svg deleted file mode 100644 index 49058da0882..00000000000 --- a/packages/volume-create/src/assets/icons/longview.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/longview/cpu-icon.svg b/packages/volume-create/src/assets/icons/longview/cpu-icon.svg deleted file mode 100644 index 052f4d48a17..00000000000 --- a/packages/volume-create/src/assets/icons/longview/cpu-icon.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/volume-create/src/assets/icons/longview/disk.svg b/packages/volume-create/src/assets/icons/longview/disk.svg deleted file mode 100644 index ec38ea590e5..00000000000 --- a/packages/volume-create/src/assets/icons/longview/disk.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/packages/volume-create/src/assets/icons/longview/package-icon.svg b/packages/volume-create/src/assets/icons/longview/package-icon.svg deleted file mode 100644 index b0ead5c8a77..00000000000 --- a/packages/volume-create/src/assets/icons/longview/package-icon.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/volume-create/src/assets/icons/longview/ram-sticks.svg b/packages/volume-create/src/assets/icons/longview/ram-sticks.svg deleted file mode 100644 index 40c4c8999e4..00000000000 --- a/packages/volume-create/src/assets/icons/longview/ram-sticks.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/longview/server-icon.svg b/packages/volume-create/src/assets/icons/longview/server-icon.svg deleted file mode 100644 index c6f66e47746..00000000000 --- a/packages/volume-create/src/assets/icons/longview/server-icon.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/volume-create/src/assets/icons/marketplace.svg b/packages/volume-create/src/assets/icons/marketplace.svg deleted file mode 100644 index 60ba66381ad..00000000000 --- a/packages/volume-create/src/assets/icons/marketplace.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - Fill 1 - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/mongodb.svg b/packages/volume-create/src/assets/icons/mongodb.svg deleted file mode 100644 index c3b4e2d16d0..00000000000 --- a/packages/volume-create/src/assets/icons/mongodb.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - Path - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/monitor-disabled.svg b/packages/volume-create/src/assets/icons/monitor-disabled.svg deleted file mode 100644 index 7be1239b37c..00000000000 --- a/packages/volume-create/src/assets/icons/monitor-disabled.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/volume-create/src/assets/icons/monitor-failed.svg b/packages/volume-create/src/assets/icons/monitor-failed.svg deleted file mode 100644 index 9916b7e2e53..00000000000 --- a/packages/volume-create/src/assets/icons/monitor-failed.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/monitor-ok.svg b/packages/volume-create/src/assets/icons/monitor-ok.svg deleted file mode 100644 index 99cf24a29ef..00000000000 --- a/packages/volume-create/src/assets/icons/monitor-ok.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/more.svg b/packages/volume-create/src/assets/icons/more.svg deleted file mode 100644 index 7b86cba0b77..00000000000 --- a/packages/volume-create/src/assets/icons/more.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/mysql.svg b/packages/volume-create/src/assets/icons/mysql.svg deleted file mode 100644 index 8144e0221a9..00000000000 --- a/packages/volume-create/src/assets/icons/mysql.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - mysql-6 - - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/notification.svg b/packages/volume-create/src/assets/icons/notification.svg deleted file mode 100644 index 865bcc0fc4e..00000000000 --- a/packages/volume-create/src/assets/icons/notification.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/volume-create/src/assets/icons/parent-child.svg b/packages/volume-create/src/assets/icons/parent-child.svg deleted file mode 100644 index 4edbab05ed4..00000000000 --- a/packages/volume-create/src/assets/icons/parent-child.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/payment/amex.svg b/packages/volume-create/src/assets/icons/payment/amex.svg deleted file mode 100644 index 12623bb8c4f..00000000000 --- a/packages/volume-create/src/assets/icons/payment/amex.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/payment/discover.svg b/packages/volume-create/src/assets/icons/payment/discover.svg deleted file mode 100644 index 67a5bd2a627..00000000000 --- a/packages/volume-create/src/assets/icons/payment/discover.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/payment/gPayButton.svg b/packages/volume-create/src/assets/icons/payment/gPayButton.svg deleted file mode 100644 index aa74f3f0d0e..00000000000 --- a/packages/volume-create/src/assets/icons/payment/gPayButton.svg +++ /dev/null @@ -1,2 +0,0 @@ - -image/svg+xml diff --git a/packages/volume-create/src/assets/icons/payment/googlePay.svg b/packages/volume-create/src/assets/icons/payment/googlePay.svg deleted file mode 100644 index 8910b402132..00000000000 --- a/packages/volume-create/src/assets/icons/payment/googlePay.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/payment/jcb.svg b/packages/volume-create/src/assets/icons/payment/jcb.svg deleted file mode 100644 index e6d96ae3178..00000000000 --- a/packages/volume-create/src/assets/icons/payment/jcb.svg +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/payment/mastercard.svg b/packages/volume-create/src/assets/icons/payment/mastercard.svg deleted file mode 100644 index 6ad92093a35..00000000000 --- a/packages/volume-create/src/assets/icons/payment/mastercard.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/payment/payPal.svg b/packages/volume-create/src/assets/icons/payment/payPal.svg deleted file mode 100644 index 4cac9240af1..00000000000 --- a/packages/volume-create/src/assets/icons/payment/payPal.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/payment/visa.svg b/packages/volume-create/src/assets/icons/payment/visa.svg deleted file mode 100644 index 360a4369b02..00000000000 --- a/packages/volume-create/src/assets/icons/payment/visa.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/pending.svg b/packages/volume-create/src/assets/icons/pending.svg deleted file mode 100644 index 03ee9107408..00000000000 --- a/packages/volume-create/src/assets/icons/pending.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/pointer.svg b/packages/volume-create/src/assets/icons/pointer.svg deleted file mode 100644 index 676f587837c..00000000000 --- a/packages/volume-create/src/assets/icons/pointer.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - icon_pointer - - - - diff --git a/packages/volume-create/src/assets/icons/postgresql.svg b/packages/volume-create/src/assets/icons/postgresql.svg deleted file mode 100644 index a3996622490..00000000000 --- a/packages/volume-create/src/assets/icons/postgresql.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - postgresql - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/promotionalOffers/heavenly-bucket.svg b/packages/volume-create/src/assets/icons/promotionalOffers/heavenly-bucket.svg deleted file mode 100644 index 4e5769a64ac..00000000000 --- a/packages/volume-create/src/assets/icons/promotionalOffers/heavenly-bucket.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/providers/akamai-logo-rgb-waveOnly.svg b/packages/volume-create/src/assets/icons/providers/akamai-logo-rgb-waveOnly.svg deleted file mode 100644 index ecc82648017..00000000000 --- a/packages/volume-create/src/assets/icons/providers/akamai-logo-rgb-waveOnly.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - Shape - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/providers/github-logo.svg b/packages/volume-create/src/assets/icons/providers/github-logo.svg deleted file mode 100644 index ab5c5549ae6..00000000000 --- a/packages/volume-create/src/assets/icons/providers/github-logo.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - diff --git a/packages/volume-create/src/assets/icons/providers/google-logo.svg b/packages/volume-create/src/assets/icons/providers/google-logo.svg deleted file mode 100644 index 307886a522d..00000000000 --- a/packages/volume-create/src/assets/icons/providers/google-logo.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - GoogleG - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/refresh.svg b/packages/volume-create/src/assets/icons/refresh.svg deleted file mode 100644 index 677cb9966bb..00000000000 --- a/packages/volume-create/src/assets/icons/refresh.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/reset.svg b/packages/volume-create/src/assets/icons/reset.svg deleted file mode 100644 index 40f4cca6ce9..00000000000 --- a/packages/volume-create/src/assets/icons/reset.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - Group - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/sort-up.svg b/packages/volume-create/src/assets/icons/sort-up.svg deleted file mode 100644 index 9039173df68..00000000000 --- a/packages/volume-create/src/assets/icons/sort-up.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/volume-create/src/assets/icons/ssh-key.svg b/packages/volume-create/src/assets/icons/ssh-key.svg deleted file mode 100644 index 32459459a33..00000000000 --- a/packages/volume-create/src/assets/icons/ssh-key.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/packages/volume-create/src/assets/icons/status.svg b/packages/volume-create/src/assets/icons/status.svg deleted file mode 100644 index ae7d06df074..00000000000 --- a/packages/volume-create/src/assets/icons/status.svg +++ /dev/null @@ -1,15 +0,0 @@ - - Group - - - - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/support.svg b/packages/volume-create/src/assets/icons/support.svg deleted file mode 100644 index 5873f7d0fe6..00000000000 --- a/packages/volume-create/src/assets/icons/support.svg +++ /dev/null @@ -1,16 +0,0 @@ - - Customer Support - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/swapSmall.svg b/packages/volume-create/src/assets/icons/swapSmall.svg deleted file mode 100644 index 6711e50df3b..00000000000 --- a/packages/volume-create/src/assets/icons/swapSmall.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/volume-create/src/assets/icons/ticket.svg b/packages/volume-create/src/assets/icons/ticket.svg deleted file mode 100644 index afb766cc72a..00000000000 --- a/packages/volume-create/src/assets/icons/ticket.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/volume-create/src/assets/icons/undo.svg b/packages/volume-create/src/assets/icons/undo.svg deleted file mode 100644 index 235305e5a9d..00000000000 --- a/packages/volume-create/src/assets/icons/undo.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/volume-create/src/assets/icons/unlock.svg b/packages/volume-create/src/assets/icons/unlock.svg deleted file mode 100644 index ae99e8d9a24..00000000000 --- a/packages/volume-create/src/assets/icons/unlock.svg +++ /dev/null @@ -1,5 +0,0 @@ - -Not Encrypted - - - diff --git a/packages/volume-create/src/assets/icons/unsorted.svg b/packages/volume-create/src/assets/icons/unsorted.svg deleted file mode 100644 index 761e21958d8..00000000000 --- a/packages/volume-create/src/assets/icons/unsorted.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/volume-create/src/assets/icons/view.svg b/packages/volume-create/src/assets/icons/view.svg deleted file mode 100644 index c69b1fbd3b8..00000000000 --- a/packages/volume-create/src/assets/icons/view.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - icon/view - Created with Sketch. - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/visibilityHide.svg b/packages/volume-create/src/assets/icons/visibilityHide.svg deleted file mode 100644 index 4fd7e5572df..00000000000 --- a/packages/volume-create/src/assets/icons/visibilityHide.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/volume-create/src/assets/icons/visibilityShow.svg b/packages/volume-create/src/assets/icons/visibilityShow.svg deleted file mode 100644 index 858614cc421..00000000000 --- a/packages/volume-create/src/assets/icons/visibilityShow.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/volume-create/src/assets/icons/youtube.svg b/packages/volume-create/src/assets/icons/youtube.svg deleted file mode 100644 index f362a87ae00..00000000000 --- a/packages/volume-create/src/assets/icons/youtube.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - Rectangle 2 - - - - - - - - diff --git a/packages/volume-create/src/assets/icons/zoomin.svg b/packages/volume-create/src/assets/icons/zoomin.svg deleted file mode 100644 index fcb722675ef..00000000000 --- a/packages/volume-create/src/assets/icons/zoomin.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/icons/zoomout.svg b/packages/volume-create/src/assets/icons/zoomout.svg deleted file mode 100644 index 7021f3a5f61..00000000000 --- a/packages/volume-create/src/assets/icons/zoomout.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/logo/akamai-logo-color.svg b/packages/volume-create/src/assets/logo/akamai-logo-color.svg deleted file mode 100644 index 18259cdabee..00000000000 --- a/packages/volume-create/src/assets/logo/akamai-logo-color.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/volume-create/src/assets/logo/akamai-logo.svg b/packages/volume-create/src/assets/logo/akamai-logo.svg deleted file mode 100644 index b977224c5e0..00000000000 --- a/packages/volume-create/src/assets/logo/akamai-logo.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/logo/akamai-wave.svg b/packages/volume-create/src/assets/logo/akamai-wave.svg deleted file mode 100644 index 423f77081c8..00000000000 --- a/packages/volume-create/src/assets/logo/akamai-wave.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/volume-create/src/assets/referrals/step-1.svg b/packages/volume-create/src/assets/referrals/step-1.svg deleted file mode 100644 index e701172beec..00000000000 --- a/packages/volume-create/src/assets/referrals/step-1.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/referrals/step-2.svg b/packages/volume-create/src/assets/referrals/step-2.svg deleted file mode 100644 index e58d24503ae..00000000000 --- a/packages/volume-create/src/assets/referrals/step-2.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/referrals/step-3.svg b/packages/volume-create/src/assets/referrals/step-3.svg deleted file mode 100644 index 6c864318f2f..00000000000 --- a/packages/volume-create/src/assets/referrals/step-3.svg +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/volume-create/src/assets/weblish/weblish-fonts.css b/packages/volume-create/src/assets/weblish/weblish-fonts.css deleted file mode 100644 index ee60a6dbb59..00000000000 --- a/packages/volume-create/src/assets/weblish/weblish-fonts.css +++ /dev/null @@ -1,52 +0,0 @@ -@font-face { - font-family: "Anonymous Pro"; - src: - local("Anonymice Powerline"), - url(https://linode.com/media/fonts/AnonymousPro.woff2) format("woff2"); -} - -@font-face { - font-family: Inconsolata; - src: - local("Inconsolata for Powerline"), - url(https://linode.com/media/fonts/Inconsolata.woff2) format("woff2"); -} - -@font-face { - font-family: "Liberation Mono"; - src: - local("Literation Mono Powerline"), - url(https://linode.com/media/fonts/LiberationMono.woff2) format("woff2"); -} - -@font-face { - font-family: "PT Mono"; - src: - local("PT Mono for Powerline"), - url(https://linode.com/media/fonts/PTMono.woff2) format("woff2"); -} - -@font-face { - font-family: "Source Code Pro"; - font-style: normal; - font-weight: 400; - src: - local("Sauce Code Powerline Regular"), - url(https://linode.com/media/fonts/SourceCodePro.woff2) format("woff2"); -} - -@font-face { - font-family: "Source Code Pro for Powerline"; - font-style: normal; - font-weight: 400; - src: - local("Sauce Code Powerline Regular"), - url(https://linode.com/media/fonts/SourceCodePro.woff2) format("woff2"); -} - -@font-face { - font-family: "Ubuntu Mono"; - src: - local("Ubuntu Mono derivative Powerline"), - url(https://linode.com/media/fonts/UbuntuMono.woff2) format("woff2"); -} diff --git a/packages/volume-create/src/assets/weblish/weblish.css b/packages/volume-create/src/assets/weblish/weblish.css deleted file mode 100644 index 30b4f0b3a99..00000000000 --- a/packages/volume-create/src/assets/weblish/weblish.css +++ /dev/null @@ -1,17 +0,0 @@ -body { - background-color: black !important; -} - -#terminal { - margin: 0; - padding: 0; - background-color: #000; - background-size: 10px 10px; - background-position: - 0 0, - 5px 5px; -} - -canvas { - margin: unset !important; -} diff --git a/packages/volume-create/src/assets/weblish/xterm.css b/packages/volume-create/src/assets/weblish/xterm.css deleted file mode 100644 index 65c4f95252f..00000000000 --- a/packages/volume-create/src/assets/weblish/xterm.css +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Copyright (c) 2014 The xterm.js authors. All rights reserved. - * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) - * https://github.com/chjj/term.js - * @license MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * Originally forked from (with the author's permission): - * Fabrice Bellard's javascript vt100 for jslinux: - * http://bellard.org/jslinux/ - * Copyright (c) 2011 Fabrice Bellard - * The original design remains. The terminal itself - * has been extended to include xterm CSI codes, among - * other features. - */ - -/** - * Default styles for xterm.js - */ - -.xterm { - font-family: courier-new, courier, monospace; - font-feature-settings: "liga" 0; - position: relative; - user-select: none; - -ms-user-select: none; - -webkit-user-select: none; -} - -.xterm.focus, -.xterm:focus { - outline: none; -} - -.xterm .xterm-helpers { - position: absolute; - top: 0; - /** - * The z-index of the helpers must be higher than the canvases in order for - * IMEs to appear on top. - */ - z-index: 10; -} - -.xterm .xterm-helper-textarea { - /* - * HACK: to fix IE's blinking cursor - * Move textarea out of the screen to the far left, so that the cursor is not visible. - */ - position: absolute; - opacity: 0; - left: -9999em; - top: 0; - width: 0; - height: 0; - z-index: -10; - /** Prevent wrapping so the IME appears against the textarea at the correct position */ - white-space: nowrap; - overflow: hidden; - resize: none; -} - -.xterm .composition-view { - /* TODO: Composition position got messed up somewhere */ - background: #000; - color: #fff; - display: none; - position: absolute; - white-space: nowrap; - z-index: 1; -} - -.xterm .composition-view.active { - display: block; -} - -.xterm .xterm-viewport { - /* On OS X this is required in order for the scroll bar to appear fully opaque */ - background-color: #000; - overflow-y: auto; - cursor: default; - position: absolute; - right: 0; - left: 0; - top: 0; - bottom: 0; -} - -.xterm .xterm-screen { - position: relative; -} - -.xterm .xterm-screen canvas { - position: absolute; - left: 0; - top: 0; -} - -.xterm .xterm-scroll-area { - visibility: hidden; -} - -.xterm-char-measure-element { - display: inline-block; - visibility: hidden; - position: absolute; - top: 0; - left: -9999em; - line-height: normal; -} - -.xterm.enable-mouse-events { - /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */ - cursor: default; -} - -.xterm:not(.enable-mouse-events) { - cursor: text; -} - -.xterm .xterm-accessibility, -.xterm .xterm-message { - position: absolute; - left: 0; - top: 0; - bottom: 0; - right: 0; - z-index: 100; - color: transparent; -} - -.xterm .live-region { - position: absolute; - left: -9999px; - width: 1px; - height: 1px; - overflow: hidden; -} - -.xterm-cursor-pointer { - cursor: pointer; -} diff --git a/packages/volume-create/src/components/EnhancedSelect/EnhancedSelect.css b/packages/volume-create/src/components/EnhancedSelect/EnhancedSelect.css deleted file mode 100644 index 5dec7585a20..00000000000 --- a/packages/volume-create/src/components/EnhancedSelect/EnhancedSelect.css +++ /dev/null @@ -1,36 +0,0 @@ -.enhancedSelect-menu-item { - height: 24px; - box-sizing: "content-box"; - overflow: "hidden"; - text-overflow: "ellipsis"; - font-size: 1rem; - box-sizing: content-box; - transition: - background-color 150ms cubic-bezier(0.4, 0, 0.2, 1), - color 0.2s cubic-bezier(0.4, 0, 0.2, 1); - font-family: "LatoWeb", sans-serif; - line-height: 1.2em; - white-space: initial; - padding: 12px; - text-overflow: initial; - width: auto; - display: flex; - position: relative; - text-align: left; - align-items: center; - justify-content: flex-start; - text-decoration: none; -} - -.enhancedSelect-menu-item-highlighted { - background-color: #3683dc; - color: white; -} - -.enhancedSelect-menu-item-selected { - color: #3683dc !important; -} - -.enhancedSelect-menu-item-selected.enhancedSelect-menu-item-highlighted { - background-color: #f4f4f4 !important; -} diff --git a/packages/volume-create/src/components/HighlightedMarkdown/__snapshots__/HighlightedMarkdown.test.tsx.snap b/packages/volume-create/src/components/HighlightedMarkdown/__snapshots__/HighlightedMarkdown.test.tsx.snap deleted file mode 100644 index 238b90d44c9..00000000000 --- a/packages/volume-create/src/components/HighlightedMarkdown/__snapshots__/HighlightedMarkdown.test.tsx.snap +++ /dev/null @@ -1,55 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`HighlightedMarkdown component > should highlight text consistently 1`] = ` - -

-

- Some markdown -

- - -
-    
-      
-        const
-      
-       x = 
-      
-        
-          function
-        
-        (
-        
-        ) 
-      
-      { 
-      
-        return
-      
-       
-      
-        true
-      
-      ; }
-
-    
-  
-

- -`; diff --git a/packages/volume-create/src/components/LandingHeader/LandingHeader.tsx b/packages/volume-create/src/components/LandingHeader/LandingHeader.tsx index 1eed003dda3..cff21fc6ca6 100644 --- a/packages/volume-create/src/components/LandingHeader/LandingHeader.tsx +++ b/packages/volume-create/src/components/LandingHeader/LandingHeader.tsx @@ -68,10 +68,6 @@ export const LandingHeader = ({ theme.breakpoints.between(636, "md"), ); - const docsAnalyticsLabel = analyticsLabel - ? analyticsLabel - : `${title} Landing`; - return ( ({ - "&.MuiListItem-root": { - "&:first-of-type > div": { - paddingTop: 10, - }, - display: "block", - padding: 0, - }, -})); - -const StyledChip = styled(Chip)(({ theme }) => ({ - "& .MuiChip-deleteIcon": { - "& svg": { - borderRadius: "50%", - }, - padding: 0, - }, - "& .MuiChip-deleteIcon.MuiSvgIcon-root": { - "&:hover": { - backgroundColor: theme.tokens.color.Neutrals.White, - color: theme.tokens.color.Ultramarine[70], - }, - backgroundColor: theme.tokens.color.Ultramarine[70], - color: theme.tokens.color.Neutrals.White, - }, -})); diff --git a/packages/volume-create/src/components/RegionSelect/RegionSelect.types.ts b/packages/volume-create/src/components/RegionSelect/RegionSelect.types.ts index 2de753264b2..931a0766025 100644 --- a/packages/volume-create/src/components/RegionSelect/RegionSelect.types.ts +++ b/packages/volume-create/src/components/RegionSelect/RegionSelect.types.ts @@ -5,7 +5,6 @@ import type { RegionSite, } from "@linode/api-v4"; import type { EnhancedAutocompleteProps } from "@linode/ui"; -import type React from "react"; import type { DisableItemOption } from "src/components/ListItemOption"; export type RegionFilterValue = @@ -18,10 +17,6 @@ export type RegionFilterValue = | "distributed-SA" | RegionSite; -interface GetRegionLabel { - includeSlug?: boolean; - region: Region; -} export interface RegionSelectProps< DisableClearable extends boolean | undefined = undefined, > extends Omit< @@ -56,29 +51,6 @@ export interface RegionSelectProps< width?: number; } -interface RegionMultiSelectProps - extends Omit< - EnhancedAutocompleteProps, - "label" | "onChange" | "options" - > { - SelectedRegionsList?: React.ComponentType<{ - onRemove: (region: string) => void; - selectedRegions: Region[]; - }>; - currentCapability: Capabilities | undefined; - disabledRegions?: Record; - helperText?: string; - isClearable?: boolean; - label?: string; - onChange: (ids: string[]) => void; - regions: Region[]; - required?: boolean; - selectedIds: string[]; - sortRegionOptions?: (a: Region, b: Region) => number; - tooltipText?: string; - width?: number; -} - export interface GetRegionOptionAvailability { accountAvailabilityData: AccountAvailability[] | undefined; currentCapability: Capabilities | undefined; diff --git a/packages/volume-create/src/components/RegionSelect/RegionSelect.utils.tsx b/packages/volume-create/src/components/RegionSelect/RegionSelect.utils.tsx index ad87ac75acb..ee6fa0eea03 100644 --- a/packages/volume-create/src/components/RegionSelect/RegionSelect.utils.tsx +++ b/packages/volume-create/src/components/RegionSelect/RegionSelect.utils.tsx @@ -7,7 +7,6 @@ import type { RegionFilterValue, } from "./RegionSelect.types"; import type { AccountAvailability, Capabilities, Region } from "@linode/api-v4"; -import type { LinodeCreateType } from "src/features/Linodes/LinodeCreate/types"; const NORTH_AMERICA = CONTINENT_CODE_TO_CONTINENT.NA; @@ -118,35 +117,6 @@ export const isRegionOptionUnavailable = ({ return regionWithUnavailability.unavailable.includes(currentCapability); }; -/** - * Util to determine whether a create type has support for distributed regions. - * - * @returns a boolean indicating whether or not the create type supports distributed regions. - */ -const isDistributedRegionSupported = (createType: LinodeCreateType) => { - const supportedDistributedRegionTypes = [ - "OS", - "Images", - undefined, // /linodes/create route - ]; - return supportedDistributedRegionTypes.includes(createType); -}; - -/** - * Util to determine whether a selected region is a distributed region. - * - * @returns a boolean indicating whether or not the selected region is a distributed region. - */ -const getIsDistributedRegion = ( - regionsData: Region[], - selectedRegion: string, -) => { - const region = regionsData.find( - (region) => region.id === selectedRegion || region.label === selectedRegion, - ); - return region?.site_type === "distributed"; -}; - export const getNewRegionLabel = (region: Region) => { const [city] = region.label.split(", "); // Include state for the US diff --git a/packages/volume-create/src/components/Tabs/__snapshots__/TabList.test.tsx.snap b/packages/volume-create/src/components/Tabs/__snapshots__/TabList.test.tsx.snap deleted file mode 100644 index 7c5d66fe7d1..00000000000 --- a/packages/volume-create/src/components/Tabs/__snapshots__/TabList.test.tsx.snap +++ /dev/null @@ -1,17 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`TabList component > renders TabList correctly 1`] = ` -

-
-
-
-
-`; diff --git a/packages/volume-create/src/components/intro.mdx b/packages/volume-create/src/components/intro.mdx deleted file mode 100644 index 8fc3de0b3f6..00000000000 --- a/packages/volume-create/src/components/intro.mdx +++ /dev/null @@ -1,16 +0,0 @@ -import { Meta } from "@storybook/blocks"; - - - -# Welcome - -The mission of **Linode** is to accelerate innovation by making cloud computing simple, affordable, and accessible to all. - -- Linode Storybook is a resource for the UI, behavior, code, and guidelines for use of all components in the Cloud Manager. -- It is the single source truth for approved components and a living document that will incorporate new components and refinements for existing components. - -## Contributing to the Design System - -### Process for adding or editing components - -Simply because a new UI element is created and used in Cloud Manager does not mean it’s automatically included in the Design System. For a new component to be added, it should provide a significant benefit over existing components and be useful for future applications. diff --git a/packages/volume-create/src/constants.ts b/packages/volume-create/src/constants.ts index 5fed5084719..739e5b3b03a 100644 --- a/packages/volume-create/src/constants.ts +++ b/packages/volume-create/src/constants.ts @@ -1,42 +1,10 @@ -import { getBooleanEnv } from "./utilities/env"; - // whether or not this is a Vite production build // This does not necessarily mean Cloud is running in a production environment. // For example, cloud.dev.linode.com is technically a production build. -const isProductionBuild = import.meta.env.PROD; // allow us to explicity enable dev tools -const ENABLE_DEV_TOOLS = getBooleanEnv( - import.meta.env.REACT_APP_ENABLE_DEV_TOOLS, -); // allow us to explicity enable maintenance mode -const ENABLE_MAINTENANCE_MODE = - import.meta.env.REACT_APP_ENABLE_MAINTENANCE_MODE === "true"; - -/** required for the app to function */ -const APP_ROOT = import.meta.env.REACT_APP_APP_ROOT || "http://localhost:3000"; -const LOGIN_ROOT = - import.meta.env.REACT_APP_LOGIN_ROOT || "https://login.linode.com"; -const API_ROOT = - import.meta.env.REACT_APP_API_ROOT || "https://api.linode.com/v4"; -const BETA_API_ROOT = API_ROOT + "beta"; -/** generate a client_id by navigating to https://cloud.linode.com/profile/clients */ -const CLIENT_ID = import.meta.env.REACT_APP_CLIENT_ID; -/** All of the following used specifically for Algolia search */ -const DOCS_BASE_URL = "https://linode.com"; -const COMMUNITY_BASE_URL = "https://linode.com/community/"; -const DOCS_SEARCH_URL = - "https://www.linode.com/docs/topresults/?docType=products%2Cguides%2Capi%2Creference-architecture&lndq="; -const COMMUNITY_SEARCH_URL = - "https://linode.com/community/questions/search?query="; -const ALGOLIA_APPLICATION_ID = - import.meta.env.REACT_APP_ALGOLIA_APPLICATION_ID || ""; -const ALGOLIA_SEARCH_KEY = import.meta.env.REACT_APP_ALGOLIA_SEARCH_KEY || ""; -const LAUNCH_DARKLY_API_KEY = import.meta.env.REACT_APP_LAUNCH_DARKLY_ID || ""; -const LINODE_STATUS_PAGE_URL = - import.meta.env.REACT_APP_STATUS_PAGE_URL || - "https://status.linode.com/api/v2"; // Maximum page size allowed by the API. Used in the `getAll()` helper function // to request as many items at once as possible. @@ -45,271 +13,38 @@ export const API_MAX_PAGE_SIZE = // Having more of a single entity than this number classifies you as having // a "large account". -const LARGE_ACCOUNT_THRESHOLD = 1500; // PayPal Client ID -const PAYPAL_CLIENT_ID = import.meta.env.REACT_APP_PAYPAL_CLIENT_ID || "sb"; // Google Pay Merchant ID -const GPAY_MERCHANT_ID = import.meta.env.REACT_APP_GPAY_MERCHANT_ID; // Google Pay Environment: 'TEST|PRODUCTION' -const GPAY_CLIENT_ENV = import.meta.env.REACT_APP_GPAY_ENV || "PRODUCTION"; - -const LONGVIEW_ROOT = "https://longview.linode.com/fetch"; - -/** optional variables */ -const SENTRY_URL = import.meta.env.REACT_APP_SENTRY_URL; -const LOGIN_SESSION_LIFETIME_MS = 45 * 60 * 1000; -const OAUTH_TOKEN_REFRESH_TIMEOUT = LOGIN_SESSION_LIFETIME_MS / 2; - -/** Adobe Analytics */ -const ADOBE_ANALYTICS_URL = import.meta.env.REACT_APP_ADOBE_ANALYTICS_URL; -const NUM_ADOBE_SCRIPTS = 3; - -/** Pendo */ -const PENDO_API_KEY = import.meta.env.REACT_APP_PENDO_API_KEY; - -/** for hard-coding token used for API Requests. Example: "Bearer 1234" */ -const ACCESS_TOKEN = import.meta.env.REACT_APP_ACCESS_TOKEN; - -const LOG_PERFORMANCE_METRICS = - !isProductionBuild && - import.meta.env.REACT_APP_LOG_PERFORMANCE_METRICS === "true"; - -const DISABLE_EVENT_THROTTLE = - Boolean(import.meta.env.REACT_APP_DISABLE_EVENT_THROTTLE) || false; // read about luxon formats https://moment.github.io/luxon/docs/manual/formatting.html // this format is not ISO -const DATETIME_DISPLAY_FORMAT = "yyyy-MM-dd HH:mm"; // ISO 8601 formats -const ISO_DATE_FORMAT = "yyyy-MM-dd"; -const ISO_DATETIME_NO_TZ_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; export const MAX_VOLUME_SIZE = 16384; -/** - * As per the current support polocy - * timeline for depricated distro is 6 months beyond eol date from image endpoints. - * refere M3-5753 for more info. - */ -const MAX_MONTHS_EOL_FILTER = 6; - -/** - * Values used for our events polling system. - * Number values are in milliseconds - */ -const POLLING_INTERVALS = { - /** - * By default, we will poll for events every 16 seconds - */ - DEFAULT: 16_000, - /** - * If there are "in-progress" events, we will poll every 2 seconds to give users - * a real-time feeling experience - * - * The /v4/account/events endpoint has a rate-limit of 400 requets per minute. - * If we request events every 2 seconds, we will make 30 calls in 1 minute. - * We should be well within rate-limits. - */ - IN_PROGRESS: 2_000, -} as const; - -/** - * Time after which data from the API is considered stale (half an hour) - */ -const REFRESH_INTERVAL = 60 * 30 * 1000; - // Default error message for non-API errors export const DEFAULT_ERROR_MESSAGE = "An unexpected error occurred."; // Default size limit for Images (some users have custom limits) -const IMAGE_DEFAULT_LIMIT = 6144; - -const allowedHTMLTagsStrict: string[] = [ - "a", - "p", - "b", - "del", - "em", - "i", - "code", - "strong", -]; - -const allowedHTMLTagsFlexible: string[] = [ - ...allowedHTMLTagsStrict, - "abbr", - "acronym", - "blockquote", - "br", - "hr", - "li", - "ol", - "ul", - "pre", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "span", - "table", - "tbody", - "td", - "th", - "thead", - "tr", -]; - -const allowedHTMLAttr = ["href", "lang", "title", "align", "class", "rel"]; - -/** - * MBps rate for intra DC migrations (AKA Mutations) - */ -const MBpsIntraDC = 200; - -/** - * MBps rate for inter DC migrations (AKA Cross-Data-Center migrations ) - */ -const MBpsInterDC = 7.5; - -/** - * The incoming network rate (in Gbps) that is standard for all Linodes - */ -const LINODE_NETWORK_IN = 40; - -/** - * Events that have entities or otherwise would - * be calculated as "clickable" in menus, but for which - * there is no sensible destination. - */ -const nonClickEvents = ["profile_update"]; - -/** - * Root URL for Object Storage clusters and buckets. - * A bucket can be accessed at: {bucket}.{cluster}.OBJECT_STORAGE_ROOT - */ -const OBJECT_STORAGE_ROOT = "linodeobjects.com"; - -/** - * This delimiter is used to retrieve objects at just one hierarchical level. - * As an example, assume the following objects are in a bucket: - * - * file1.txt - * my-folder/file2.txt - * my-folder/file3.txt - * - * Retrieving an object-list with a delimiter of '/' will return `file1.txt` - * only. This mechanism, in combination with "prefix" and "marker", allow us - * to simulate folder traversal of a bucket. - */ -const OBJECT_STORAGE_DELIMITER = "/"; // Value from 1-4 reflecting a minimum score from zxcvbn -const MINIMUM_PASSWORD_STRENGTH = 4; // When true, use the mock API defined in serverHandlers.ts instead of making network requests -const MOCK_SERVICE_WORKER = - import.meta.env.REACT_APP_MOCK_SERVICE_WORKER === "true"; // Maximum payment methods -const MAXIMUM_PAYMENT_METHODS = 6; // Default payment limits of Braintree payments in USD ($) -const PAYMENT_MIN = 5; -const PAYMENT_SOFT_MAX = 2_000; -const PAYMENT_HARD_MAX = 50_000; - -const DB_ROOT_USERNAME = "linroot"; // The date Linode switching to Akamai (for purposes of billing) -const AKAMAI_DATE = "2022-12-15 00:00:00"; - -const ADDRESSES = { - akamai: { - international: { - address1: "Grafenauweg 8", - city: "Zug", - country: "Switzerland", - entity: "Akamai Technologies International AG", - state: "Zug", - zip: "CH-6300", - }, - us: { - address1: "145 Broadway", - city: "Cambridge", - country: "USA", - entity: "Akamai Technologies, Inc.", - state: "MA", - zip: "02142", - }, - }, - linode: { - address1: "249 Arch St.", - city: "Philadelphia", - country: "USA", - entity: "Linode", - state: "PA", - zip: "19106", - }, -}; - -const ACCESS_LEVELS = { - none: "none", - readOnly: "read_only", - readWrite: "read_write", -}; // Linode Community URL accessible from the TopMenu Community icon -const LINODE_COMMUNITY_URL = "https://linode.com/community"; - -const FEEDBACK_LINK = "https://www.linode.com/feedback/"; - -const DEVELOPERS_LINK = "https://developers.linode.com"; // URL validators -const OFFSITE_URL_REGEX = - /(?=.{1,2000}$)((\s)*((ht|f)tp(s?):\/\/|mailto:)[A-Za-z0-9]+[~a-zA-Z0-9-_\.@\#\$%&;:,\?=/\+!\(\)]*(\s)*)/; -const ONSITE_URL_REGEX = /^([A-Za-z0-9/\.\?=&\-~]){1,2000}$/; // Firewall links -const CREATE_FIREWALL_LINK = - "https://techdocs.akamai.com/cloud-computing/docs/create-a-cloud-firewall"; -const FIREWALL_GET_STARTED_LINK = - "https://techdocs.akamai.com/cloud-computing/docs/getting-started-with-cloud-firewalls"; -const FIREWALL_LIMITS_CONSIDERATIONS_LINK = - "https://techdocs.akamai.com/cloud-computing/docs/cloud-firewall#limits-and-considerations"; // A/B Testing LD metrics keys for DX Tools -const LD_DX_TOOLS_METRICS_KEYS = { - CURL_CODE_SNIPPET: "A/B Test: Step 2 : cURL copy code snippet (copy icon)", - CURL_RESOURCE_LINKS: "A/B Test: Step 2 : DX Tools cURL resources links", - CURL_TAB_SELECTION: "A/B Test: Step 2 : DX Tools cURL tab selection", - INTEGRATION_ANSIBLE_CODE_SNIPPET: - "A/B Test: Step 2 : Integrations: Ansible copy code snippet (copy icon)", - INTEGRATION_ANSIBLE_RESOURCE_LINKS: - "a-b-test-step-2-dx-tools-integrations-ansible-resources-links", - INTEGRATION_TAB_SELECTION: - "A/B Test: Step 2 : DX Tools Integrations tab selection", - INTEGRATION_TERRAFORM_CODE_SNIPPET: - "A/B Test: Step 2 : Integrations: Terraform copy code snippet (copy icon)", - INTEGRATION_TERRAFORM_RESOURCE_LINKS: - "A/B Test: Step 2 : DX Tools integrations terraform resources links", - LINODE_CLI_CODE_SNIPPET: - "A/B Test: Step 2 : Linode CLI Tab selection and copy code snippet (copy icon)", - LINODE_CLI_RESOURCE_LINKS: - "A/B Test: Step 2 : DX Tools Linode CLI resources links", - LINODE_CLI_TAB_SELECTION: "A/B Test: Step 2 : Linode CLI Tab Selection", - OPEN_MODAL: "A/B Test: Step 1 : DX Tools Open Modal", - SDK_GO_CODE_SNIPPET: - "A/B Test: Step 2 : SDK: GO copy code snippet (copy icon)", - SDK_GO_RESOURCE_LINKS: "A/B Test: Step 2 : DX Tools SDK GO resources links", - SDK_PYTHON_CODE_SNIPPET: - "A/B Test: Step 2 : SDK: Python copy code snippet (copy icon)", - SDK_PYTHON_RESOURCE_LINKS: - "A/B Test: Step 2 : DX Tools SDK Python resources links", - SDK_TAB_SELECTION: "A/B Test: Step 2 : DX Tools SDK tab selection", -}; diff --git a/packages/volume-create/src/dev-tools/dev-tools.css b/packages/volume-create/src/dev-tools/dev-tools.css deleted file mode 100644 index 11653c13377..00000000000 --- a/packages/volume-create/src/dev-tools/dev-tools.css +++ /dev/null @@ -1,601 +0,0 @@ -.dev-tools { - --open-height-desktop: 375px; - --open-height-mobile: 400px; - - --background-color-closed: rgba(0, 0, 0, 0.55); - --background-color-opened: rgba(0, 0, 0, 0.85); - --background-blur: blur(7px); - - --open-close-transition: background-color 0.2s, height 0.075s; - - position: fixed; - width: 100%; - bottom: 0; - left: 0; - z-index: 1; - color: white; - font-size: 11pt; - scrollbar-color: rgba(255, 255, 255, 0.75) transparent; - - &.isDraggable { - position: relative; - height: calc(100% - 50px); - - .dev-tools__main { - flex-direction: column; - } - - .dev-tools__tool { - min-height: 300px; - } - - .dev-tools__tool__body, - .dev-tools__msw__column__body { - flex-basis: auto; - } - - .dev-tools__body { - border-radius: 0 0 4px 4px; - height: 100% !important; - } - } -} - -.dev-tools__draggable-handle { - position: absolute; - z-index: 2; - left: -30px; - top: 35px; - cursor: grab; - background: #222222; - border: none; - border-radius: 4px 0 0 4px; - padding: 4px 4px 2px 4px; - - svg { - fill: white; - } -} - -.dev-tools__resize-handle { - position: absolute; - z-index: 2; - right: 0px; - bottom: 16px; - color: white; - cursor: ew-resize; - background: transparent; - border: none; - - svg { - fill: white; - } -} - -.dev-tools hr { - border: none; - height: 1px; - background-color: rgba(255, 255, 255, 0.25); - margin-top: 12px; - margin-bottom: 16px; -} - -.dev-tools__select { - position: relative; - display: inline-block; - background: transparent; - border-radius: 4px; - border: 2px solid rgba(255, 255, 255, 0.5); - color: #fff; - - &.thin { - border-width: 1px; - } -} - -.dev-tools__select:has(> select:focus), -.dev-tools__select:has(> select:active) { - background: rgb(50, 50, 50); - border-color: rgba(255, 255, 255, 0.65); -} - -.dev-tools__select::after { - position: absolute; - right: 8px; - top: 10px; - content: ""; - display: block; - width: 6px; - height: 6px; - border-right: 2px solid white; - border-top: 2px solid white; - transform: rotate(135deg); -} - -.dev-tools__select select { - background: transparent; - border: none; - color: white; - padding: 4px 4px 5px 6px; - font-family: inherit; - font-size: 10.5pt; - appearance: none; - outline: none; -} - -/* avoid overriding TanStack React Query Devtools styles */ -.dev-tools__body button:not(.tsqd-parent-container button) { - background: transparent; - border: 2px solid rgba(255, 255, 255, 0.5); - border-radius: 1000px; - color: white; - cursor: pointer; - padding: 5px 18px 7px 18px; - font-weight: bold; - font-size: 10.5pt; - font-family: inherit; - transition: background-color 0.2s; - white-space: nowrap; - - &.small { - padding: 2px 8px 4px 8px; - border-width: 1px; - font-size: 12px; - margin: 0; - } - - &.right-align { - float: right; - } - - &.green { - background: #17cf73; - color: #080808; - } -} - -.dev-tools__body .dev-tools__content button:hover { - background: rgba(255, 255, 255, 0.1); -} - -.dev-tools__body .dev-tools__content button:not(:disabled).green:hover { - background-color: #60e9a4; - color: #080808; -} - -.dev-tools__body .dev-tools__content button:disabled, -.dev-tools__body .dev-tools__content button:disabled:active { - background: transparent; - cursor: not-allowed; - border-color: rgba(255, 255, 255, 0.3); - color: rgba(255, 255, 255, 0.25); -} - -.dev-tools__segmented-button .dev-tools__content button:not(:only-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - border-top-right-radius: 0; - border-bottom-right-radius: 0; - border-left: 1px solid rgba(255, 255, 255, 0.5); - border-right: 1px solid rgba(255, 255, 255, 0.5); -} - -.dev-tools__segmented-button { - display: flex; - flex-wrap: nowrap; - - button { - white-space: nowrap; - } -} - -.dev-tools__segmented-button - .dev-tools__content - button:first-child:not(:only-child) { - border-top-left-radius: 1000px; - border-bottom-left-radius: 1000px; - border-top-right-radius: 0; - border-bottom-right-radius: 0; - border-left: 2px solid rgba(255, 255, 255, 0.5); - border-right: 1px solid rgba(255, 255, 255, 0.5); - padding-left: 24px; -} - -.dev-tools__segmented-button - .dev-tools__content - button:last-child:not(:only-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - border-top-right-radius: 1000px; - border-bottom-right-radius: 1000px; - border-left: 1px solid rgba(255, 255, 255, 0.5); - border-right: 2px solid rgba(255, 255, 255, 0.5); - padding-right: 24px; -} - -.dev-tools__button-list { - width: 100%; - display: flex; - gap: 8px; - justify-content: end; -} - -.dev-tools__body .dev-tools__content button.toggle-button.toggle-button--on { - background: rgba(255, 255, 255, 0.2); - border: 2px solid white; - border-right: 2px solid white; - border-left: 2px solid white; - border-color: white; - border-left-color: white; - border-right-color: white; - border-top-color: white; - border-bottom-color: white; - color: white; - text-shadow: 0px -1px 0px black; -} - -.dev-tools__body .dev-tools__content button:active { - background: rgb(50, 50, 50); - border-color: rgba(255, 255, 255, 0.65); -} - -.dev-tools__toggle { - width: 70px; - height: 54px; - border-top-right-radius: 12px; - position: absolute; - left: 0; - bottom: 0; - z-index: 1; -} - -.dev-tools__toggle button { - width: 100%; - height: 100%; - background-color: transparent; - border: 0; - color: white; - cursor: pointer; -} - -.dev-tools__toggle button svg { - position: relative; - top: 3px; - left: -5px; -} - -.dev-tools.dev-tools--msw .dev-tools__toggle button svg { - color: #17cf73; - filter: drop-shadow(0 0 3px limegreen); -} - -.dev-tools__toggle button::after { - content: ""; - width: 8px; - height: 8px; - border-right: 2px solid white; - border-top: 2px solid white; - display: inline-block; - position: relative; - left: 3px; - top: -3px; - transform: rotate(-45deg); - transition: 0.3s transform; -} - -.dev-tools.dev-tools--open .dev-tools__toggle button::after { - transform: translateY(-4px) rotate(135deg); -} - -.dev-tools .dev-tools__toggle, -.dev-tools .dev-tools__body { - background-color: var(--background-color-closed); - backdrop-filter: var(--background-blur); - transition: var(--open-close-transition); -} - -.dev-tools.dev-tools--open .dev-tools__toggle { - background-color: #080808; - backdrop-filter: none; -} - -.dev-tools__draggable-toggle { - display: flex; - justify-content: end; - - button { - background: #1d1d1e; - background-blend-mode: overlay; - border: 0; - - cursor: pointer; - padding: 4px; - position: relative; - bottom: -4px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - } - - svg { - color: white; - } -} - -.dev-tools.dev-tools--open .dev-tools__body { - background-color: var(--background-color-opened); -} - -.dev-tools__body input[type="number"], -.dev-tools__body input[type="text"], -.dev-tools__body textarea { - border-radius: 4px; - border: 1px solid rgba(255, 255, 255, 0.5); - font-family: inherit; - font-size: 10.5pt; - float: right; -} - -.dev-tools__body textarea { - width: 100%; -} - -.dev-tools__body input[type="checkbox"] { - margin-right: 8px; - position: relative; - top: 1px; -} - -.dev-tools__modal-form { - max-height: 600px; - overflow-y: auto; - padding-right: 24px; - - label { - display: flex; - flex-direction: column; - gap: 4px; - font-family: "LatoWebBold", sans-serif; - } - - .dev-tools__modal-form__field { - margin-bottom: 12px; - } - - .dev-tools__modal-form__field__radio-group { - display: flex; - gap: 8px; - } - - input, - select { - padding: 4px; - } - - textarea, - select:is([multiple]) { - min-height: 200px; - font-family: monospace; - white-space: pre; - } -} - -/* - * Dev tools body has height of `0` when `.dev-tools--open` class is not present. - */ -.dev-tools .dev-tools__body { - height: 0; - padding: 0; -} - -.dev-tools.dev-tools--open .dev-tools__body { - height: var(--open-height-mobile); -} - -.dev-tools__content { - padding: 12px 12px; - display: flex; - flex-direction: column; - gap: 12px; - width: 100%; - height: 100%; -} - -.dev-tools__status-bar { - flex-grow: 0; - flex-shrink: 0; - display: flex; - align-items: center; - justify-content: space-between; - flex-wrap: nowrap; - overflow-x: auto; - gap: 12px; -} - -.dev-tools__main { - display: flex; - flex-direction: column; - flex-basis: 0; - flex-grow: 1; - flex-shrink: 1; - overflow: auto; - gap: 24px; -} - -.dev-tools__main__column { - flex-basis: 0; - flex-shrink: 0; - flex-grow: 1; -} - -.dev-tools__tool { - display: flex; - flex-direction: column; - height: 100%; - overflow: hidden; - gap: 12px; -} - -.dev-tools__tool__header { - flex-grow: 0; - flex-shrink: 0; - flex-basis: 0; -} - -.dev-tools__tool__header span { - font-size: 13pt; - font-weight: bold; -} - -.dev-tools__tool__footer { - flex-grow: 0; - flex-shrink: 0; - flex-basis: 0; - align-items: center; -} - -.dev-tools__list-box, -.dev-tools__scroll-box { - /*padding: 4px 8px;*/ - border: 1px solid rgba(255, 255, 255, 0.25); - box-shadow: inset 0px 5px 9px 0px rgba(0, 0, 0, 0.35); - height: 100%; - overflow: auto; -} - -.dev-tools__list-box ul { - padding-left: none; - padding: 4px 8px; - margin: 0; - height: 100%; -} - -.dev-tools__list-box li { - padding-top: 3px; - padding-bottom: 4px; - margin-top: 2px; - margin-bottom: 2px; - list-style: none; -} - -.dev-tools__list-box li:nth-child(even) { - background-color: rgba(255, 255, 255, 0.1); - border-radius: 4px; - margin-left: -4px; - margin-right: -4px; - padding-left: 4px; - padding-right: 4px; -} - -.dev-tools__msw__presets__toggle { - &.enabled { - color: #17cf73; - } - - &.disabled { - color: #ff9800; - } -} - -.dev-tools .dev-tools__list-box .dev-tools__list-box__separator { - border-bottom: 1px solid rgba(255, 255, 255, 0.25); - margin-left: -8px; - margin-right: -8px; - padding-left: 8px; - padding-right: 8px; - padding-bottom: 9px; - margin-bottom: 6px; - background-color: transparent; - border-radius: 0; - - &.has-button { - min-height: 36px; - } - - &.no-separator:not(:last-child) { - border-bottom: none; - margin-bottom: 0; - padding-bottom: 0; - } -} - -.dev-tools__msw { - display: flex; - flex-direction: column; - gap: 12px; -} - -.dev-tools__msw__presets { - display: flex; - justify-content: space-between; - align-items: center; - padding-bottom: 12px; - border-bottom: 1px solid rgba(255, 255, 255, 0.25); -} - -.dev-tools__msw__extras { - display: flex; - flex-direction: column; - flex-grow: 1; - gap: 12px; - - &.disabled { - opacity: 0.5; - } -} - -.dev-tools__msw__column__heading { - flex-basis: 0; - flex-grow: 0; - flex-shrink: 0; - font-weight: bold; - - &.disabled { - opacity: 0.5; - } -} - -@media only screen and (min-width: 1024px) { - .dev-tools.dev-tools--open .dev-tools__body { - height: var(--open-height-desktop); - } - - .dev-tools__main { - flex-direction: row; - } - - .dev-tools__msw__extras { - flex-direction: row; - } - - .dev-tools__tool__body { - flex-grow: 1; - flex-shrink: 1; - flex-basis: 0; - overflow: auto; - } - - .dev-tools__msw__column { - flex-grow: 1; - flex-shrink: 1; - flex-basis: 0; - height: 100%; - min-height: 120px; - display: flex; - flex-direction: column; - gap: 4px; - } - - .dev-tools__msw__column__body { - flex-basis: 0; - flex-grow: 1; - flex-shrink: 1; - overflow: auto; - } -} - -/* TanStack React Query Devtools */ -.tsqd-main-panel { - height: 100% !important; - width: 100% !important; - position: relative !important; -} diff --git a/packages/volume-create/src/features/Billing/PdfGenerator/LinodeLogo.png b/packages/volume-create/src/features/Billing/PdfGenerator/LinodeLogo.png deleted file mode 100644 index ebdb514936bacc0332c2944deef9736d5a178cd8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17046 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 v6O*6hs9;%Exf_ zfqxM)QWH0pmIk2&UPFU`2AP4pdHogO7Z&gfczY%&2srRN=x?`u%T z*I#ti-Bbku;R6vD;#YD8JxYU0)mE;1nd5LW{tli6As}EZ5KY;O>RXr@?5&^_HXSAI z)A6KU1si;PH(ij;^_2oK1X&Ih5fM#}14aTRZ(9bL4_QLM)p#fU>E)QSX)u

Gld> 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 diff --git a/packages/volume-create/src/features/Linodes/LinodeCreate/types.ts b/packages/volume-create/src/features/Linodes/LinodeCreate/types.ts deleted file mode 100644 index 7e7b7aff4c8..00000000000 --- a/packages/volume-create/src/features/Linodes/LinodeCreate/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type LinodeCreateType = - | "Backups" - | "Clone Linode" - | "Images" - | "OS" - | "One-Click" - | "StackScripts"; diff --git a/packages/volume-create/src/features/Longview/LONGVIEW.md b/packages/volume-create/src/features/Longview/LONGVIEW.md deleted file mode 100644 index d4a681d52cf..00000000000 --- a/packages/volume-create/src/features/Longview/LONGVIEW.md +++ /dev/null @@ -1,204 +0,0 @@ -# Longview - -Longview is a monitoring service that allows you to monitor either a Linode service, such as a Linode or NodeBalancer. To get started, [please see the Linode documentation.](https://techdocs.akamai.com/cloud-computing/docs/getting-started-with-longview) - -## Making Requests - -Once you have Longview installed on a server, now you can start making requests using both APIv4 and the Longview API. First is the APIv4 interactions. - -### Getting your API key from APIv4 - -Once you have a server and a Longview client created, send a GET request to the API to get the API key for your Longview client. Here's an example curl request: - -``` -curl --request GET \ - --url https://api.linode.com/v4/longview/clients/230115 \ - --header 'authorization: Bearer 12345' -``` - -which should return a shape that looks like: - -```js -{ - "id": 230115, - "apps": { - "nginx": false, - "apache": false, - "mysql": false - }, - "install_code": "1234", - "created": "2019-10-10T17:16:54", - "api_key": "1234", - "updated": "2019-10-11T14:15:25", - "label": "longview_client" -} -``` - -Hang on to the `api_key`. We'll need it to make requests to the Longview API. - -### Using the Longview API - -Here's an example Longview request: - -``` -curl --request POST \ - --url https://longview.linode.com/fetch \ - --header 'content-type: multipart/form-data; boundary=---011000010111000001101001' \ - --form api_key=1234 \ - --form api_action=batch \ - --form 'api_requestArray=[{ "api_action": "getLatestValue", "keys": [ "Disk.*" ] }]' -``` - -You can also batch datasets with comma-separated keys like so: - -``` -curl --request POST \ - --url https://longview.linode.com/fetch \ - --header 'content-type: multipart/form-data; boundary=---011000010111000001101001' \ - --form api_key=1234 \ - --form api_action=batch \ - --form 'api_requestArray=[{ "api_action": "getLatestValue", "keys": [ "Disk.*", "Memory.*" ] }]' -``` - -We recommend to keep `--form api_action=batch \` untouched and instead make changes to the `api_requestArray` field to filter down the data. There are a couple configurable fields here that you can change in the `apiRequestArray`: - -#### `api_action` - -| api_action | Description | -| --------------- | ------------------------------------------------------------ | -| getValues | Gets all data points for the specified key | -| getLatestValue | Gets the latest data point for the specified key | -| getTopProcesses | Gets all the top processes running on the server | -| lastUpdated | Returns a datetime when the Longview stats were last updated | - -#### `keys` - -| key | Returned Unit | Description | -| ----------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | -| \* | | Returns everything. This is _extremely_ slow, so run at your own risk. | -| Disk.\* | Bytes | Returns all storage information for each disk. Run `df -h` on the server to compare data. | -| Memory.\* | Kilobytes | Returns all data for ext4 and swap memory. Run `free` on the server to compare data. | -| CPU.\* | Literal Percentage | Returns usage for all CPUs on the server. Run `top` on the server to compare data | -| Load.\* | Literal number | Returns the literal number for how much Load is on the system. 1 load === 100% of CPU utilized | -| Network.\* | Bytes | Returns interfaces for inbound and outbound network traffic on the server. Run `apt install -y netload && netload` on the server to compare data | -| Packages.\* | Array | Returns an array of Packages that have available upgrades. This check is performed every 24 hours by the agent | - -## Longview Errors and Notifications - -Errors or warnings from the Longview API will always have HTTP status code 200, so using response codes to identify an error won't work. -To detect errors/warnings, look in the NOTIFICATIONS array included in every Longview response, which has the following shape: - -``` -{ - CODE: 10, - SEVERITY: 1, - TEXT: "An error message" -} -``` - -Severity values are interpreted as follows: - -- 0: Message -- 1: Slightly more important message -- 2: Warning/non-fatal error -- 3: Fatal error - -Within Cloud Manager, we treat notifications of severity 3 as actual errors, and will reject the Promise for any request that returns -one or more notifications of this type. Other notifications are passed through to the app normally and handled by consuming components. - -The full list of possible notifications can be found in the Longview server repo and is reproduced in the table below: - -| Name | SEVERITY | CODE | TEXT | -| --------------- | -------- | ---- | ------------------------------------------------------------------------------------------------------- | -| BADMETHOD | 3 | 1 | No such method. | -| BADAUTH | 3 | 4 | Authentication failed. | -| INVALIDPROPERTY | 3 | 7 | Property is invalid. | -| TRANSITION | 1 | 8 | Your data is currently in a transitional state. Any irregularities will clear up shortly. | -| THROTTLED | 3 | 14 | Requests are temporarily throttled for this Longview client. Please try again later. | -| MAINTENANCE | 3 | 23 | Longview is currently under maintenance. Data is temporarily unavailable. | -| STORAGEERROR | 2 | 29 | We've detected a problem with your Longview data. Please contact Support. | -| READONLY | 1 | 30 | Longview is currently in read-only mode. Current data may not be available at this time. | -| FUTUREDATA | 1 | 38 | Your system's clock is fast. Please ensure that ntp is installed and running. | -| PASTDATA | 1 | 39 | Your system's clock is slow. Please ensure that ntp is installed and running. | -| CLIENTUPDATE | 0 | 60 | An update for the Longview agent is available. Please update your installation of Longview. | -| MULTIPOST | 2 | 50 | Multiple clients appear to be posting data with this API key. Please check your clients' configuration. | - -## Populating your Linode with Data - -While developing with Longview, you may find it useful to populate your Linode with data for the Longview Client to record. Here are some tricks to up the usage of each reporting area: - -### CPU - -### RAM - -### Swap - -1. SSH into your Linode. - -2. `$ touch filename.c` - -3. Open `filename.c` with the editor of your choice, and paste the following: - -``` -#include  -#include  -#include  - -int main(int argc, char** argv) { -    int max = 8211; -    int mb = 0; -    char* buffer; - -    if(argc > 1) -        max = atoi(argv[1]); - -    while((buffer=malloc(1024*1024)) != NULL && mb < max) { -        memset(buffer, 0, 1024*1024); -        mb++; -        printf("Allocated %d MB\n", mb); -    } -return 0; -} -``` - -4. `$ dd if=/dev/urandom of=/dev/sdb bs=1M count=256` - -5. `$ gcc filename.c -o memeater` - -6. `$ ./memeater` - -7. In a separate terminal, SSH into your Linode and run `$ free h` to see your usage increase (this may take a few minutes). - -### Load - -### Networking - -## FAQ - -### I have 2 CPUs and `Load.*` is telling me I have a combined Load of `2.5`. What does that mean? - -If you have 2 CPUs, a Load of 2 means that both CPUs are 100% utilizied. Anything past that, that means there are scheduled processes that will begin when the Load clears up. - -As per @abemassry: - -> a 1 Core system with a load of 1 is running at 100% - -> a 1 core system with a load of 2 has double the amount of work scheduled than it can handle at that particular time slice - -See [this blog post](http://www.brendangregg.com/blog/2017-08-08/linux-load-averages.html) for a more detailed explanation. - ---- - -### How do I calculate my actual used memory on my ext disk? - -Actual used memory is `used - (buffers + cache)` which are all returned from the Longview API. - -Please note, however, that full memory on the system is `used + free` - ---- - -### How do I determine the maximum amount of network traffic I can have? - -Idk. Still have to figure this out. - ---- diff --git a/packages/volume-create/src/features/Longview/shared/USAGE.md b/packages/volume-create/src/features/Longview/shared/USAGE.md deleted file mode 100644 index 3cb811f0df2..00000000000 --- a/packages/volume-create/src/features/Longview/shared/USAGE.md +++ /dev/null @@ -1,56 +0,0 @@ -# Shared Components - -## TimeRangeSelect - -The `` component is meant to act as a compliment to -each line graph for the Longview feature. All Longview graphs will allow -the user to specify between what 2 timestamps they want to receive data for - the default being the past 30 minutes. - -Furthermore, if you're a Longview Pro member, you get more options than a free user. - -The `` aims to simplify the developer experience here. - -### Props - -| Prop Name | Type | Description | -| ----------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| handleStatsChange | (start: number, end: number) => void; | Function that returns the start and end times in seconds - these values should be used when requesting values from the Longview API with the `getValues` action. | - -### Usage - -Ideally in your parent, you'll have an interval set up to poll for `lastUpdated` time - the datetime that the Longview data has been updated for the client currently being viewed. - -You can also add typing to the actual `` component itself. - -Here's an example: - -```js -import * as React from 'react'; -import { TimeRangeSelect } from '../../shared/TimeRangeSelect'; -import { get } from '../../request'; -import { LongviewCPU } from '../../request.types'; - -interface Props { - clientAPIKey: string; -} - -type CombinedProps = Props; - -const Installation: React.FC = props => { - return ( - { - get(props.clientAPIKey, 'getValues', { - fields: ['cpu'], - start, - end - }) - .then(r => r) - .catch(e => e) - }} - /> - ); -}; - -export default React.memo(Installation); -``` diff --git a/packages/volume-create/src/features/Search/searchLanding.css b/packages/volume-create/src/features/Search/searchLanding.css deleted file mode 100644 index 885c0efee55..00000000000 --- a/packages/volume-create/src/features/Search/searchLanding.css +++ /dev/null @@ -1,18 +0,0 @@ -@keyframes fadein { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -.resultq { - font-size: 2.5rem; - line-height: 0.75; -} - -.nothing { - opacity: 0; - animation: fadein 0.2s linear 2.5s 1 normal forwards; -} diff --git a/packages/volume-create/src/queries/base.ts b/packages/volume-create/src/queries/base.ts index 6d3b45d87d8..52bdc6abdbf 100644 --- a/packages/volume-create/src/queries/base.ts +++ b/packages/volume-create/src/queries/base.ts @@ -1,11 +1,3 @@ -import { isEmpty } from "@linode/api-v4/lib/request"; -import { APIError, ResourcePage } from "@linode/api-v4/lib/types"; -import { - QueryClient, - QueryKey, - UseMutationOptions, -} from "@tanstack/react-query"; - // ============================================================================= // Config // ============================================================================= @@ -31,269 +23,6 @@ export const queryPresets = { }, }; -/** - * Creates and returns a new TanStack Query query client instance. - * - * Allows the query client behavior to be configured by specifying a preset. The - * 'longLived' preset is most suitable for production use, while 'oneTimeFetch' is - * preferred for tests. - * - * @param preset - Optional query preset for client. Either 'longLived' or 'oneTimeFetch'. - * - * @returns New `QueryClient` instance. - */ -const queryClientFactory = ( - preset: "longLived" | "oneTimeFetch" = "oneTimeFetch", -) => { - return new QueryClient({ - defaultOptions: { queries: queryPresets[preset] }, - }); -}; - -// ============================================================================= -// Types -// ============================================================================= -type ItemsByID = Record; - // ============================================================================= // Utility Functions // ============================================================================= - -/** - * "Indexers" for the following methods are included to handle - * the case where an entity's primary key isn't "id." By - * default, these methods will try to map Entity.id: Entity, - * but consumers can override this to map over whatever value - * is unique to that entity type. One example of this is Entity Transfers, - * which have a unique primary key of "token." - * - */ - -const listToItemsByID = ( - entityList: E[], - indexer: string = "id", -) => { - return entityList.reduce>( - (map, item) => ({ ...map, [item[indexer]]: item }), - {}, - ); -}; - -const mutationHandlers = , E = APIError[]>( - queryKey: QueryKey, - indexer: string = "id", - queryClient: QueryClient, -): UseMutationOptions void> => { - return { - onSuccess: (updatedEntity, variables) => { - // Update the query data to include the newly updated Entity. - queryClient.setQueryData>(queryKey, (oldData) => ({ - ...oldData, - [variables[indexer as keyof V]]: updatedEntity, - })); - }, - }; -}; - -const simpleMutationHandlers = ( - queryKey: QueryKey, - queryClient: QueryClient, -): UseMutationOptions void> => { - return { - onSuccess: (updatedEntity, variables: V) => { - queryClient.setQueryData(queryKey, (oldData: T) => ({ - ...oldData, - ...(isEmpty(updatedEntity) ? variables : updatedEntity), - })); - }, - }; -}; - -const creationHandlers = , V, E = APIError[]>( - queryKey: QueryKey, - indexer: string = "id", - queryClient: QueryClient, -): UseMutationOptions void> => { - return { - onSuccess: (updatedEntity) => { - // Add the new Entity to the existing data. - queryClient.setQueryData>(queryKey, (oldData) => ({ - ...oldData, - [updatedEntity[indexer]]: updatedEntity, - })); - }, - }; -}; - -const deletionHandlers = , E = APIError[]>( - queryKey: QueryKey, - indexer: string = "id", - queryClient: QueryClient, -): UseMutationOptions void> => { - return { - onSuccess: (_, variables) => { - // Remove the Entity from the existing data. - queryClient.setQueryData>(queryKey, (oldData) => { - const oldDataCopy = { ...oldData }; - delete oldDataCopy[variables[indexer]]; - return oldDataCopy; - }); - }, - }; -}; - -const itemInListMutationHandler = < - T extends { id: number | string }, - V, - E = APIError[], ->( - queryKey: QueryKey, - queryClient: QueryClient, -): UseMutationOptions void> => { - return { - onSuccess: (updatedEntity, variables) => { - queryClient.setQueryData(queryKey, (oldData) => { - if (!oldData) { - return []; - } - - const index = oldData?.findIndex( - (item) => item.id === updatedEntity.id, - ); - - if (index === -1) { - return oldData; - } - - const copy = [...oldData]; - - copy[index] = updatedEntity; - - return copy; - }); - }, - }; -}; - -const itemInListCreationHandler = ( - queryKey: QueryKey, - queryClient: QueryClient, -): UseMutationOptions void> => { - return { - onSuccess: (createdEntity) => { - queryClient.setQueryData(queryKey, (oldData) => { - if (!oldData) { - return []; - } - - oldData = [...oldData, createdEntity]; - - return oldData; - }); - }, - }; -}; - -const itemInListDeletionHandler = < - T, - V extends { id?: number | string }, - E = APIError[], ->( - queryKey: QueryKey, - queryClient: QueryClient, -): UseMutationOptions void> => { - return { - onSuccess: (_, variables) => { - queryClient.setQueryData(queryKey, (oldData) => { - if (!oldData) { - return []; - } - - const index = oldData?.findIndex((item) => item.id === variables.id); - - if (index === -1) { - return oldData; - } - - const copy = [...oldData]; - - copy.splice(index, 1); - - return copy; - }); - }, - }; -}; - -/** - * Use this function when you wish to update one entity within paginated React Query data - * @param queryKey The React Query queryKey prefix of paginated data (without the filters and page) - * @param id the id of the entity of you want to update within this paginated data - * @param newData the new data for the entity - */ -const updateInPaginatedStore = ( - queryKey: QueryKey, - id: number | string, - newData: Partial, - queryClient: QueryClient, -) => { - queryClient.setQueriesData | undefined>( - { queryKey }, - (oldData) => { - if (oldData === undefined) { - return undefined; - } - - const toUpdateIndex = oldData.data.findIndex( - (entity) => entity.id === id, - ); - - const isEntityOnPage = toUpdateIndex !== -1; - - if (!isEntityOnPage) { - return oldData; - } - - const updatedPaginatedData = [...oldData.data]; - - updatedPaginatedData[toUpdateIndex] = { - ...oldData.data[toUpdateIndex], - ...newData, - }; - - return { - ...oldData, - data: updatedPaginatedData, - }; - }, - ); -}; - -const getItemInPaginatedStore = ( - queryKey: QueryKey, - id: number, - queryClient: QueryClient, -) => { - const stores = queryClient.getQueriesData | undefined>({ - queryKey, - }); - - for (const store of stores) { - const data = store[1]?.data; - const item = data?.find((item) => item.id === id); - if (item) { - return item; - } - } - - return null; -}; - -const doesItemExistInPaginatedStore = ( - queryKey: QueryKey, - id: number, - queryClient: QueryClient, -) => { - const item = getItemInPaginatedStore(queryKey, id, queryClient); - return item !== null; -}; diff --git a/packages/volume-create/src/queries/firewalls.ts b/packages/volume-create/src/queries/firewalls.ts deleted file mode 100644 index 799296e1eff..00000000000 --- a/packages/volume-create/src/queries/firewalls.ts +++ /dev/null @@ -1,485 +0,0 @@ -import { - addFirewallDevice, - createFirewall, - deleteFirewall, - deleteFirewallDevice, - getFirewall, - getFirewallDevices, - getFirewalls, - getTemplate, - getTemplates, - updateFirewall, - updateFirewallRules, -} from "@linode/api-v4/lib/firewalls"; -import { createQueryKeys } from "@lukemorales/query-key-factory"; -import { - keepPreviousData, - useMutation, - useQuery, - useQueryClient, -} from "@tanstack/react-query"; - -import { getAll } from "src/utilities/getAll"; - -import { linodeQueries } from "./linodes/linodes"; -import { nodebalancerQueries } from "./nodebalancers"; -import { profileQueries } from "./profile/profile"; - -import type { - APIError, - CreateFirewallPayload, - Filter, - Firewall, - FirewallDevice, - FirewallDevicePayload, - FirewallRules, - FirewallTemplate, - Params, - ResourcePage, -} from "@linode/api-v4"; - -const getAllFirewallDevices = ( - id: number, - passedParams: Params = {}, - passedFilter: Filter = {}, -) => - getAll((params, filter) => - getFirewallDevices( - id, - { ...params, ...passedParams }, - { ...filter, ...passedFilter }, - ), - )().then((data) => data.data); - -const getAllFirewallTemplates = () => - getAll(getTemplates)().then((data) => data.data); - -const getAllFirewallsRequest = () => - getAll((passedParams, passedFilter) => - getFirewalls(passedParams, passedFilter), - )().then((data) => data.data); - -export const firewallQueries = createQueryKeys("firewalls", { - firewall: (id: number) => ({ - contextQueries: { - devices: { - queryFn: () => getAllFirewallDevices(id), - queryKey: null, - }, - }, - queryFn: () => getFirewall(id), - queryKey: [id], - }), - firewalls: { - contextQueries: { - all: { - queryFn: getAllFirewallsRequest, - queryKey: null, - }, - paginated: (params: Params = {}, filter: Filter = {}) => ({ - queryFn: () => getFirewalls(params, filter), - queryKey: [params, filter], - }), - }, - queryKey: null, - }, - template: (slug: string) => ({ - queryFn: () => getTemplate(slug), - queryKey: [slug], - }), - templates: { - queryFn: getAllFirewallTemplates, - queryKey: null, - }, -}); - -const useAllFirewallDevicesQuery = (id: number) => - useQuery( - firewallQueries.firewall(id)._ctx.devices, - ); - -const useAddFirewallDeviceMutation = (id: number) => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: (data) => addFirewallDevice(id, data), - onSuccess(firewallDevice) { - // Append the new entity to the Firewall object in the paginated store - queryClient.setQueriesData>( - { queryKey: firewallQueries.firewalls._ctx.paginated._def }, - (page) => { - if (!page) { - return undefined; - } - - const indexOfFirewall = page.data.findIndex( - (firewall) => firewall.id === id, - ); - - // If the firewall does not exist on this page, don't change anything - if (indexOfFirewall === -1) { - return page; - } - - const firewall = page.data[indexOfFirewall]; - - const newData = [...page.data]; - - newData[indexOfFirewall] = { - ...firewall, - entities: [...firewall.entities, firewallDevice.entity], - }; - return { ...page, data: newData }; - }, - ); - - // Append the new entity to the Firewall object in the "all firewalls" store - queryClient.setQueryData( - firewallQueries.firewalls._ctx.all.queryKey, - (firewalls) => { - if (!firewalls) { - return undefined; - } - - const indexOfFirewall = firewalls.findIndex( - (firewall) => firewall.id === id, - ); - - // If the firewall does not exist in the list, don't do anything - if (indexOfFirewall === -1) { - return firewalls; - } - - const newFirewalls = [...firewalls]; - - const firewall = firewalls[indexOfFirewall]; - - newFirewalls[indexOfFirewall] = { - ...firewall, - entities: [...firewall.entities, firewallDevice.entity], - }; - - return newFirewalls; - }, - ); - - // Append the new entity to the Firewall object - queryClient.setQueryData( - firewallQueries.firewall(id).queryKey, - (oldFirewall) => { - if (!oldFirewall) { - return undefined; - } - return { - ...oldFirewall, - entities: [...oldFirewall.entities, firewallDevice.entity], - }; - }, - ); - - // Add device to the dedicated devices store - queryClient.setQueryData( - firewallQueries.firewall(id)._ctx.devices.queryKey, - (existingFirewallDevices) => { - if (!existingFirewallDevices) { - return [firewallDevice]; - } - return [...existingFirewallDevices, firewallDevice]; - }, - ); - - // Refresh the cached result of the linode-specific firewalls query - if (firewallDevice.entity.type === "linode") { - queryClient.invalidateQueries({ - queryKey: linodeQueries.linode(firewallDevice.entity.id)._ctx - .firewalls.queryKey, - }); - } - - // Refresh the cached result of the nodebalancer-specific firewalls query - if (firewallDevice.entity.type === "nodebalancer") { - queryClient.invalidateQueries({ - queryKey: nodebalancerQueries.nodebalancer(firewallDevice.entity.id) - ._ctx.firewalls.queryKey, - }); - } - }, - }); -}; - -const useRemoveFirewallDeviceMutation = ( - firewallId: number, - deviceId: number, -) => { - const queryClient = useQueryClient(); - - return useMutation<{}, APIError[]>({ - mutationFn: () => deleteFirewallDevice(firewallId, deviceId), - onSuccess() { - // Invalidate firewall lists because GET /v4/firewalls returns all entities for each firewall - queryClient.invalidateQueries({ - queryKey: firewallQueries.firewalls.queryKey, - }); - - // Invalidate the firewall because the firewall objects has all entities and we want them to be in sync - queryClient.invalidateQueries({ - exact: true, - queryKey: firewallQueries.firewall(firewallId).queryKey, - }); - - // Remove device from the firewall's dedicaed devices store - queryClient.setQueryData( - firewallQueries.firewall(firewallId)._ctx.devices.queryKey, - (oldData) => { - return oldData?.filter((device) => device.id !== deviceId) ?? []; - }, - ); - }, - }); -}; - -const useFirewallsQuery = (params?: Params, filter?: Filter) => { - return useQuery, APIError[]>({ - ...firewallQueries.firewalls._ctx.paginated(params, filter), - placeholderData: keepPreviousData, - }); -}; - -const useFirewallQuery = (id: number) => - useQuery(firewallQueries.firewall(id)); - -const useAllFirewallsQuery = (enabled: boolean = true) => { - return useQuery({ - ...firewallQueries.firewalls._ctx.all, - enabled, - }); -}; - -const useMutateFirewall = (id: number) => { - const queryClient = useQueryClient(); - return useMutation>({ - mutationFn: (data) => updateFirewall(id, data), - onSuccess(firewall) { - // Update the firewall in the store - queryClient.setQueryData( - firewallQueries.firewall(firewall.id).queryKey, - firewall, - ); - - // Invalidate firewall lists - queryClient.invalidateQueries({ - queryKey: firewallQueries.firewalls.queryKey, - }); - }, - }); -}; - -const useCreateFirewall = () => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: createFirewall, - onSuccess(firewall) { - // Invalidate firewall lists - queryClient.invalidateQueries({ - queryKey: firewallQueries.firewalls.queryKey, - }); - - // Set the firewall in the store - queryClient.setQueryData( - firewallQueries.firewall(firewall.id).queryKey, - firewall, - ); - - // If a restricted user creates an entity, we must make sure grants are up to date. - queryClient.invalidateQueries({ - queryKey: profileQueries.grants.queryKey, - }); - - // For each entity attached to the firewall upon creation, invalidate - // the entity's firewall query so that firewalls are up to date - // on the entity's details/settings page. - for (const entity of firewall.entities) { - if (entity.type === "linode") { - queryClient.invalidateQueries({ - queryKey: linodeQueries.linode(entity.id)._ctx.firewalls.queryKey, - }); - } - if (entity.type === "nodebalancer") { - queryClient.invalidateQueries({ - queryKey: nodebalancerQueries.nodebalancer(entity.id)._ctx.firewalls - .queryKey, - }); - } - } - }, - }); -}; - -const useDeleteFirewall = (id: number) => { - const queryClient = useQueryClient(); - return useMutation<{}, APIError[]>({ - mutationFn: () => deleteFirewall(id), - onSuccess() { - // Remove firewall and its subqueries from the cache - queryClient.removeQueries({ - queryKey: firewallQueries.firewall(id).queryKey, - }); - - // Invalidate firewall lists - queryClient.invalidateQueries({ - queryKey: firewallQueries.firewalls.queryKey, - }); - }, - }); -}; - -const useUpdateFirewallRulesMutation = (firewallId: number) => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: (data) => updateFirewallRules(firewallId, data), - onSuccess(updatedRules) { - // Update rules on specific firewall - queryClient.setQueryData( - firewallQueries.firewall(firewallId).queryKey, - (oldData) => { - if (!oldData) { - return undefined; - } - return { ...oldData, rules: updatedRules }; - }, - ); - - // Update the Firewall object in the paginated store - queryClient.setQueriesData>( - { queryKey: firewallQueries.firewalls._ctx.paginated._def }, - (page) => { - if (!page) { - return undefined; - } - - const indexOfFirewall = page.data.findIndex( - (firewall) => firewall.id === firewallId, - ); - - // If the firewall does not exist on this page, don't change anything - if (indexOfFirewall === -1) { - return page; - } - - const firewall = page.data[indexOfFirewall]; - - const newData = [...page.data]; - - newData[indexOfFirewall] = { - ...firewall, - rules: updatedRules, - }; - return { ...page, data: newData }; - }, - ); - - // Update the the Firewall object in the "all firewalls" store - queryClient.setQueryData( - firewallQueries.firewalls._ctx.all.queryKey, - (firewalls) => { - if (!firewalls) { - return undefined; - } - - const indexOfFirewall = firewalls.findIndex( - (firewall) => firewall.id === firewallId, - ); - - // If the firewall does not exist in the list, don't do anything - if (indexOfFirewall === -1) { - return firewalls; - } - - const newFirewalls = [...firewalls]; - - const firewall = firewalls[indexOfFirewall]; - - newFirewalls[indexOfFirewall] = { - ...firewall, - rules: updatedRules, - }; - - return newFirewalls; - }, - ); - }, - }); -}; - -const firewallEventsHandler = ({ - event, - invalidateQueries, - queryClient, -}: EventHandlerData) => { - if (!event.entity) { - // Ignore any events that don't have an associated entity - return; - } - - switch (event.action) { - case "firewall_delete": - // Invalidate firewall lists - invalidateQueries({ - queryKey: firewallQueries.firewalls.queryKey, - }); - - // Remove firewall from the cache - queryClient.removeQueries({ - queryKey: firewallQueries.firewall(event.entity.id).queryKey, - }); - case "firewall_create": - // Invalidate firewall lists - invalidateQueries({ - queryKey: firewallQueries.firewalls.queryKey, - }); - case "firewall_device_add": - case "firewall_device_remove": - // For a firewall device event, the primary entity is the fireall and - // the secondary entity is the device that is added/removed - - // If a Linode is added or removed as a firewall device, invalidate it's firewalls - if (event.secondary_entity && event.secondary_entity.type === "linode") { - invalidateQueries({ - queryKey: linodeQueries.linode(event.secondary_entity.id)._ctx - .firewalls.queryKey, - }); - } - - // If a NodeBalancer is added or removed as a firewall device, invalidate it's firewalls - if ( - event.secondary_entity && - event.secondary_entity.type === "nodebalancer" - ) { - invalidateQueries({ - queryKey: nodebalancerQueries.nodebalancer(event.secondary_entity.id) - ._ctx.firewalls.queryKey, - }); - } - - // Invalidate the firewall - invalidateQueries({ - queryKey: firewallQueries.firewall(event.entity.id).queryKey, - }); - - // Invalidate firewall lists - invalidateQueries({ - queryKey: firewallQueries.firewalls.queryKey, - }); - case "firewall_disable": - case "firewall_enable": - case "firewall_rules_update": - case "firewall_update": - // invalidate the firewall - invalidateQueries({ - queryKey: firewallQueries.firewall(event.entity.id).queryKey, - }); - // Invalidate firewall lists - invalidateQueries({ - queryKey: firewallQueries.firewalls.queryKey, - }); - } -}; diff --git a/packages/volume-create/src/queries/linodes/configs.ts b/packages/volume-create/src/queries/linodes/configs.ts index a73cef84acc..a599870353d 100644 --- a/packages/volume-create/src/queries/linodes/configs.ts +++ b/packages/volume-create/src/queries/linodes/configs.ts @@ -1,17 +1,8 @@ -import { - createLinodeConfig, - deleteLinodeConfig, - updateLinodeConfig, -} from "@linode/api-v4"; -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { useQuery } from "@tanstack/react-query"; import { linodeQueries } from "./linodes"; -import type { - APIError, - Config, - LinodeConfigCreationData, -} from "@linode/api-v4"; +import type { APIError, Config } from "@linode/api-v4"; export const useAllLinodeConfigsQuery = (id: number, enabled = true) => { return useQuery({ @@ -19,39 +10,3 @@ export const useAllLinodeConfigsQuery = (id: number, enabled = true) => { enabled, }); }; - -const useLinodeConfigDeleteMutation = (linodeId: number, configId: number) => { - const queryClient = useQueryClient(); - return useMutation<{}, APIError[]>({ - mutationFn: () => deleteLinodeConfig(linodeId, configId), - onSuccess() { - queryClient.invalidateQueries({ - queryKey: linodeQueries.linode(linodeId)._ctx.configs.queryKey, - }); - }, - }); -}; - -const useLinodeConfigCreateMutation = (linodeId: number) => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: (data) => createLinodeConfig(linodeId, data), - onSuccess() { - queryClient.invalidateQueries({ - queryKey: linodeQueries.linode(linodeId)._ctx.configs.queryKey, - }); - }, - }); -}; - -const useLinodeConfigUpdateMutation = (linodeId: number, configId: number) => { - const queryClient = useQueryClient(); - return useMutation>({ - mutationFn: (data) => updateLinodeConfig(linodeId, configId, data), - onSuccess() { - queryClient.invalidateQueries({ - queryKey: linodeQueries.linode(linodeId)._ctx.configs.queryKey, - }); - }, - }); -}; diff --git a/packages/volume-create/src/queries/linodes/linodes.ts b/packages/volume-create/src/queries/linodes/linodes.ts index 07426154b34..8c63e967948 100644 --- a/packages/volume-create/src/queries/linodes/linodes.ts +++ b/packages/volume-create/src/queries/linodes/linodes.ts @@ -1,8 +1,4 @@ import { - changeLinodePassword, - cloneLinode, - createLinode, - deleteLinode, getLinode, getLinodeBackups, getLinodeFirewalls, @@ -15,32 +11,11 @@ import { getLinodeTransferByDate, getLinodes, getType, - linodeBoot, - linodeReboot, - linodeShutdown, - rescueLinode, - resizeLinode, - scheduleOrQueueMigration, - updateLinode, } from "@linode/api-v4"; import { createQueryKeys } from "@lukemorales/query-key-factory"; -import { - keepPreviousData, - useInfiniteQuery, - useMutation, - useQuery, - useQueryClient, -} from "@tanstack/react-query"; - -import { placementGroupQueries } from "src/queries/placementGroups"; -import { manuallySetVPCConfigInterfacesToActive } from "src/utilities/configs"; +import { keepPreviousData, useQuery } from "@tanstack/react-query"; -import { accountQueries } from "../account/queries"; import { queryPresets } from "../base"; -import { firewallQueries } from "../firewalls"; -import { profileQueries } from "../profile/profile"; -import { vlanQueries } from "../vlans"; -import { vpcQueries } from "../vpcs/vpcs"; import { getAllLinodeConfigs, getAllLinodeDisks, @@ -49,22 +24,7 @@ import { getAllLinodesRequest, } from "./requests"; -import type { - APIError, - Config, - CreateLinodeRequest, - DeepPartial, - Devices, - Filter, - Kernel, - Linode, - LinodeCloneData, - LinodeLishData, - MigrateLinodeRequest, - Params, - ResizeLinodePayload, - ResourcePage, -} from "@linode/api-v4"; +import type { APIError, Filter, Linode, Params } from "@linode/api-v4"; export const linodeQueries = createQueryKeys("linodes", { kernel: (id: string) => ({ @@ -154,19 +114,6 @@ export const linodeQueries = createQueryKeys("linodes", { }, }); -const useLinodesQuery = ( - params: Params = {}, - filter: Filter = {}, - enabled: boolean = true, -) => { - return useQuery, APIError[]>({ - ...linodeQueries.linodes._ctx.paginated(params, filter), - ...queryPresets.longLived, - enabled, - placeholderData: keepPreviousData, - }); -}; - export const useAllLinodesQuery = ( params: Params = {}, filter: Filter = {}, @@ -179,310 +126,3 @@ export const useAllLinodesQuery = ( placeholderData: keepPreviousData, }); }; - -const useInfiniteLinodesQuery = (filter: Filter = {}) => - useInfiniteQuery, APIError[]>({ - ...linodeQueries.linodes._ctx.infinite(filter), - getNextPageParam: ({ page, pages }) => { - if (page === pages) { - return undefined; - } - return page + 1; - }, - initialPageParam: 1, - }); - -const useLinodeQuery = (id: number, enabled = true) => { - return useQuery({ - ...linodeQueries.linode(id), - enabled, - }); -}; - -const useLinodeUpdateMutation = (id: number) => { - const queryClient = useQueryClient(); - return useMutation>({ - mutationFn: (data) => updateLinode(id, data), - onSuccess(linode) { - queryClient.invalidateQueries({ - queryKey: linodeQueries.linodes.queryKey, - }); - queryClient.setQueryData( - linodeQueries.linode(id).queryKey, - linode, - ); - }, - }); -}; - -const useAllLinodeKernelsQuery = ( - params: Params = {}, - filter: Filter = {}, - enabled = true, -) => { - return useQuery({ - ...linodeQueries.kernels(params, filter), - enabled, - }); -}; - -const useLinodeKernelQuery = (kernel: string) => { - return useQuery(linodeQueries.kernel(kernel)); -}; - -const useLinodeLishQuery = (id: number) => { - return useQuery({ - ...linodeQueries.linode(id)._ctx.lish, - staleTime: Infinity, - }); -}; - -const useDeleteLinodeMutation = (id: number) => { - const queryClient = useQueryClient(); - - const linode = queryClient.getQueryData( - linodeQueries.linode(id).queryKey, - ); - - const placementGroupId = linode?.placement_group?.id; - - return useMutation<{}, APIError[]>({ - mutationFn: () => deleteLinode(id), - async onSuccess() { - queryClient.removeQueries(linodeQueries.linode(id)); - queryClient.invalidateQueries(linodeQueries.linodes); - - // If the linode is assigned to a placement group, - // we need to invalidate the placement group queries - if (placementGroupId) { - queryClient.invalidateQueries({ - queryKey: - placementGroupQueries.placementGroup(placementGroupId).queryKey, - }); - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.all._def, - }); - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.paginated._def, - }); - } - }, - }); -}; - -const useCreateLinodeMutation = () => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: createLinode, - onSuccess(linode, variables) { - queryClient.invalidateQueries(linodeQueries.linodes); - queryClient.setQueryData( - linodeQueries.linode(linode.id).queryKey, - linode, - ); - - // If a restricted user creates an entity, we must make sure grants are up to date. - queryClient.invalidateQueries(profileQueries.grants); - - if (variables.interfaces?.some((i) => i.purpose === "vlan")) { - // If a Linode is created with a VLAN, invalidate vlans because - // they are derived from Linode configs. - queryClient.invalidateQueries({ queryKey: vlanQueries._def }); - } - - const vpcId = variables.interfaces?.find( - (i) => i.purpose === "vpc", - )?.vpc_id; - - if (vpcId) { - // If a Linode is created with a VPC, invalidate the related VPC queries. - queryClient.invalidateQueries({ queryKey: vpcQueries.all.queryKey }); - queryClient.invalidateQueries({ queryKey: vpcQueries.paginated._def }); - queryClient.invalidateQueries({ - queryKey: vpcQueries.vpc(vpcId).queryKey, - }); - } - - // If the Linode is assigned to a placement group on creation, - // we need to invalidate the placement group queries - if (variables.placement_group?.id) { - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.placementGroup( - variables.placement_group.id, - ).queryKey, - }); - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.all._def, - }); - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.paginated._def, - }); - } - - // If the Linode is attached to a firewall on creation, invalidate the firewall - // so that the new device is reflected. - if (variables.firewall_id) { - queryClient.invalidateQueries({ - queryKey: firewallQueries.firewalls.queryKey, - }); - queryClient.invalidateQueries({ - queryKey: firewallQueries.firewall(variables.firewall_id).queryKey, - }); - } - }, - }); -}; - -interface LinodeCloneDataWithId extends LinodeCloneData { - sourceLinodeId: number; -} - -const useCloneLinodeMutation = () => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: ({ sourceLinodeId, ...data }) => - cloneLinode(sourceLinodeId, data), - onSuccess(linode) { - queryClient.invalidateQueries(linodeQueries.linodes); - queryClient.setQueryData( - linodeQueries.linode(linode.id).queryKey, - linode, - ); - }, - }); -}; - -const useBootLinodeMutation = (id: number, configsToUpdate?: Config[]) => { - const queryClient = useQueryClient(); - return useMutation<{}, APIError[], { config_id?: number }>({ - mutationFn: ({ config_id }) => linodeBoot(id, config_id), - onSuccess() { - queryClient.invalidateQueries(linodeQueries.linodes); - queryClient.invalidateQueries({ - exact: true, - queryKey: linodeQueries.linode(id).queryKey, - }); - - if (configsToUpdate) { - /** - * PR #9893: If booting is successful, we manually set the query config data to have its vpc interfaces as - * active in order to remove the flickering 'Reboot Needed' status issue. This makes sure the Linode's status - * shows up as 'Running' right after being booting. Note that the configs query eventually gets invalidated - * and refetched after the Linode's status changes, ensuring that the actual data will be up to date. - */ - const updatedConfigs: Config[] = - manuallySetVPCConfigInterfacesToActive(configsToUpdate); - queryClient.setQueryData( - linodeQueries.linode(id)._ctx.configs.queryKey, - updatedConfigs, - ); - } - }, - }); -}; - -const useRebootLinodeMutation = (id: number, configsToUpdate?: Config[]) => { - const queryClient = useQueryClient(); - return useMutation<{}, APIError[], { config_id?: number }>({ - mutationFn: ({ config_id }) => linodeReboot(id, config_id), - onSuccess() { - queryClient.invalidateQueries(linodeQueries.linodes); - queryClient.invalidateQueries({ - exact: true, - queryKey: linodeQueries.linode(id).queryKey, - }); - - /** - * PR #9893: If rebooting is successful, we manually set the query config data to have its vpc interfaces as - * active in order to remove the flickering 'Reboot Needed' status issue. This makes sure the Linode's status - * shows up as 'Running' right after being rebooting. Note that the configs query eventually gets invalidated - * and refetched after the Linode's status changes, ensuring that the actual data will be up to date. - */ - if (configsToUpdate) { - const updatedConfigs: Config[] = - manuallySetVPCConfigInterfacesToActive(configsToUpdate); - queryClient.setQueryData( - linodeQueries.linode(id)._ctx.configs.queryKey, - updatedConfigs, - ); - } - }, - }); -}; - -const useShutdownLinodeMutation = (id: number) => { - const queryClient = useQueryClient(); - return useMutation<{}, APIError[]>({ - mutationFn: () => linodeShutdown(id), - onSuccess() { - queryClient.invalidateQueries(linodeQueries.linodes); - queryClient.invalidateQueries({ - exact: true, - queryKey: linodeQueries.linode(id).queryKey, - }); - }, - }); -}; - -const useLinodeChangePasswordMutation = (id: number) => - useMutation<{}, APIError[], { root_pass: string }>({ - mutationFn: ({ root_pass }) => changeLinodePassword(id, root_pass), - }); - -const useLinodeMigrateMutation = (id: number) => { - const queryClient = useQueryClient(); - return useMutation<{}, APIError[], MigrateLinodeRequest>({ - mutationFn: (data) => scheduleOrQueueMigration(id, data), - onSuccess(response, variables) { - queryClient.invalidateQueries(linodeQueries.linodes); - queryClient.invalidateQueries({ - exact: true, - queryKey: linodeQueries.linode(id).queryKey, - }); - - if (variables.placement_group?.id) { - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.placementGroup( - variables.placement_group.id, - ).queryKey, - }); - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.all._def, - }); - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.paginated._def, - }); - } - }, - }); -}; - -const useLinodeResizeMutation = (id: number) => { - const queryClient = useQueryClient(); - return useMutation<{}, APIError[], ResizeLinodePayload>({ - mutationFn: (data) => resizeLinode(id, data), - onSuccess() { - queryClient.invalidateQueries(linodeQueries.linodes); - queryClient.invalidateQueries({ - exact: true, - queryKey: linodeQueries.linode(id).queryKey, - }); - // Refetch notifications to dismiss any migration notifications - queryClient.invalidateQueries(accountQueries.notifications); - }, - }); -}; - -const useLinodeRescueMutation = (id: number) => { - const queryClient = useQueryClient(); - return useMutation<{}, APIError[], Devices>({ - mutationFn: (data) => rescueLinode(id, data), - onSuccess() { - queryClient.invalidateQueries(linodeQueries.linodes); - queryClient.invalidateQueries({ - exact: true, - queryKey: linodeQueries.linode(id).queryKey, - }); - }, - }); -}; diff --git a/packages/volume-create/src/queries/linodes/requests.ts b/packages/volume-create/src/queries/linodes/requests.ts index b9f130488a7..a9343606435 100644 --- a/packages/volume-create/src/queries/linodes/requests.ts +++ b/packages/volume-create/src/queries/linodes/requests.ts @@ -1,7 +1,6 @@ import { getLinodeConfigs, getLinodeDisks, - getLinodeFirewalls, getLinodeKernels, getLinodeTypes, getLinodes, @@ -13,7 +12,6 @@ import type { Config, Disk, Filter, - Firewall, Kernel, Linode, Params, @@ -43,19 +41,6 @@ export const getAllLinodeConfigs = (id: number) => getLinodeConfigs(id, params, filter), )().then((data) => data.data); -const getAllLinodeFirewalls = ( - linodeId: number, - passedParams: Params = {}, - passedFilter: Filter = {}, -) => - getAll((params, filter) => - getLinodeFirewalls( - linodeId, - { ...params, ...passedParams }, - { ...filter, ...passedFilter }, - ), - )().then((data) => data.data); - export const getAllLinodeDisks = (id: number) => getAll((params, filter) => getLinodeDisks(id, params, filter))().then( (data) => data.data, diff --git a/packages/volume-create/src/queries/nodebalancers.ts b/packages/volume-create/src/queries/nodebalancers.ts deleted file mode 100644 index 4bf07ddcd63..00000000000 --- a/packages/volume-create/src/queries/nodebalancers.ts +++ /dev/null @@ -1,345 +0,0 @@ -import { - createNodeBalancer, - createNodeBalancerConfig, - deleteNodeBalancer, - deleteNodeBalancerConfig, - getNodeBalancer, - getNodeBalancerConfigs, - getNodeBalancerFirewalls, - getNodeBalancerStats, - getNodeBalancerTypes, - getNodeBalancers, - updateNodeBalancer, - updateNodeBalancerConfig, -} from "@linode/api-v4"; -import { createQueryKeys } from "@lukemorales/query-key-factory"; -import { - keepPreviousData, - useInfiniteQuery, - useMutation, - useQuery, - useQueryClient, -} from "@tanstack/react-query"; - -import { getAll } from "src/utilities/getAll"; - -import { queryPresets } from "./base"; -import { firewallQueries } from "./firewalls"; -import { profileQueries } from "./profile/profile"; - -import type { - APIError, - CreateNodeBalancerConfig, - CreateNodeBalancerPayload, - Filter, - Firewall, - NodeBalancer, - NodeBalancerConfig, - NodeBalancerStats, - Params, - PriceType, - ResourcePage, -} from "@linode/api-v4"; - -const getAllNodeBalancerTypes = () => - getAll((params) => getNodeBalancerTypes(params))().then( - (results) => results.data, - ); - -const getAllNodeBalancerConfigs = (id: number) => - getAll((params) => - getNodeBalancerConfigs(id, params), - )().then((data) => data.data); - -const getAllNodeBalancers = () => - getAll((params) => getNodeBalancers(params))().then( - (data) => data.data, - ); - -export const nodebalancerQueries = createQueryKeys("nodebalancers", { - nodebalancer: (id: number) => ({ - contextQueries: { - configurations: { - queryFn: () => getAllNodeBalancerConfigs(id), - queryKey: null, - }, - firewalls: { - queryFn: () => getNodeBalancerFirewalls(id), - queryKey: null, - }, - stats: { - queryFn: () => getNodeBalancerStats(id), - queryKey: null, - }, - }, - queryFn: () => getNodeBalancer(id), - queryKey: [id], - }), - nodebalancers: { - contextQueries: { - all: { - queryFn: getAllNodeBalancers, - queryKey: null, - }, - infinite: (filter: Filter = {}) => ({ - queryFn: ({ pageParam }) => - getNodeBalancers( - { page: pageParam as number, page_size: 25 }, - filter, - ), - queryKey: [filter], - }), - paginated: (params: Params = {}, filter: Filter = {}) => ({ - queryFn: () => getNodeBalancers(params, filter), - queryKey: [params, filter], - }), - }, - queryKey: null, - }, - types: { - queryFn: getAllNodeBalancerTypes, - queryKey: null, - }, -}); - -const useNodeBalancerStatsQuery = (id: number) => { - return useQuery({ - ...nodebalancerQueries.nodebalancer(id)._ctx.stats, - refetchInterval: 20000, - retry: false, - }); -}; - -const useNodeBalancersQuery = (params: Params, filter: Filter) => - useQuery, APIError[]>({ - ...nodebalancerQueries.nodebalancers._ctx.paginated(params, filter), - placeholderData: keepPreviousData, - }); - -const useNodeBalancerQuery = (id: number, enabled = true) => - useQuery({ - ...nodebalancerQueries.nodebalancer(id), - enabled, - }); - -const useNodebalancerUpdateMutation = (id: number) => { - const queryClient = useQueryClient(); - return useMutation>({ - mutationFn: (data) => updateNodeBalancer(id, data), - onSuccess(nodebalancer) { - // Invalidate paginated stores - queryClient.invalidateQueries({ - queryKey: nodebalancerQueries.nodebalancers.queryKey, - }); - // Update the NodeBalancer store - queryClient.setQueryData( - nodebalancerQueries.nodebalancer(id).queryKey, - nodebalancer, - ); - }, - }); -}; - -const useNodebalancerDeleteMutation = (id: number) => { - const queryClient = useQueryClient(); - return useMutation<{}, APIError[]>({ - mutationFn: () => deleteNodeBalancer(id), - onSuccess() { - // Remove NodeBalancer queries for this specific NodeBalancer - queryClient.removeQueries({ - queryKey: nodebalancerQueries.nodebalancer(id).queryKey, - }); - // Invalidate paginated stores - queryClient.invalidateQueries({ - queryKey: nodebalancerQueries.nodebalancers.queryKey, - }); - }, - }); -}; - -const useNodebalancerCreateMutation = () => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: createNodeBalancer, - onSuccess(nodebalancer, variables) { - // Invalidate paginated stores - queryClient.invalidateQueries({ - queryKey: nodebalancerQueries.nodebalancers.queryKey, - }); - // Prime the cache for this specific NodeBalancer - queryClient.setQueryData( - nodebalancerQueries.nodebalancer(nodebalancer.id).queryKey, - nodebalancer, - ); - // If a restricted user creates an entity, we must make sure grants are up to date. - queryClient.invalidateQueries({ - queryKey: profileQueries.grants.queryKey, - }); - - // If a NodeBalancer is assigned to a firewall upon creation, make sure we invalidate that firewall - // so it reflects the new entity. - if (variables.firewall_id) { - // Invalidate the paginated list of firewalls because GET /v4/networking/firewalls returns all firewall entities - queryClient.invalidateQueries({ - queryKey: firewallQueries.firewalls.queryKey, - }); - - // Invalidate the affected firewall - queryClient.invalidateQueries({ - queryKey: firewallQueries.firewall(variables.firewall_id).queryKey, - }); - } - }, - }); -}; - -const useNodebalancerConfigCreateMutation = (id: number) => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: (data) => createNodeBalancerConfig(id, data), - onSuccess(config) { - // Append new config to the configurations list - queryClient.setQueryData( - nodebalancerQueries.nodebalancer(id)._ctx.configurations.queryKey, - (previousData) => { - if (!previousData) { - return [config]; - } - return [...previousData, config]; - }, - ); - }, - }); -}; - -interface CreateNodeBalancerConfigWithConfig - extends Partial { - configId: number; -} - -const useNodebalancerConfigUpdateMutation = (nodebalancerId: number) => { - const queryClient = useQueryClient(); - return useMutation< - NodeBalancerConfig, - APIError[], - CreateNodeBalancerConfigWithConfig - >({ - mutationFn: ({ configId, ...data }) => - updateNodeBalancerConfig(nodebalancerId, configId, data), - onSuccess(config) { - // Update the config within the configs list - queryClient.setQueryData( - nodebalancerQueries.nodebalancer(nodebalancerId)._ctx.configurations - .queryKey, - (previousData) => { - if (!previousData) { - return [config]; - } - const indexOfConfig = previousData.findIndex( - (c) => c.id === config.id, - ); - if (indexOfConfig === -1) { - return [...previousData, config]; - } - const newConfigs = [...previousData]; - newConfigs[indexOfConfig] = config; - return newConfigs; - }, - ); - }, - }); -}; - -const useNodebalancerConfigDeleteMutation = (nodebalancerId: number) => { - const queryClient = useQueryClient(); - return useMutation<{}, APIError[], { configId: number }>({ - mutationFn: ({ configId }) => - deleteNodeBalancerConfig(nodebalancerId, configId), - onSuccess(_, vars) { - queryClient.setQueryData( - nodebalancerQueries.nodebalancer(nodebalancerId)._ctx.configurations - .queryKey, - (oldData) => { - return (oldData ?? []).filter( - (config) => config.id !== vars.configId, - ); - }, - ); - }, - }); -}; - -const useAllNodeBalancerConfigsQuery = (id: number) => - useQuery({ - ...nodebalancerQueries.nodebalancer(id)._ctx.configurations, - refetchInterval: 20000, - }); - -// Please don't use -const useAllNodeBalancersQuery = (enabled = true) => - useQuery({ - ...nodebalancerQueries.nodebalancers._ctx.all, - enabled, - }); - -const useInfiniteNodebalancersQuery = (filter: Filter) => - useInfiniteQuery, APIError[]>({ - ...nodebalancerQueries.nodebalancers._ctx.infinite(filter), - getNextPageParam: ({ page, pages }) => { - if (page === pages) { - return undefined; - } - return page + 1; - }, - initialPageParam: 1, - }); - -const useNodeBalancersFirewallsQuery = (nodebalancerId: number) => - useQuery, APIError[]>( - nodebalancerQueries.nodebalancer(nodebalancerId)._ctx.firewalls, - ); - -const useNodeBalancerTypesQuery = () => - useQuery({ - ...queryPresets.oneTimeFetch, - ...nodebalancerQueries.types, - }); - -const nodebalancerEventHandler = ({ - event, - invalidateQueries, -}: EventHandlerData) => { - const nodebalancerId = event.entity?.id; - - if (event.action.startsWith("nodebalancer_node")) { - // We don't store NodeBalancer nodes is React Query currently, so just skip these events - return; - } - - if (nodebalancerId === undefined) { - // Ignore events that don't have an associated NodeBalancer - return; - } - - if (event.action.startsWith("nodebalancer_config")) { - // If the event is about a NodeBalancer's configs, just invalidate the configs - invalidateQueries({ - queryKey: - nodebalancerQueries.nodebalancer(nodebalancerId)._ctx.configurations - .queryKey, - }); - } else { - // If we've made it here, the event is about a NodeBalancer - - // Invalidate the specific NodeBalancer - invalidateQueries({ - exact: true, - queryKey: nodebalancerQueries.nodebalancer(nodebalancerId).queryKey, - }); - - // Invalidate all paginated lists - invalidateQueries({ - queryKey: nodebalancerQueries.nodebalancers.queryKey, - }); - } -}; diff --git a/packages/volume-create/src/queries/placementGroups.ts b/packages/volume-create/src/queries/placementGroups.ts deleted file mode 100644 index a3351584002..00000000000 --- a/packages/volume-create/src/queries/placementGroups.ts +++ /dev/null @@ -1,283 +0,0 @@ -import { - assignLinodesToPlacementGroup, - createPlacementGroup, - deletePlacementGroup, - getPlacementGroup, - getPlacementGroups, - unassignLinodesFromPlacementGroup, - updatePlacementGroup, -} from "@linode/api-v4"; -import { createQueryKeys } from "@lukemorales/query-key-factory"; -import { - keepPreviousData, - useMutation, - useQuery, - useQueryClient, -} from "@tanstack/react-query"; - -import { getAll } from "src/utilities/getAll"; - -import { linodeQueries } from "./linodes/linodes"; -import { profileQueries } from "./profile/profile"; - -import type { - APIError, - AssignLinodesToPlacementGroupPayload, - CreatePlacementGroupPayload, - Filter, - Params, - PlacementGroup, - ResourcePage, - UnassignLinodesFromPlacementGroupPayload, - UpdatePlacementGroupPayload, -} from "@linode/api-v4"; - -const getAllPlacementGroupsRequest = ( - _params: Params = {}, - _filter: Filter = {}, -) => - getAll((params, filter) => - getPlacementGroups({ ...params, ..._params }, { ...filter, ..._filter }), - )().then((data) => data.data); - -export const placementGroupQueries = createQueryKeys("placement-groups", { - all: (params: Params = {}, filter: Filter = {}) => ({ - queryFn: () => getAllPlacementGroupsRequest(params, filter), - queryKey: [params, filter], - }), - paginated: (params: Params, filter: Filter) => ({ - queryFn: () => getPlacementGroups(params, filter), - queryKey: [params, filter], - }), - placementGroup: (placementGroupId: number) => ({ - queryFn: () => getPlacementGroup(placementGroupId), - queryKey: [placementGroupId], - }), -}); - -interface AllPlacementGroupsQueryOptions { - enabled?: boolean; - filter?: Filter; - params?: Params; -} - -const useAllPlacementGroupsQuery = ({ - enabled = true, - filter = {}, - params = {}, -}: AllPlacementGroupsQueryOptions) => - useQuery({ - enabled, - ...placementGroupQueries.all(params, filter), - }); - -const usePlacementGroupsQuery = ( - params: Params, - filter: Filter, - enabled: boolean = true, -) => - useQuery, APIError[]>({ - enabled, - placeholderData: keepPreviousData, - ...placementGroupQueries.paginated(params, filter), - }); - -const usePlacementGroupQuery = ( - placementGroupId: number, - enabled: boolean = true, -) => { - return useQuery({ - enabled, - ...placementGroupQueries.placementGroup(placementGroupId), - }); -}; - -const useCreatePlacementGroup = () => { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: createPlacementGroup, - onSuccess(placementGroup) { - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.paginated._def, - }); - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.all._def, - }); - queryClient.setQueryData( - placementGroupQueries.placementGroup(placementGroup.id).queryKey, - placementGroup, - ); - - // If a restricted user creates an entity, we must make sure grants are up to date. - queryClient.invalidateQueries({ - queryKey: profileQueries.grants.queryKey, - }); - }, - }); -}; - -const useMutatePlacementGroup = (id: number) => { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: (data) => updatePlacementGroup(id, data), - onSuccess(placementGroup) { - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.paginated._def, - }); - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.all._def, - }); - queryClient.setQueryData( - placementGroupQueries.placementGroup(id).queryKey, - placementGroup, - ); - }, - }); -}; - -const useDeletePlacementGroup = (id: number) => { - const queryClient = useQueryClient(); - - return useMutation<{}, APIError[]>({ - mutationFn: () => deletePlacementGroup(id), - onSuccess() { - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.paginated._def, - }); - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.all._def, - }); - queryClient.removeQueries({ - queryKey: placementGroupQueries.placementGroup(id).queryKey, - }); - }, - }); -}; - -const useAssignLinodesToPlacementGroup = (placementGroupId: number) => { - const queryClient = useQueryClient(); - - return useMutation< - PlacementGroup, - APIError[], - AssignLinodesToPlacementGroupPayload - >({ - mutationFn: (req) => assignLinodesToPlacementGroup(placementGroupId, req), - onSuccess: (_, variables) => { - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.paginated._def, - }); - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.all._def, - }); - queryClient.invalidateQueries({ - queryKey: - placementGroupQueries.placementGroup(placementGroupId).queryKey, - }); - - queryClient.invalidateQueries(linodeQueries.linodes); - - for (const linodeId of variables.linodes) { - queryClient.invalidateQueries({ - exact: true, - queryKey: linodeQueries.linode(linodeId).queryKey, - }); - } - }, - }); -}; - -const useUnassignLinodesFromPlacementGroup = (placementGroupId: number) => { - const queryClient = useQueryClient(); - return useMutation< - PlacementGroup, - APIError[], - UnassignLinodesFromPlacementGroupPayload - >({ - mutationFn: (req) => - unassignLinodesFromPlacementGroup(placementGroupId, req), - onSuccess: (_, variables) => { - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.paginated._def, - }); - queryClient.invalidateQueries({ - queryKey: placementGroupQueries.all._def, - }); - queryClient.invalidateQueries({ - queryKey: - placementGroupQueries.placementGroup(placementGroupId).queryKey, - }); - - queryClient.invalidateQueries(linodeQueries.linodes); - - for (const linodeId of variables.linodes) { - queryClient.invalidateQueries({ - exact: true, - queryKey: linodeQueries.linode(linodeId).queryKey, - }); - } - }, - }); -}; - -const placementGroupEventHandler = ({ - event, - invalidateQueries, -}: EventHandlerData) => { - const { action, entity, secondary_entity } = event; - // for assignment/unassignment events - // in the case of a migration, the assignment/unassignment events are happening asynchronously, - // without using the hook. We need to invalidate the placement group queries here. - // invalidateQueries({ queryKey: placementGroupQueries._def }); - // event looks as follow: - // { - // "id": {id}, - // "created": {created}, - // "seen": false, - // "read": false, - // "percent_complete": null, - // "time_remaining": null, - // "rate": null, - // "duration": null, - // "action": "placement_group_unassign", - // "username": {username}, - // "entity": { - // "label": {label}, - // "id": {id}, - // "type": "placement_group", - // "url": "/v4/placement/groups/{id}" - // }, - // "status": "notification", - // "secondary_entity": { - // "id": {id}, - // "type": "linode", - // "label": {label}, - // "url": "/v4/linode/instances/{id}" - // }, - // "message": "" - // } - if ( - action !== "placement_group_unassign" && - action !== "placement_group_assign" - ) { - return; - } - - if (entity && secondary_entity) { - invalidateQueries({ - queryKey: placementGroupQueries.placementGroup(entity.id).queryKey, - }); - invalidateQueries({ - queryKey: linodeQueries.linode(secondary_entity.id).queryKey, - }); - } - - invalidateQueries({ - queryKey: placementGroupQueries.paginated._def, - }); - invalidateQueries({ - queryKey: linodeQueries.linodes._ctx.all._def, - }); -}; diff --git a/packages/volume-create/src/queries/profile/profile.ts b/packages/volume-create/src/queries/profile/profile.ts index ed0d22ec297..76ee3e02998 100644 --- a/packages/volume-create/src/queries/profile/profile.ts +++ b/packages/volume-create/src/queries/profile/profile.ts @@ -21,7 +21,6 @@ import type { Profile, RequestOptions, } from "@linode/api-v4"; -import type { QueryClient } from "@tanstack/react-query"; export const profileQueries = createQueryKeys("profile", { appTokens: (params: Params = {}, filter: Filter = {}) => ({ @@ -65,19 +64,6 @@ export const useProfile = (options: RequestOptions = {}) => { }); }; -const updateProfileData = ( - newData: Partial, - queryClient: QueryClient, -): void => { - queryClient.setQueryData( - profileQueries.profile().queryKey, - (oldData: Profile) => ({ - ...oldData, - ...newData, - }), - ); -}; - export const useGrants = () => { const { data: profile } = useProfile(); return useQuery({ diff --git a/packages/volume-create/src/queries/regions/regions.ts b/packages/volume-create/src/queries/regions/regions.ts index b2a1681fd5d..ac56ea77fd6 100644 --- a/packages/volume-create/src/queries/regions/regions.ts +++ b/packages/volume-create/src/queries/regions/regions.ts @@ -10,7 +10,7 @@ import { getAllRegionsRequest, } from "./requests"; -import type { Region, RegionAvailability } from "@linode/api-v4/lib/regions"; +import type { Region } from "@linode/api-v4/lib/regions"; import type { APIError } from "@linode/api-v4/lib/types"; const regionQueries = createQueryKeys("regions", { @@ -37,17 +37,6 @@ const regionQueries = createQueryKeys("regions", { }, }); -const useRegionQuery = (regionId: string) => { - return useQuery({ - ...regionQueries.region(regionId), - enabled: Boolean(regionId), - select: (region) => ({ - ...region, - label: getNewRegionLabel(region), - }), - }); -}; - export const useRegionsQuery = () => useQuery({ ...regionQueries.regions, @@ -58,19 +47,3 @@ export const useRegionsQuery = () => label: getNewRegionLabel(region), })), }); - -const useRegionsAvailabilitiesQuery = (enabled: boolean = true) => - useQuery({ - ...regionQueries.availability._ctx.all, - enabled, - }); - -const useRegionAvailabilityQuery = ( - regionId: string, - enabled: boolean = true, -) => { - return useQuery({ - ...regionQueries.availability._ctx.region(regionId), - enabled, - }); -}; diff --git a/packages/volume-create/src/queries/vlans.ts b/packages/volume-create/src/queries/vlans.ts deleted file mode 100644 index 3a78d2edf82..00000000000 --- a/packages/volume-create/src/queries/vlans.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { getVlans } from "@linode/api-v4/lib/vlans"; -import { createQueryKeys } from "@lukemorales/query-key-factory"; -import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; - -import { getAll } from "src/utilities/getAll"; - -import type { APIError, Filter, ResourcePage, VLAN } from "@linode/api-v4"; - -const getAllVLANs = (): Promise => - getAll((params) => getVlans(params))().then(({ data }) => data); - -export const vlanQueries = createQueryKeys("vlans", { - all: { - queryFn: getAllVLANs, - queryKey: null, - }, - infinite: (filter: Filter = {}) => ({ - queryFn: ({ pageParam = 1 }) => - getVlans({ page: pageParam as number, page_size: 25 }, filter), - queryKey: [filter], - }), -}); - -const useVlansQuery = () => { - return useQuery(vlanQueries.all); -}; - -const useVLANsInfiniteQuery = (filter: Filter = {}, enabled = true) => { - return useInfiniteQuery, APIError[]>({ - getNextPageParam: ({ page, pages }) => { - if (page === pages) { - return undefined; - } - return page + 1; - }, - initialPageParam: 1, - ...vlanQueries.infinite(filter), - enabled, - }); -}; diff --git a/packages/volume-create/src/queries/volumes/volumes.ts b/packages/volume-create/src/queries/volumes/volumes.ts index 90f9693e8ed..f402127b28b 100644 --- a/packages/volume-create/src/queries/volumes/volumes.ts +++ b/packages/volume-create/src/queries/volumes/volumes.ts @@ -1,39 +1,18 @@ import { - attachVolume, - cloneVolume, createVolume, - deleteVolume, - detachVolume, getLinodeVolumes, getVolume, getVolumes, - migrateVolumes, - resizeVolume, - updateVolume, } from "@linode/api-v4"; import { createQueryKeys } from "@lukemorales/query-key-factory"; -import { - keepPreviousData, - useInfiniteQuery, - useMutation, - useQuery, - useQueryClient, -} from "@tanstack/react-query"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { accountQueries } from "../account/queries"; import { queryPresets } from "../base"; import { profileQueries } from "../profile/profile"; import { getAllVolumeTypes, getAllVolumes } from "./requests"; -import type { - AttachVolumePayload, - CloneVolumePayload, - ResizeVolumePayload, - UpdateVolumeRequest, - Volume, - VolumeRequestPayload, -} from "@linode/api-v4"; -import type { APIError, ResourcePage } from "@linode/api-v4/lib/types"; +import type { Volume, VolumeRequestPayload } from "@linode/api-v4"; +import type { APIError } from "@linode/api-v4/lib/types"; import type { Filter, Params, PriceType } from "@linode/api-v4/lib/types"; const volumeQueries = createQueryKeys("volumes", { @@ -74,115 +53,12 @@ const volumeQueries = createQueryKeys("volumes", { }), }); -const useVolumeQuery = (id: number, enabled = true) => { - return useQuery({ - ...volumeQueries.volume(id), - enabled, - }); -}; - -const useVolumesQuery = (params: Params, filter: Filter) => - useQuery, APIError[]>({ - ...volumeQueries.lists._ctx.paginated(params, filter), - placeholderData: keepPreviousData, - }); - export const useVolumeTypesQuery = () => useQuery({ ...volumeQueries.types, ...queryPresets.oneTimeFetch, }); -const useInfiniteVolumesQuery = (filter: Filter) => - useInfiniteQuery, APIError[]>({ - ...volumeQueries.lists._ctx.infinite(filter), - getNextPageParam: ({ page, pages }) => { - if (page === pages) { - return undefined; - } - return page + 1; - }, - initialPageParam: 1, - }); - -const useAllVolumesQuery = ( - params: Params = {}, - filter: Filter = {}, - enabled = true, -) => - useQuery({ - ...volumeQueries.lists._ctx.all(params, filter), - enabled, - }); - -const useLinodeVolumesQuery = ( - linodeId: number, - params: Params = {}, - filter: Filter = {}, - enabled = true, -) => - useQuery, APIError[]>({ - ...volumeQueries.linode(linodeId)._ctx.volumes(params, filter), - enabled, - placeholderData: keepPreviousData, - }); - -interface ResizeVolumePayloadWithId extends ResizeVolumePayload { - volumeId: number; -} - -const useResizeVolumeMutation = () => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: ({ volumeId, ...data }) => resizeVolume(volumeId, data), - onSuccess(volume) { - // Update the specific volume - queryClient.setQueryData( - volumeQueries.volume(volume.id).queryKey, - volume, - ); - // Invalidate all lists - queryClient.invalidateQueries({ - queryKey: volumeQueries.lists.queryKey, - }); - // If the volume is assigned to a Linode, invalidate that Linode's list - if (volume.linode_id) { - queryClient.invalidateQueries({ - queryKey: volumeQueries.linode(volume.linode_id)._ctx.volumes._def, - }); - } - }, - }); -}; - -interface CloneVolumePayloadWithId extends CloneVolumePayload { - volumeId: number; -} - -const useCloneVolumeMutation = () => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: ({ volumeId, ...data }) => cloneVolume(volumeId, data), - onSuccess() { - queryClient.invalidateQueries({ - queryKey: volumeQueries.lists.queryKey, - }); - }, - }); -}; - -const useDeleteVolumeMutation = () => { - const queryClient = useQueryClient(); - return useMutation<{}, APIError[], { id: number }>({ - mutationFn: ({ id }) => deleteVolume(id), - onSuccess() { - queryClient.invalidateQueries({ - queryKey: volumeQueries.lists.queryKey, - }); - }, - }); -}; - export const useCreateVolumeMutation = () => { const queryClient = useQueryClient(); return useMutation({ @@ -203,85 +79,3 @@ export const useCreateVolumeMutation = () => { }, }); }; - -const useVolumesMigrateMutation = () => { - const queryClient = useQueryClient(); - - return useMutation<{}, APIError[], number[]>({ - mutationFn: migrateVolumes, - onSuccess: () => { - // If a customer "force" migrates they will then see a - // `volume_migration_imminent` notification instead of - // the `volume_migration_scheduled` notification. - setTimeout(() => { - // Refetch notifications after 1.5 seconds. The API needs some time to process. - queryClient.invalidateQueries({ - queryKey: accountQueries.notifications.queryKey, - }); - }, 1500); - }, - }); -}; - -interface UpdateVolumePayloadWithId extends UpdateVolumeRequest { - volumeId: number; -} - -const useUpdateVolumeMutation = () => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: ({ volumeId, ...data }) => updateVolume(volumeId, data), - onSuccess(volume) { - // Update the specific volume - queryClient.setQueryData( - volumeQueries.volume(volume.id).queryKey, - volume, - ); - // Invalidate all lists - queryClient.invalidateQueries({ - queryKey: volumeQueries.lists.queryKey, - }); - // If the volume is assigned to a Linode, invalidate that Linodes's list - if (volume.linode_id) { - queryClient.invalidateQueries({ - queryKey: volumeQueries.linode(volume.linode_id)._ctx.volumes._def, - }); - } - }, - }); -}; - -interface AttachVolumePayloadWithId extends AttachVolumePayload { - volumeId: number; -} - -const useAttachVolumeMutation = () => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: ({ volumeId, ...data }) => attachVolume(volumeId, data), - onSuccess(volume) { - // Update the specific volume - queryClient.setQueryData( - volumeQueries.volume(volume.id).queryKey, - volume, - ); - // Invalidate all lists - queryClient.invalidateQueries({ - queryKey: volumeQueries.lists.queryKey, - }); - - // If the volume is assigned to a Linode, invalidate that Linode's list - if (volume.linode_id) { - queryClient.invalidateQueries({ - queryKey: volumeQueries.linode(volume.linode_id)._ctx.volumes._def, - }); - } - }, - }); -}; - -const useDetachVolumeMutation = () => { - return useMutation<{}, APIError[], { id: number }>({ - mutationFn: ({ id }) => detachVolume(id), - }); -}; diff --git a/packages/volume-create/src/queries/vpcs/requests.ts b/packages/volume-create/src/queries/vpcs/requests.ts deleted file mode 100644 index 20dd7891118..00000000000 --- a/packages/volume-create/src/queries/vpcs/requests.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { getVPCs } from "@linode/api-v4"; - -import { getAll } from "src/utilities/getAll"; - -import type { VPC } from "@linode/api-v4"; - -export const getAllVPCsRequest = () => - getAll((params) => getVPCs(params))().then((data) => data.data); diff --git a/packages/volume-create/src/queries/vpcs/vpcs.ts b/packages/volume-create/src/queries/vpcs/vpcs.ts deleted file mode 100644 index ff45c217608..00000000000 --- a/packages/volume-create/src/queries/vpcs/vpcs.ts +++ /dev/null @@ -1,202 +0,0 @@ -import { - CreateSubnetPayload, - CreateVPCPayload, - ModifySubnetPayload, - Subnet, - UpdateVPCPayload, - VPC, - createSubnet, - createVPC, - deleteSubnet, - deleteVPC, - getSubnet, - getSubnets, - getVPC, - getVPCs, - modifySubnet, - updateVPC, -} from "@linode/api-v4"; -import { createQueryKeys } from "@lukemorales/query-key-factory"; -import { - keepPreviousData, - useMutation, - useQuery, - useQueryClient, -} from "@tanstack/react-query"; - -import { getAllVPCsRequest } from "./requests"; - -import type { - APIError, - Filter, - Params, - ResourcePage, -} from "@linode/api-v4/lib/types"; - -// VPC queries -export const vpcQueries = createQueryKeys("vpcs", { - all: { - queryFn: getAllVPCsRequest, - queryKey: null, - }, - paginated: (params: Params = {}, filter: Filter = {}) => ({ - queryFn: () => getVPCs(params, filter), - queryKey: [params, filter], - }), - vpc: (vpcId: number) => ({ - contextQueries: { - subnets: { - contextQueries: { - paginated: (params: Params = {}, filter: Filter = {}) => ({ - queryFn: () => getSubnets(vpcId, params, filter), - queryKey: [params, filter], - }), - subnet: (subnetId: number) => ({ - queryFn: () => getSubnet(vpcId, subnetId), - queryKey: [subnetId], - }), - }, - queryKey: null, - }, - }, - queryFn: () => getVPC(vpcId), - queryKey: [vpcId], - }), -}); - -const useAllVPCsQuery = (enabled = true) => - useQuery({ - ...vpcQueries.all, - enabled, - }); - -const useVPCsQuery = (params: Params, filter: Filter, enabled = true) => { - return useQuery, APIError[]>({ - ...vpcQueries.paginated(params, filter), - enabled, - placeholderData: keepPreviousData, - }); -}; - -const useVPCQuery = (id: number, enabled: boolean = true) => - useQuery({ - ...vpcQueries.vpc(id), - enabled, - }); - -const useCreateVPCMutation = () => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: createVPC, - onSuccess(vpc) { - queryClient.invalidateQueries({ - queryKey: vpcQueries.all.queryKey, - }); - queryClient.invalidateQueries({ - queryKey: vpcQueries.paginated._def, - }); - queryClient.setQueryData(vpcQueries.vpc(vpc.id).queryKey, vpc); - }, - }); -}; - -const useUpdateVPCMutation = (id: number) => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: (data) => updateVPC(id, data), - onSuccess(vpc) { - queryClient.invalidateQueries({ - queryKey: vpcQueries.paginated._def, - }); - queryClient.setQueryData(vpcQueries.vpc(vpc.id).queryKey, vpc); - }, - }); -}; - -const useDeleteVPCMutation = (id: number) => { - const queryClient = useQueryClient(); - return useMutation<{}, APIError[]>({ - mutationFn: () => deleteVPC(id), - onSuccess() { - queryClient.invalidateQueries({ - queryKey: vpcQueries.all.queryKey, - }); - queryClient.invalidateQueries({ - queryKey: vpcQueries.paginated._def, - }); - queryClient.removeQueries({ - queryKey: vpcQueries.vpc(id).queryKey, - }); - }, - }); -}; - -// Subnet queries -const useSubnetsQuery = ( - vpcId: number, - params: Params, - filter: Filter, - enabled: boolean = true, -) => - useQuery, APIError[]>({ - ...vpcQueries.vpc(vpcId)._ctx.subnets._ctx.paginated(params, filter), - enabled, - placeholderData: keepPreviousData, - }); - -const useCreateSubnetMutation = (vpcId: number) => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: (data) => createSubnet(vpcId, data), - onSuccess() { - // New subnet created --> refresh the VPC queries (all, paginated, & individual), plus the /subnets VPC query - queryClient.invalidateQueries({ - queryKey: vpcQueries.all.queryKey, - }); - queryClient.invalidateQueries({ - queryKey: vpcQueries.paginated._def, - }); - queryClient.invalidateQueries({ - queryKey: vpcQueries.vpc(vpcId).queryKey, - }); - }, - }); -}; - -const useUpdateSubnetMutation = (vpcId: number, subnetId: number) => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: (data) => modifySubnet(vpcId, subnetId, data), - onSuccess() { - // New subnet created --> refresh the VPC queries (all, paginated, & individual), plus the /subnets VPC query - queryClient.invalidateQueries({ - queryKey: vpcQueries.all.queryKey, - }); - queryClient.invalidateQueries({ - queryKey: vpcQueries.paginated._def, - }); - queryClient.invalidateQueries({ - queryKey: vpcQueries.vpc(vpcId).queryKey, - }); - }, - }); -}; - -const useDeleteSubnetMutation = (vpcId: number, subnetId: number) => { - const queryClient = useQueryClient(); - return useMutation<{}, APIError[]>({ - mutationFn: () => deleteSubnet(vpcId, subnetId), - onSuccess() { - // New subnet created --> refresh the VPC queries (all, paginated, & individual), plus the /subnets VPC query - queryClient.invalidateQueries({ - queryKey: vpcQueries.all.queryKey, - }); - queryClient.invalidateQueries({ - queryKey: vpcQueries.paginated._def, - }); - queryClient.invalidateQueries({ - queryKey: vpcQueries.vpc(vpcId).queryKey, - }); - }, - }); -}; diff --git a/packages/volume-create/src/utilities/configs.ts b/packages/volume-create/src/utilities/configs.ts deleted file mode 100644 index 3461b67e9af..00000000000 --- a/packages/volume-create/src/utilities/configs.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Config } from "@linode/api-v4"; - -// This is a helper function to manually set interfaces related to VPCs to active. We call this function when rebooting/booting linodes; we specifically set the react queryCache -// to this in order to address the flickering 'Reboot Needed' status issue (see PR#9893). -// NOTE: This logic only works for linodes with one configuration/one vpc interface, and will lead to VERY CONFUSING results for linodes with multiple configurations. -export const manuallySetVPCConfigInterfacesToActive = ( - configs: Config[], -): Config[] => { - return configs.map((config) => { - return { - ...config, - interfaces: config.interfaces.map((linodeInterface) => { - if (linodeInterface.purpose === "vpc") { - return { ...linodeInterface, active: true }; - } else { - return linodeInterface; - } - }), - }; - }); -}; diff --git a/packages/volume-create/src/utilities/env.ts b/packages/volume-create/src/utilities/env.ts deleted file mode 100644 index 354dfa96849..00000000000 --- a/packages/volume-create/src/utilities/env.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Helps you parse an environment variable that is intended to be a boolean. - */ -export function getBooleanEnv(value: boolean | string | undefined) { - if (value === undefined) { - return undefined; - } - if (value === true || value === "true") { - return true; - } - return false; -} diff --git a/packages/volume-create/src/utilities/errorUtils.ts b/packages/volume-create/src/utilities/errorUtils.ts index 4dc1a8e07ad..c2119434ea4 100644 --- a/packages/volume-create/src/utilities/errorUtils.ts +++ b/packages/volume-create/src/utilities/errorUtils.ts @@ -1,5 +1,4 @@ import { APIError } from "@linode/api-v4/lib/types"; -import { pathOr } from "ramda"; import { DEFAULT_ERROR_MESSAGE } from "src/constants"; @@ -47,64 +46,3 @@ const isDefaultError = (errorResponse: APIError[]) => { errorResponse[0].reason === DEFAULT_ERROR_MESSAGE ); }; - -const getErrorStringOrDefault = ( - errors: APIError[] | string, - defaultError: string = "An unexpected error occurred.", -): string => { - if (typeof errors === "string") { - return errors; - } - const apiErrors = pathOr(errors, ["response", "data", "errors"], errors); - return pathOr(defaultError, [0, "reason"], apiErrors); -}; - -/** - * Returns a mapping of error fields to responses. Intended for mapping - * API errors to input fields or other field-specific displays. - * - * Any errors which do not have a field, or do not match any specified field, - * will be assigned to the 'none' key. If there are multiple errors in this category, - * only one will be included in the map. This is not ideal, but the point of this - * function is to make sure that if a request returns an error, some usable feedback - * will be available to the user. - * - * @example getErrorMap(['label','password'], errors) - * - * { - * 'label': 'This label is too long.', - * 'password': 'Must contain special characters.', - * 'none': 'You forgot to check for region errors.' - * } - * - * - * @param fields optional list of fields to include in the response object - * @param errors an API error response - */ -// type = GetReturnType> -const getErrorMap = ( - fields: T[] = [], - errors?: APIError[] | null, -): Partial> => { - if (!errors) { - return {} as Partial>; - } - return errors.reduce( - (accum, thisError) => { - if (thisError.field && fields.includes(thisError.field as T)) { - return { - ...accum, - // We generally want the first error that matches the field, - // so don't override it if it's already there - [thisError.field]: accum[thisError.field] || thisError.reason, - }; - } else { - return { - ...accum, - none: thisError.reason, - }; - } - }, - { none: undefined } as Record, - ) as Record<"none" | T, string | undefined>; -}; diff --git a/packages/volume-create/src/utilities/formatRegion.ts b/packages/volume-create/src/utilities/formatRegion.ts index a1a0d115a7d..44ec2be7193 100644 --- a/packages/volume-create/src/utilities/formatRegion.ts +++ b/packages/volume-create/src/utilities/formatRegion.ts @@ -3,20 +3,9 @@ import { COUNTRY_CODE_TO_CONTINENT_CODE, } from "@linode/api-v4"; -import type { Agreements, Country, Profile } from "@linode/api-v4"; +import type { Country } from "@linode/api-v4"; import type { Region } from "@linode/api-v4"; -interface GDPRConfiguration { - /** The user's agreements */ - agreements: Agreements | undefined; - /** The user's profile */ - profile: Profile | undefined; - /** The list of regions */ - regions: Region[] | undefined; - /** The ID of the selected region (e.g. 'eu-west') */ - selectedRegionId: string | undefined; -} - export const getRegionCountryGroup = (region: Region | undefined) => { if (!region) { return "Other"; @@ -31,61 +20,3 @@ export const getRegionCountryGroup = (region: Region | undefined) => { ? (CONTINENT_CODE_TO_CONTINENT[continentCode] ?? "Other") : "Other"; }; - -const getSelectedRegion = ( - regions: Region[], - selectedRegionId: string | undefined, -): Region | undefined => { - return regions.find((thisRegion) => selectedRegionId === thisRegion.id); -}; - -const getSelectedRegionGroup = ( - regions: Region[], - selectedRegionId: string | undefined, -): string | undefined => { - const selectedRegion = getSelectedRegion(regions, selectedRegionId); - - if (!selectedRegion) { - return undefined; - } - - return getRegionCountryGroup(selectedRegion); -}; - -const isEURegion = (regionContinent: string | undefined): boolean => { - return regionContinent === CONTINENT_CODE_TO_CONTINENT.EU; -}; - -/** - * - * @returns The group of the selected region, if it exists - * @example - * const { selectedRegionGroup, showGDPRCheckbox } = getGDPRDetails({ - * agreements, - * profile, - * regions, - * selectedRegionId, - * }); - */ -const getGDPRDetails = ({ - agreements, - profile, - regions, - selectedRegionId, -}: GDPRConfiguration): { - selectedRegionGroup?: string; - showGDPRCheckbox: boolean; -} => { - if (regions === undefined) { - return { selectedRegionGroup: undefined, showGDPRCheckbox: false }; - } - - const selectedRegionGroup = getSelectedRegionGroup(regions, selectedRegionId); - - const showGDPRCheckbox = - Boolean(!profile?.restricted) && - Boolean(!agreements?.eu_model) && - isEURegion(selectedRegionGroup); - - return { selectedRegionGroup, showGDPRCheckbox }; -}; diff --git a/packages/volume-create/src/utilities/formikErrorUtils.ts b/packages/volume-create/src/utilities/formikErrorUtils.ts index 08573323b5b..88a893c5b56 100644 --- a/packages/volume-create/src/utilities/formikErrorUtils.ts +++ b/packages/volume-create/src/utilities/formikErrorUtils.ts @@ -4,84 +4,8 @@ import { getAPIErrorOrDefault } from "./errorUtils"; import { isNilOrEmpty } from "./isNilOrEmpty"; import type { APIError } from "@linode/api-v4/lib/types"; -import type { FormikErrors } from "formik"; - -const getFormikErrorsFromAPIErrors = ( - errors: APIError[], - prefixToRemoveFromFields?: string, -): FormikErrors => { - return errors.reduce((acc: FormikErrors, error: APIError) => { - if (error.field) { - const field = prefixToRemoveFromFields - ? error.field.replace(prefixToRemoveFromFields, "") - : error.field; - - set(acc, field, error.reason); - } - return acc; - }, {}); -}; // regex used in the below set function -const onlyDigitsRegex = /^\d+$/; - -/** - * Helper for getFormikErrorsFromAPIErrors, sets the given value at a specified path of the given object. - * Note that while we are using this function in place of lodash's set, it is not an exact replacement. - * This method both mutates the passed in object and returns it. - * - * @param object — The object to modify. - * @param path — The path of the property to set. - * @param value — The value to set. - * @return — Returns object. - */ -const set = ( - obj: FormikErrors, - path: string, - value: string, -): FormikErrors => { - const parts = path.split(/\.|\[|\]/).filter(Boolean); - - // ensure that obj is not an array and that the path is prototype pollution safe - if (Array.isArray(obj) || !isPrototypePollutionSafe(parts)) { - return obj; - } - - parts.reduce((acc: Record, part: string, index: number) => { - if (index === parts.length - 1) { - // Last part, set the value - acc[part] = value; - } else if (part.match(onlyDigitsRegex)) { - // Handle array indices - const arrayIndex = parseInt(part, 10); - acc[arrayIndex] = - acc[arrayIndex] ?? (parts[index + 1].match(onlyDigitsRegex) ? [] : {}); - } else { - // Handle nested objects - const potentialNextVal = parts[index + 1].match(onlyDigitsRegex) - ? [] - : {}; - acc[part] = typeof acc[part] === "object" ? acc[part] : potentialNextVal; - } - return acc[part]; - }, obj); - - return obj; -}; - -/** - * Ensures a path cannot lead to a prototype pollution issue. - * - * @param path - The path to check - * @return - boolean depending on whether the path is safe or not - */ -const isPrototypePollutionSafe = (path: string[]): boolean => { - return path.reduce((safeSoFar, val) => { - const isCurKeySafe = - val !== "__proto__" && val !== "prototype" && val !== "constructor"; - return safeSoFar && isCurKeySafe; - }, true); -}; export const handleFieldErrors = ( callback: (error: unknown) => void, @@ -120,28 +44,3 @@ export const handleGeneralErrors = ( return callback(generalError); } }; - -const handleAPIErrors = ( - errors: APIError[], - setFieldError: (field: string, message: string) => void, - setError?: (message: string) => void, -) => { - errors.forEach((error: APIError) => { - if (error.field) { - /** - * The line below gets the field name because the API returns something like this... - * {"errors": [{"reason": "Invalid credit card number", "field": "data.card_number"}]} - * It takes 'data.card_number' and translates it to 'card_number' - */ - const key = error.field.split(".")[error.field.split(".").length - 1]; - if (key) { - setFieldError(key, error.reason); - } - } else { - // Put any general API errors into a - if (setError) { - setError(error.reason); - } - } - }); -}; diff --git a/packages/volume-create/src/utilities/getAll.ts b/packages/volume-create/src/utilities/getAll.ts index 26cff6cf384..7a5cd692bbb 100644 --- a/packages/volume-create/src/utilities/getAll.ts +++ b/packages/volume-create/src/utilities/getAll.ts @@ -15,12 +15,6 @@ type GetFunction = ( filters?: Filter, ) => Promise>; -type GetFromEntity = ( - entityId?: number, - params?: Params, - filters?: Filter, -) => Promise>; - interface GetAllData { data: T[]; results: number; diff --git a/packages/volume-create/src/utilities/pricing/constants.ts b/packages/volume-create/src/utilities/pricing/constants.ts index 47ab674c22c..11234f00e27 100644 --- a/packages/volume-create/src/utilities/pricing/constants.ts +++ b/packages/volume-create/src/utilities/pricing/constants.ts @@ -1,21 +1,3 @@ export const UNKNOWN_PRICE = "--.--"; -const PRICE_ERROR_TOOLTIP_TEXT = "There was an error loading the price."; -const PRICES_RELOAD_ERROR_NOTICE_TEXT = - "There was an error retrieving prices. Please reload and try again."; -const HA_UPGRADE_PRICE_ERROR_MESSAGE = - "Upgrading to HA is not available at this time. Try again later."; -const HA_PRICE_ERROR_MESSAGE = `The cost for HA control plane is not available at this time.`; // Other constants -const PLAN_SELECTION_NO_REGION_SELECTED_MESSAGE = - "Select a region to view plans and prices."; -const LKE_CREATE_CLUSTER_CHECKOUT_MESSAGE = - "Select a region, HA choice, and add a Node Pool to view pricing and create a cluster."; -const LKE_ENTERPRISE_CREATE_CLUSTER_CHECKOUT_MESSAGE = - "Select a region and add a Node Pool to view pricing and create a cluster."; -const DIFFERENT_PRICE_STRUCTURE_WARNING = - "The selected region has a different price structure."; -const MAGIC_DATE_THAT_DC_SPECIFIC_PRICING_WAS_IMPLEMENTED = - "2023-10-05 00:00:00Z"; -const DOCS_LINK_LABEL_DC_PRICING = "How Data Center Pricing Works"; -const DOCS_LINK_LABEL_APL_APPLICATIONS = "Available Applications"; diff --git a/packages/volume-create/src/utilities/pricing/dynamicPricing.ts b/packages/volume-create/src/utilities/pricing/dynamicPricing.ts index d4ff536d5a5..7d4a9763de6 100644 --- a/packages/volume-create/src/utilities/pricing/dynamicPricing.ts +++ b/packages/volume-create/src/utilities/pricing/dynamicPricing.ts @@ -1,25 +1,9 @@ -import { UNKNOWN_PRICE } from "./constants"; - import type { PriceType, Region, RegionPriceObject } from "@linode/api-v4"; interface RegionPrice extends RegionPriceObject { id: string; } -// TODO: Delete once all products are using /types endpoints. -interface DataCenterPricingOptions { - /** - * The base price for an entity. - * @example 5 or 5.50 - */ - basePrice: number; - /** - * The `id` of the region we intended to get the price for. - * @example us-east - */ - regionId: Region["id"] | undefined; -} - interface DataCenterPricingByTypeOptions { /** * The number of decimal places to return for the price. @@ -48,42 +32,6 @@ interface DataCenterPricingByTypeOptions { } // The key is a region id and the value is the percentage increase in price. -const priceIncreaseMap = { - "br-gru": 0.4, // Sao Paulo - "id-cgk": 0.2, // Jakarta -}; - -/** - * This function is used to calculate the dynamic pricing for a given entity, based on potential region increased costs. - * @example - * const price = getDCSpecificPrice({ - * basePrice: getVolumePrice(20), - * regionId: 'us-east', - * }); - * @returns a data center specific price - */ -const getDCSpecificPrice = ({ - basePrice, - regionId, -}: DataCenterPricingOptions) => { - if (!regionId || !basePrice) { - return undefined; - } - - if (regionId in priceIncreaseMap) { - const increaseFactor = - priceIncreaseMap[regionId as keyof typeof priceIncreaseMap]; - - if (increaseFactor !== undefined) { - // If increaseFactor is defined, it means the region has a price increase and we should apply it. - const increase = basePrice * increaseFactor; - - return (basePrice + increase).toFixed(2); - } - } - - return basePrice.toFixed(2); -}; /** * This function is used to calculate the dynamic pricing for a given entity, based on potential region increased costs. @@ -118,14 +66,3 @@ export const getDCSpecificPriceByType = ({ return price?.toFixed(decimalPrecision) ?? undefined; }; - -const renderMonthlyPriceToCorrectDecimalPlace = ( - monthlyPrice: null | number | undefined, -) => { - if (typeof monthlyPrice !== "number") { - return UNKNOWN_PRICE; - } - return Number.isInteger(monthlyPrice) - ? monthlyPrice - : monthlyPrice.toFixed(2); -};