From 70e2cd6f5e5aa55e09b057632c2d8df8f000e8ee Mon Sep 17 00:00:00 2001 From: Mohammed Hamdoune Date: Tue, 8 Oct 2024 11:56:09 +0200 Subject: [PATCH 01/10] feat(pci-load-balancer): sizeinput for create page ref: DTCORE-2666 Signed-off-by: Mohammed Hamdoune --- .../src/pages/create/SizeInput.component.tsx | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 packages/manager/apps/pci-load-balancer/src/pages/create/SizeInput.component.tsx diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/SizeInput.component.tsx b/packages/manager/apps/pci-load-balancer/src/pages/create/SizeInput.component.tsx new file mode 100644 index 000000000000..9f642258fed3 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/SizeInput.component.tsx @@ -0,0 +1,102 @@ +import { useCatalog } from '@ovh-ux/manager-pci-common'; +import { + TilesInputComponent, + useCatalogPrice, +} from '@ovh-ux/manager-react-components'; +import { OsdsText } from '@ovhcloud/ods-components/react'; +import { useTranslation } from 'react-i18next'; +import { ODS_TEXT_LEVEL, ODS_TEXT_SIZE } from '@ovhcloud/ods-components'; +import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming'; + +const SIZE_FLAVOUR_REGEX = /octavia-loadbalancer.loadbalancer-([sml]).hour.consumption/; + +export type TPlan = { + code: string; + price: number; + label: string; + technicalName: string; +}; + +const LabelComponent = ({ + item, + isSelected, +}: Readonly<{ + item: TPlan; + isSelected: boolean; +}>) => { + const { t: tCreate } = useTranslation('create'); + + const { getFormattedHourlyCatalogPrice } = useCatalogPrice(); + + return ( +
+
+ + {tCreate('octavia_load_balancer_create_size_flavour_title', { + sizeCode: item.label, + })} + +
+ + {tCreate( + `octavia_load_balancer_create_size_flavour_description_${item.code}`, + )} + +
+
+ + {getFormattedHourlyCatalogPrice(item.price)} + +
+
+
+ ); +}; + +export default function SizeInputComponent({ + value = null, + onInput, +}: Readonly<{ value?: TPlan; onInput: (item: TPlan) => void }>): JSX.Element { + const { data: catalog, isPending: isCatalogPending } = useCatalog(); + + const plans = (catalog?.addons + ? catalog.addons.reduce((filtered: TPlan[], addon) => { + const found = addon.planCode.match(SIZE_FLAVOUR_REGEX); + if (found) { + filtered.push({ + code: found[1], + price: addon.pricings[0].price, + label: found[1].toUpperCase(), + technicalName: addon.blobs.technical.name, + }); + } + return filtered; + }, []) + : [] + ).sort((a, b) => a.price - b.price); + + return ( + + items={plans} + value={value} + onInput={onInput} + label={(item) => ( + + )} + tileClass={{ active: 'h-full', inactive: 'h-full' }} + /> + ); +} From 87181e04725ace069ebb75e238061277577870bb Mon Sep 17 00:00:00 2001 From: Mohammed Hamdoune Date: Tue, 8 Oct 2024 11:59:51 +0200 Subject: [PATCH 02/10] feat(pci-load-balancer): create page first step ref: DTCORE-2668 Signed-off-by: Mohammed Hamdoune --- .../translations/create/Messages_de_DE.json | 42 +++++ .../translations/create/Messages_en_GB.json | 42 +++++ .../translations/create/Messages_es_ES.json | 42 +++++ .../translations/create/Messages_fr_CA.json | 42 +++++ .../translations/create/Messages_fr_FR.json | 42 +++++ .../translations/create/Messages_it_IT.json | 42 +++++ .../translations/create/Messages_pl_PL.json | 42 +++++ .../translations/create/Messages_pt_PT.json | 42 +++++ .../order-price/Messages_de_DE.json | 14 ++ .../order-price/Messages_en_GB.json | 14 ++ .../order-price/Messages_es_ES.json | 14 ++ .../order-price/Messages_fr_CA.json | 14 ++ .../order-price/Messages_fr_FR.json | 14 ++ .../order-price/Messages_it_IT.json | 14 ++ .../order-price/Messages_pl_PL.json | 14 ++ .../order-price/Messages_pt_PT.json | 14 ++ .../apps/pci-load-balancer/src/constants.ts | 24 +++ .../src/pages/create/Create.page.tsx | 111 +++++++++++++ .../src/pages/create/SizeInput.component.tsx | 8 +- .../src/pages/create/store.ts | 146 ++++++++++++++++++ .../src/pages/listing/Listing.page.tsx | 2 +- .../apps/pci-load-balancer/src/routes.tsx | 5 + 22 files changed, 736 insertions(+), 8 deletions(-) create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/create/Messages_de_DE.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/create/Messages_en_GB.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/create/Messages_es_ES.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/create/Messages_fr_CA.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/create/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/create/Messages_it_IT.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/create/Messages_pl_PL.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/create/Messages_pt_PT.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_de_DE.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_en_GB.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_es_ES.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_fr_CA.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_it_IT.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_pl_PL.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_pt_PT.json create mode 100644 packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx create mode 100644 packages/manager/apps/pci-load-balancer/src/pages/create/store.ts diff --git a/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_de_DE.json b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_de_DE.json new file mode 100644 index 000000000000..b5ef2c4aec5f --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_de_DE.json @@ -0,0 +1,42 @@ +{ + "octavia_load_balancer_create_title": "Einen Loadbalancer erstellen", + "octavia_load_balancer_create_description": "Mit unserem Loadbalancer-Dienst können Sie die Last Ihrer Anwendung in Echtzeit sicher und automatisch auf mehrere Nodes verteilen. Abhängig von den Anforderungen Ihrer Anwendung können Sie aus mehreren Modi auswählen, z. B. TCP, UDP oder HTTP/S, um die Last Ihrer Anwendung auszugleichen. Für SSL/TLS-Zertifikate bieten wir eine einfache Integration von Let's Encrypt an. Sie können aber auch Ihre eigenen Zertifikate hochladen.", + "octavia_load_balancer_create_size_title": "Größe des Loadbalancers auswählen", + "octavia_load_balancer_create_size_intro": "Je nach den Anforderungen Ihrer Anwendung in puncto Netzwerkbandbreite, Anzahl an Anfragen pro Sekunde oder SSL-Verbindungen können Sie zwischen verschiedenen Größen von Loadbalancern wählen. Mehr Informationen zu den Merkmalen der einzelnen Loadbalancer-Größen", + "octavia_load_balancer_create_size_intro_link": "Produktseite anzeigen", + "octavia_load_balancer_create_size_flavour_title": "Größe {{sizeCode}}", + "octavia_load_balancer_create_size_flavour_description_s": "Geeignet für kleine Anwendungen. Ideal für Tests und Entwicklungsumgebungen oder für kleine Projekte.", + "octavia_load_balancer_create_size_flavour_description_m": "Geeignet für die meisten Projekte mit mittlerem Traffic.", + "octavia_load_balancer_create_size_flavour_description_l": "Wenn der Traffic Ihrer Anwendungen seine höchste Dichte erreicht, benötigen Sie möglicherweise die beste Loadbalancer-Leistung, um dies zu bewältigen.", + "octavia_load_balancer_create_size_flavour_price_interval": " /Stunde abgerechnet.", + "octavia_load_balancer_create_region_title": "Wählen Sie eine Region aus", + "octavia_load_balancer_create_region_intro": "Bitte wählen Sie die Public-Cloud-Region aus, in der Ihr Loadbalancer gehostet wird. Wählen Sie aus den Public-Cloud-Regionen, in denen ein privates Netzwerk definiert ist, die Region aus, in der der Loadbalancer gehostet wird. Ist die gewünschte Region nicht in der Liste? Um mehr über die Verfügbarkeit der Funktionen je nach Region zu erfahren, ", + "octavia_load_balancer_create_region_link": "klicken Sie hier.", + "octavia_load_balancer_create_floating_ip_title": "Eine öffentliche IP-Adresse zuweisen", + "octavia_load_balancer_create_floating_ip_intro": "Um öffentlichen Traffic zu empfangen, müssen Sie Ihrem Loadbalancer eine Floating IP zuweisen.", + "octavia_load_balancer_create_floating_ip_field": "Eine öffentliche IP-Adresse (Floating IP) zuweisen", + "octavia_load_balancer_create_floating_ip_field_no_floating_ip": "Keine öffentlichen IP-Adressen", + "octavia_load_balancer_create_floating_ip_field_new_floating_ip": "Neue öffentliche IP", + "octavia_load_balancer_create_floating_ip_new_information": "Ihre Floating IP wird am Ende des Konfigurationsprozesses erstellt.", + "octavia_load_balancer_create_floating_ip_new_price": "Die neue Floating IP wird mit {{price}}", + "octavia_load_balancer_create_floating_ip_new_price_interval": " /Stunde abgerechnet.", + "octavia_load_balancer_create_floating_ip_no_floating_ip_information": "Wenn Sie keine öffentliche IP-Adresse zuweisen, ist Ihr Loadbalancer nur über ein privates Netzwerk erreichbar.", + "octavia_load_balancer_create_private_network_title": "Privates Netzwerk auswählen", + "octavia_load_balancer_create_private_network_intro": "Konfigurieren Sie das private Netzwerk, in dem Ihr Loadbalancer gehostet wird: Wenn Sie eine Floating IP mit Ihrem Loadbalancer verbinden möchten, muss das Subnet über eine Gateway IP verfügen.", + "octavia_load_balancer_create_private_network_field": "Privates Netzwerk auswählen", + "octavia_load_balancer_create_private_network_field_subnet": "Subnet auswählen", + "octavia_load_balancer_create_private_network_no_subnet_text": "Für das ausgewählte private Netzwerk ist kein Subnet mit definierter Gateway IP vorhanden: Wählen Sie ein anderes privates Netzwerk aus oder erstellen Sie ein neues.", + "octavia_load_balancer_create_private_network_no_gateway_text": "Wir haben festgestellt, dass im ausgewählten privaten Netzwerk/Subnet keine Gateway IP vorhanden ist. Wir werden sie für Sie erstellen, wenn der Loadbalancer erstellt wird.", + "octavia_load_balancer_create_private_network_no_gateway_text_price": "Preis der neuen Gateway IP:", + "octavia_load_balancer_create_gateway_price_interval": " /Stunde abgerechnet.", + "octavia_load_balancer_create_instance_title": "Instanzen hinzufügen", + "octavia_load_balancer_create_instance_intro": "Um Ihren Loadbalancer zu konfigurieren, definieren Sie Ihre Listener (Frontends) mit einem Port und einem Protokoll. Weisen Sie ihn anschließend einem Instanzen-Pool (Backends) zu, der über einen bestimmten Port und ein bestimmtes Protokoll erreichbar ist. Wenn Sie über mehrere Protokolle und/oder Ports pro Instanz verfügen, können Sie mehrere Pools pro Listener konfigurieren. Weitere Informationen dazu finden Sie in der Dokumentation.", + "octavia_load_balancer_create_instance_banner_text": "In diesem Assistenten können Sie bis zu {{maxListeners}} Listener mit bis zu {{maxInstances}} Instanzen hinzufügen. Wenn Sie weitere Optionen wünschen oder erweiterte Funktionen (wie Proxy-Protokolle, Health Check oder L7-Richtlinien) bearbeiten möchten, wechseln Sie in den Bearbeitungsmodus.", + "octavia_load_balancer_create_instance_banner_text_bold": "Sie können nach der Erstellung Ihres Loadbalancers im Bereich Mitglieder der Pool-Ansicht Mitglieder über IP-Adressen eingeben.", + "octavia_load_balancer_create_instance_banner_health_monitor_text": "Bei dieser ersten Integration des Loadbalancers in Ihr Kundencenter können die Health Monitors erst bei der Erstellung des Loadbalancers konfiguriert werden.", + "octavia_load_balancer_create_name_title": "Informationen", + "octavia_load_balancer_create_name_field_label": "Name des Loadbalancers", + "octavia_load_balancer_create_submit": "Einen Loadbalancer erstellen", + "octavia_load_balancer_create_banner": "Ihr Loadbalancer wird erstellt. Es dauert nur wenige Minuten.", + "octavia_load_balancer_create_name_field_help": "Darf ausschließlich Zahlen, Buchstaben, Unterstriche, Gedankenstriche und Punkte enthalten." +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_en_GB.json b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_en_GB.json new file mode 100644 index 000000000000..ea2a2c713cda --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_en_GB.json @@ -0,0 +1,42 @@ +{ + "octavia_load_balancer_create_title": "Create a Load Balancer", + "octavia_load_balancer_create_description": "With our Load Balancer service, you can securely and automatically balance your application’s load in real time, across several nodes. Depending your application’s needs, you can choose from several modes, such as TCP, UDP, or HTTP/S to balance your application’s load. For SSL/TLS certificates, we offer easy Let’s Encrypt integration, or you can also upload your own certificates.", + "octavia_load_balancer_create_size_title": "Choose the size of the Load Balancer", + "octavia_load_balancer_create_size_intro": "Depending on your application’s requirements in terms of network bandwidth, the number of requests per second or SSL connections, you can pick from different sizes of Load Balancer. Learn more about the capabilities of each Load Balancer size", + "octavia_load_balancer_create_size_intro_link": "Visit product page", + "octavia_load_balancer_create_size_flavour_title": "Size {{sizeCode}}", + "octavia_load_balancer_create_size_flavour_description_s": "Suitable for small applications. Ideal for testing and development environments, or for small projects.", + "octavia_load_balancer_create_size_flavour_description_m": "Suitable for most projects that handle moderate traffic.", + "octavia_load_balancer_create_size_flavour_description_l": "When your application traffic reaches its highest density, you may need the best Load Balancer performance to handle it.", + "octavia_load_balancer_create_size_flavour_price_interval": " /hour", + "octavia_load_balancer_create_region_title": "Select a region", + "octavia_load_balancer_create_region_intro": "Please select the Public Cloud region where your Load Balancer will be hosted. From the list of Public Cloud regions with defined private network, choose the region where the Load Balancer will be hosted. Can't find the region you want in the list? To learn more about feature availability by region, ", + "octavia_load_balancer_create_region_link": "Click here ", + "octavia_load_balancer_create_floating_ip_title": "Attach a Public IP", + "octavia_load_balancer_create_floating_ip_intro": "To receive public traffic, you need to attach a Floating IP to your Load Balancer", + "octavia_load_balancer_create_floating_ip_field": "Attach a Public IP (Floating IP)", + "octavia_load_balancer_create_floating_ip_field_no_floating_ip": "No Public IP", + "octavia_load_balancer_create_floating_ip_field_new_floating_ip": "New Public IP", + "octavia_load_balancer_create_floating_ip_new_information": "Your Floating IP will be created at the end of the configuration process.", + "octavia_load_balancer_create_floating_ip_new_price": "The new Floating IP will be charged at {{price}}", + "octavia_load_balancer_create_floating_ip_new_price_interval": " /hour", + "octavia_load_balancer_create_floating_ip_no_floating_ip_information": "By choosing not to attach any Public IPs, your Load Balancer will only be accessible from a private network.", + "octavia_load_balancer_create_private_network_title": "Select a private network", + "octavia_load_balancer_create_private_network_intro": "Configure the private network on which your Load Balancer will be hosted: if you want a Floating IP to be associated with your Load Balancer, the subnet must have an IP gateway", + "octavia_load_balancer_create_private_network_field": "Select a private network", + "octavia_load_balancer_create_private_network_field_subnet": "Select a subnet", + "octavia_load_balancer_create_private_network_no_subnet_text": "The private network selected does not have a subnet with an IP gateway defined: choose or Create another private network.", + "octavia_load_balancer_create_private_network_no_gateway_text": "We have detected a missing gateway in the selected private network/subnet. We will set up one for you when you create the load balancer", + "octavia_load_balancer_create_private_network_no_gateway_text_price": "Price of the new gateway:", + "octavia_load_balancer_create_gateway_price_interval": " /hour", + "octavia_load_balancer_create_instance_title": "Add instances", + "octavia_load_balancer_create_instance_intro": "To configure your Load Balancer, define your front-end listeners with a port and a protocol. Then assign it to a pool of instances (back ends) that can be reached on a specific port and protocol. If you have multiple protocols and/or ports per instance, you can configure multiple pools per listener. Please refer to the documentation", + "octavia_load_balancer_create_instance_banner_text": "You can add up to {{maxListeners}} listeners with a maximum of {{maxInstances}} instances in this wizard. For more options or to edit more advanced features (such as proxy protocols, health check, or L7 policies), switch to edit mode.", + "octavia_load_balancer_create_instance_banner_text_bold": "You can enter members via IP after creating your Load Balancer in the members section of the Pool view", + "octavia_load_balancer_create_name_title": "Information", + "octavia_load_balancer_create_name_field_label": "Load Balancer name", + "octavia_load_balancer_create_submit": "Create a Load Balancer", + "octavia_load_balancer_create_banner": "Creating your Load Balancer... This will only take a few minutes.", + "octavia_load_balancer_create_instance_banner_health_monitor_text": "In this first integration of the Load Balancer in the OVHcloud Control Panel, the Health Monitors can only be configured when the Load Balancer is created.", + "octavia_load_balancer_create_name_field_help": "May contain numbers, letters, underscores, dashes and full stops only." +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_es_ES.json b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_es_ES.json new file mode 100644 index 000000000000..0547c3f5e2b2 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_es_ES.json @@ -0,0 +1,42 @@ +{ + "octavia_load_balancer_create_title": "Crear un Load Balancer:", + "octavia_load_balancer_create_description": "Nuestro servicio Load Balancer le permite repartir la carga de su aplicación en tiempo real entre varios nodos, de forma automática y segura. En función de las necesidades de su aplicación, puede elegir entre varios modos, como por ejemplo TCP, UDP o HTTP/S, para equilibrar la carga de su aplicación. Para los certificados SSL/TLS, puede optar por la integración fácil de Let's encrypt o puede descargar sus propios certificados.", + "octavia_load_balancer_create_size_title": "Elegir el tamaño del Load Balancer", + "octavia_load_balancer_create_size_intro": "Dispone de distintos tamaños de Load Balancer, en función de las necesidades de su aplicación en materia de ancho de banda de red, de número de peticiones por segundo o de conexiones SSL. Para más información sobre la capacidad de cada tamaño del Load Balancer,", + "octavia_load_balancer_create_size_intro_link": "consulte la página del producto.", + "octavia_load_balancer_create_size_flavour_title": "Tamaño {{sizeCode}}", + "octavia_load_balancer_create_size_flavour_description_s": "Adaptado a las pequeñas aplicaciones. Ideal para pruebas y entornos de desarrollo o para proyectos pequeños.", + "octavia_load_balancer_create_size_flavour_description_m": "Adecuado para la mayoría de proyectos que conllevan un tráfico medio.", + "octavia_load_balancer_create_size_flavour_description_l": "Cuando el tráfico de sus aplicaciones alcanza su densidad más alta, es posible que necesite el mejor rendimiento del Load Balancer para poder hacer frente a la carga.", + "octavia_load_balancer_create_size_flavour_price_interval": " /hora", + "octavia_load_balancer_create_region_title": "Seleccione una región", + "octavia_load_balancer_create_region_intro": "Seleccione la región de Public Cloud en la que desee alojar el Load Balancer. Elija entre las regiones de Public Cloud en las que se haya definido una red privada aquella en la que desea alojar el Load Balancer. ¿No encuentra la región que desea en la lista? Para saber más sobre la disponibilidad de las funciones según la región, ", + "octavia_load_balancer_create_region_link": "Haga clic aquí", + "octavia_load_balancer_create_floating_ip_title": "Asociar una IP pública", + "octavia_load_balancer_create_floating_ip_intro": "Para recibir tráfico público, debe asociar una Floating IP a su Load Balancer", + "octavia_load_balancer_create_floating_ip_field": "Asociar una IP pública (Floating IP)", + "octavia_load_balancer_create_floating_ip_field_no_floating_ip": "Ninguna IP pública", + "octavia_load_balancer_create_floating_ip_field_new_floating_ip": "Nueva IP pública", + "octavia_load_balancer_create_floating_ip_new_information": "La Floating IP se creará una vez completado el proceso de configuración.", + "octavia_load_balancer_create_floating_ip_new_price": "La nueva Floating IP tendrá un coste de {{price}}", + "octavia_load_balancer_create_floating_ip_new_price_interval": " /hora", + "octavia_load_balancer_create_floating_ip_no_floating_ip_information": "Si decide no asociar ninguna IP pública, solo podrá acceder al Load Balancer desde una red privada.", + "octavia_load_balancer_create_private_network_title": "Seleccione una red privada", + "octavia_load_balancer_create_private_network_intro": "Configurar la red privada en la desea alojar el Load Balancer. Si quiere que una Floating IP esté asociada a su Load Balancer, la subred debe tener una Gateway IP", + "octavia_load_balancer_create_private_network_field": "Elija una red privada", + "octavia_load_balancer_create_private_network_field_subnet": "Seleccione una subred", + "octavia_load_balancer_create_private_network_no_subnet_text": "La red privada seleccionada no tiene una subred con una gateway IP definida: elija o cree otra red privada.", + "octavia_load_balancer_create_private_network_no_gateway_text": "Hemos detectado que falta una puerta de enlace en la red privada / subred seleccionada. Vamos a crear una para usted al crear el Load Balancer", + "octavia_load_balancer_create_private_network_no_gateway_text_price": "Precio del nuevo servicio Gateway:", + "octavia_load_balancer_create_gateway_price_interval": " /hora", + "octavia_load_balancer_create_instance_title": "Añadir instancias", + "octavia_load_balancer_create_instance_intro": "Para configurar el Load Balancer, defina sus listeners (front-ends) con un puerto y un protocolo. A continuación, asígnelo a un pool de instancias (back-ends) accesibles en un puerto y protocolo específicos. Si dispone de varios protocolos y/o puertos por instancia, puede configurar varios pools por listener consultando la documentación", + "octavia_load_balancer_create_instance_banner_text": "Puede agregar hasta {{maxListeners}} listeners con un máximo de {{maxInstances}} instancias en este asistente. Para tener acceso a otras opciones o para editar funcionalidades más avanzadas (como los protocolos proxy, el health check o las políticas L7), pase al modo edición.", + "octavia_load_balancer_create_instance_banner_text_bold": "Una vez creado el Load Balancer, podrá introducir miembros a través de las IP en la sección miembros de la vista Pool", + "octavia_load_balancer_create_instance_banner_health_monitor_text": "En esta primera integración del Load Balancer en el área de cliente, los Health Monitors solo pueden configurarse al crear el Load Balancer.", + "octavia_load_balancer_create_name_title": "Información", + "octavia_load_balancer_create_name_field_label": "Nombre del Load Balancer", + "octavia_load_balancer_create_submit": "Crear un Load Balancer:", + "octavia_load_balancer_create_banner": "Creando el Load Balancer. Solo tardará unos minutos.", + "octavia_load_balancer_create_name_field_help": "Solo debe contener números, letras, guiones, guiones bajos o puntos." +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_fr_CA.json b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_fr_CA.json new file mode 100644 index 000000000000..c6876d510c61 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_fr_CA.json @@ -0,0 +1,42 @@ +{ + "octavia_load_balancer_create_title": "Créer un Load Balancer", + "octavia_load_balancer_create_description": "Avec notre service Load Balancer, vous pouvez équilibrer de manière sécurisée et automatique la charge de votre application en temps réel, sur plusieurs nœuds. En fonction des besoins de votre application, vous pouvez choisir parmi plusieurs modes, comme TCP, UDP ou HTTP/S pour équilibrer la charge de votre application. Pour les certificats SSL/TLS, nous proposons une intégration facile de Let's encrypt ou vous pouvez également télécharger vos propres certificats.", + "octavia_load_balancer_create_size_title": "Sélectionner la taille du Load Balancer", + "octavia_load_balancer_create_size_intro": "Selon les besoins de votre application en matière de bande passante réseau, du nombre de requêtes par seconde ou de connexions SSL, vous pouvez choisir parmi différentes tailles de Load Balancer. Pour en savoir plus sur les capacités de chaque taille de Load Balancer", + "octavia_load_balancer_create_size_intro_link": "Consulter la page produit", + "octavia_load_balancer_create_size_flavour_title": "Taille {{sizeCode}}", + "octavia_load_balancer_create_size_flavour_description_s": "Adaptée aux petites applications. Idéale pour des tests et environnements de développement ou pour de petits projets.", + "octavia_load_balancer_create_size_flavour_description_m": "Convient à la plupart des projets traitant un trafic moyen.", + "octavia_load_balancer_create_size_flavour_description_l": "Lorsque le trafic de vos applications atteint sa plus haute densité, vous pouvez avoir besoin des meilleures performances du Load Balancer pour y faire face.", + "octavia_load_balancer_create_size_flavour_price_interval": " / heure", + "octavia_load_balancer_create_region_title": "Sélectionnez une région", + "octavia_load_balancer_create_region_intro": "Veuillez sélectionner la région Public Cloud où votre Load Balancer sera hébergé. Parmi les regions public cloud où un réseau privé est défini, sélectionnez celle où le Load Balancer sera hébergé. Vous ne trouvez pas la région que vous désirez dans la liste ? Pour en savoir plus sur la disponibilité des fonctions par région, ", + "octavia_load_balancer_create_region_link": "Cliquez ici", + "octavia_load_balancer_create_floating_ip_title": "Attacher une IP Publique", + "octavia_load_balancer_create_floating_ip_intro": "Pour recevoir du trafic public, vous devez attacher une Floating IP à votre Load Balancer", + "octavia_load_balancer_create_floating_ip_field": "Attacher une IP Publique (Floating IP)", + "octavia_load_balancer_create_floating_ip_field_no_floating_ip": "Aucune IP Publique", + "octavia_load_balancer_create_floating_ip_field_new_floating_ip": "Nouvelle IP Publique", + "octavia_load_balancer_create_floating_ip_new_information": "La création de votre Floating IP se fera à la fin du processus de configuration.", + "octavia_load_balancer_create_floating_ip_new_price": "La nouvelle Floating IP sera facturée {{price}}", + "octavia_load_balancer_create_floating_ip_new_price_interval": " / heure", + "octavia_load_balancer_create_floating_ip_no_floating_ip_information": "En choississant de n'attacher aucune IP Publique, votre Load balancer ne sera accessible que depuis un réseau privé.", + "octavia_load_balancer_create_private_network_title": "Sélectionnez un réseau privé", + "octavia_load_balancer_create_private_network_intro": "Configurer le réseau privé dans lequel sera hébergé votre load balancer : si vous souhaitez qu'une floating IP soit associé à votre load balancer, le subnet doit avoir une gateway ip", + "octavia_load_balancer_create_private_network_field": "Choisissez un réseau privé", + "octavia_load_balancer_create_private_network_field_subnet": "Choisissez un subnet", + "octavia_load_balancer_create_private_network_no_subnet_text": "Le réseau privé sélectionné n'a pas de subnet ayant une gateway ip définie: choisissez ou Créez un autre réseau privé.", + "octavia_load_balancer_create_private_network_no_gateway_text": "Nous avons détecté qu'il manque une gateway sur le réseau privé / subnet selectionné. Nous allons en créer une pour vous au moment de la création du load balancer", + "octavia_load_balancer_create_private_network_no_gateway_text_price": "Prix de la nouvelle gateway :", + "octavia_load_balancer_create_gateway_price_interval": " / heure", + "octavia_load_balancer_create_instance_title": "Ajouter des instances", + "octavia_load_balancer_create_instance_intro": "Pour configurer votre Load Balancer, définissez vos listeners (front ends) avec un port et un protocole. Attribuez-le ensuite à un pool d'instances (back ends) joignables sur un port et un protocole spécifiques. Si vous disposez de plusieurs protocoles et/ou ports par instance, vous pouvez configurer plusieurs pools par listener en consultant la documentation", + "octavia_load_balancer_create_instance_banner_text": "Vous pouvez ajouter jusqu'à {{maxListeners}} listeners avec {{maxInstances}} instances au maximum dans cet assistant. Pour plus d'options ou pour éditer des fonctionnalités plus avancées (comme les protocoles proxy, le health check ou les politiques L7), passez en mode édition.", + "octavia_load_balancer_create_instance_banner_text_bold": "Vous pourrez saisir des membres via IP après la création de votre Load Balancer dans la section membres de la vue Pool", + "octavia_load_balancer_create_instance_banner_health_monitor_text": "Dans cette première intégration du Load Balancer dans votre espace client, les Health Monitors ne sont configurables qu'à la création du LoadBalancer.", + "octavia_load_balancer_create_name_title": "Informations", + "octavia_load_balancer_create_name_field_label": "Nom du Load Balancer", + "octavia_load_balancer_create_name_field_help": "Doit uniquement contenir des nombres, lettres, underscores, tirets ou points.", + "octavia_load_balancer_create_submit": "Créer un Load Balancer", + "octavia_load_balancer_create_banner": "Votre Load Balancer est en cours de création. Cela ne prendra que quelques minutes." +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_fr_FR.json b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_fr_FR.json new file mode 100644 index 000000000000..c6876d510c61 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_fr_FR.json @@ -0,0 +1,42 @@ +{ + "octavia_load_balancer_create_title": "Créer un Load Balancer", + "octavia_load_balancer_create_description": "Avec notre service Load Balancer, vous pouvez équilibrer de manière sécurisée et automatique la charge de votre application en temps réel, sur plusieurs nœuds. En fonction des besoins de votre application, vous pouvez choisir parmi plusieurs modes, comme TCP, UDP ou HTTP/S pour équilibrer la charge de votre application. Pour les certificats SSL/TLS, nous proposons une intégration facile de Let's encrypt ou vous pouvez également télécharger vos propres certificats.", + "octavia_load_balancer_create_size_title": "Sélectionner la taille du Load Balancer", + "octavia_load_balancer_create_size_intro": "Selon les besoins de votre application en matière de bande passante réseau, du nombre de requêtes par seconde ou de connexions SSL, vous pouvez choisir parmi différentes tailles de Load Balancer. Pour en savoir plus sur les capacités de chaque taille de Load Balancer", + "octavia_load_balancer_create_size_intro_link": "Consulter la page produit", + "octavia_load_balancer_create_size_flavour_title": "Taille {{sizeCode}}", + "octavia_load_balancer_create_size_flavour_description_s": "Adaptée aux petites applications. Idéale pour des tests et environnements de développement ou pour de petits projets.", + "octavia_load_balancer_create_size_flavour_description_m": "Convient à la plupart des projets traitant un trafic moyen.", + "octavia_load_balancer_create_size_flavour_description_l": "Lorsque le trafic de vos applications atteint sa plus haute densité, vous pouvez avoir besoin des meilleures performances du Load Balancer pour y faire face.", + "octavia_load_balancer_create_size_flavour_price_interval": " / heure", + "octavia_load_balancer_create_region_title": "Sélectionnez une région", + "octavia_load_balancer_create_region_intro": "Veuillez sélectionner la région Public Cloud où votre Load Balancer sera hébergé. Parmi les regions public cloud où un réseau privé est défini, sélectionnez celle où le Load Balancer sera hébergé. Vous ne trouvez pas la région que vous désirez dans la liste ? Pour en savoir plus sur la disponibilité des fonctions par région, ", + "octavia_load_balancer_create_region_link": "Cliquez ici", + "octavia_load_balancer_create_floating_ip_title": "Attacher une IP Publique", + "octavia_load_balancer_create_floating_ip_intro": "Pour recevoir du trafic public, vous devez attacher une Floating IP à votre Load Balancer", + "octavia_load_balancer_create_floating_ip_field": "Attacher une IP Publique (Floating IP)", + "octavia_load_balancer_create_floating_ip_field_no_floating_ip": "Aucune IP Publique", + "octavia_load_balancer_create_floating_ip_field_new_floating_ip": "Nouvelle IP Publique", + "octavia_load_balancer_create_floating_ip_new_information": "La création de votre Floating IP se fera à la fin du processus de configuration.", + "octavia_load_balancer_create_floating_ip_new_price": "La nouvelle Floating IP sera facturée {{price}}", + "octavia_load_balancer_create_floating_ip_new_price_interval": " / heure", + "octavia_load_balancer_create_floating_ip_no_floating_ip_information": "En choississant de n'attacher aucune IP Publique, votre Load balancer ne sera accessible que depuis un réseau privé.", + "octavia_load_balancer_create_private_network_title": "Sélectionnez un réseau privé", + "octavia_load_balancer_create_private_network_intro": "Configurer le réseau privé dans lequel sera hébergé votre load balancer : si vous souhaitez qu'une floating IP soit associé à votre load balancer, le subnet doit avoir une gateway ip", + "octavia_load_balancer_create_private_network_field": "Choisissez un réseau privé", + "octavia_load_balancer_create_private_network_field_subnet": "Choisissez un subnet", + "octavia_load_balancer_create_private_network_no_subnet_text": "Le réseau privé sélectionné n'a pas de subnet ayant une gateway ip définie: choisissez ou Créez un autre réseau privé.", + "octavia_load_balancer_create_private_network_no_gateway_text": "Nous avons détecté qu'il manque une gateway sur le réseau privé / subnet selectionné. Nous allons en créer une pour vous au moment de la création du load balancer", + "octavia_load_balancer_create_private_network_no_gateway_text_price": "Prix de la nouvelle gateway :", + "octavia_load_balancer_create_gateway_price_interval": " / heure", + "octavia_load_balancer_create_instance_title": "Ajouter des instances", + "octavia_load_balancer_create_instance_intro": "Pour configurer votre Load Balancer, définissez vos listeners (front ends) avec un port et un protocole. Attribuez-le ensuite à un pool d'instances (back ends) joignables sur un port et un protocole spécifiques. Si vous disposez de plusieurs protocoles et/ou ports par instance, vous pouvez configurer plusieurs pools par listener en consultant la documentation", + "octavia_load_balancer_create_instance_banner_text": "Vous pouvez ajouter jusqu'à {{maxListeners}} listeners avec {{maxInstances}} instances au maximum dans cet assistant. Pour plus d'options ou pour éditer des fonctionnalités plus avancées (comme les protocoles proxy, le health check ou les politiques L7), passez en mode édition.", + "octavia_load_balancer_create_instance_banner_text_bold": "Vous pourrez saisir des membres via IP après la création de votre Load Balancer dans la section membres de la vue Pool", + "octavia_load_balancer_create_instance_banner_health_monitor_text": "Dans cette première intégration du Load Balancer dans votre espace client, les Health Monitors ne sont configurables qu'à la création du LoadBalancer.", + "octavia_load_balancer_create_name_title": "Informations", + "octavia_load_balancer_create_name_field_label": "Nom du Load Balancer", + "octavia_load_balancer_create_name_field_help": "Doit uniquement contenir des nombres, lettres, underscores, tirets ou points.", + "octavia_load_balancer_create_submit": "Créer un Load Balancer", + "octavia_load_balancer_create_banner": "Votre Load Balancer est en cours de création. Cela ne prendra que quelques minutes." +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_it_IT.json b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_it_IT.json new file mode 100644 index 000000000000..11e0f4114467 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_it_IT.json @@ -0,0 +1,42 @@ +{ + "octavia_load_balancer_create_title": "Crea un Load Balancer", + "octavia_load_balancer_create_description": "Il Load Balancer di OVHcloud permette di equilibrare il carico delle applicazioni su più nodi in tempo reale, in modo automatico e sicuro. In base alle esigenze dell’applicazione, è possibile scegliere tra diverse modalità, come TCP, UDP o HTTP/S per equilibrare il carico dell’applicazione. Per i certificati SSL/TLS, offriamo un’integrazione semplice con Let's Encrypt. In alternativa, puoi scaricare i tuoi certificati.", + "octavia_load_balancer_create_size_title": "Seleziona la dimensione del Load Balancer", + "octavia_load_balancer_create_size_intro": "In base alle necessità dell’applicazione in termini di banda passante di rete, numero di richieste al secondo o connessioni SSL, puoi scegliere tra diverse dimensioni di Load Balancer. Scopri di più sulle capacità di ogni dimensione del Load Balancer", + "octavia_load_balancer_create_size_intro_link": "Consulta la pagina del prodotto", + "octavia_load_balancer_create_size_flavour_title": "Dimensione {{sizeCode}}", + "octavia_load_balancer_create_size_flavour_description_s": "Ideale per piccole applicazioni. Ideale per test e ambienti di sviluppo o per piccoli progetti.", + "octavia_load_balancer_create_size_flavour_description_m": "Adatto alla maggior parte dei progetti con un traffico medio.", + "octavia_load_balancer_create_size_flavour_description_l": "Quando il traffico delle applicazioni raggiunge la massima densità, potresti aver bisogno delle performance più elevate offerte dal Load Balancer.", + "octavia_load_balancer_create_size_flavour_price_interval": " /ora", + "octavia_load_balancer_create_region_title": "Seleziona una Region", + "octavia_load_balancer_create_region_intro": "Seleziona la Region Public Cloud in cui sarà ospitato il Load Balancer. Tra le Region Public Cloud in cui è definita una rete privata, seleziona quella in cui verrà ospitato il Load Balancer. Non trovi la Region che cerchi nella lista? Per maggiori informazioni sulla disponibilità delle funzioni per Region, ", + "octavia_load_balancer_create_region_link": "Clicca qui", + "octavia_load_balancer_create_floating_ip_title": "Associa un IP pubblico", + "octavia_load_balancer_create_floating_ip_intro": "Per ricevere traffico pubblico, devi associare un Floating IP al Load Balancer", + "octavia_load_balancer_create_floating_ip_field": "Associa un IP Pubblico (Floating IP)", + "octavia_load_balancer_create_floating_ip_field_no_floating_ip": "Nessun IP Pubblico", + "octavia_load_balancer_create_floating_ip_field_new_floating_ip": "Nuovo IP Pubblico", + "octavia_load_balancer_create_floating_ip_new_information": "Il Floating IP verrà creato al termine del processo di configurazione.", + "octavia_load_balancer_create_floating_ip_new_price": "Il nuovo Floating IP sarà fatturato {{price}}", + "octavia_load_balancer_create_floating_ip_new_price_interval": " /ora", + "octavia_load_balancer_create_floating_ip_no_floating_ip_information": "Scegliendo di non associare IP pubblici, il Load Balancer sarà accessibile esclusivamente da una rete privata.", + "octavia_load_balancer_create_private_network_title": "Selezionare una rete privata", + "octavia_load_balancer_create_private_network_intro": "Configura la rete privata in cui sarà ospitato il Load Balancer : per associare un floating IP al Load Balancer, la sottorete deve disporre di un gateway ip", + "octavia_load_balancer_create_private_network_field": "Seleziona una rete privata", + "octavia_load_balancer_create_private_network_field_subnet": "Scegli una sottorete", + "octavia_load_balancer_create_private_network_no_subnet_text": "La rete privata selezionata non ha una sottorete con un gateway ip definito: scegli o Crea un'altra rete privata.", + "octavia_load_balancer_create_private_network_no_gateway_text": "Abbiamo rilevato che manca un gateway sulla rete privata / subnet selezionata. Ne creeremo una per te al momento della creazione del Load Balancer", + "octavia_load_balancer_create_private_network_no_gateway_text_price": "Prezzo del nuovo gateway:", + "octavia_load_balancer_create_gateway_price_interval": " /ora", + "octavia_load_balancer_create_instance_title": "Aggiungi istanze", + "octavia_load_balancer_create_instance_intro": "Per configurare il Load Balancer, definisci i tuoi listener (front end) con una porta e un protocollo. Dopodiché, assegnalo a un pool di istanze (back end) raggiungibili su una porta e un protocollo specifici. Se disponi di più protocolli e/o porte per istanza, puoi configurare più pool per listener consultando la documentazione", + "octavia_load_balancer_create_instance_banner_text": "Con questo assistente è possibile aggiungere fino a {{maxListeners}} listeners con un massimo di {{maxInstances}} istanze. Per ulteriori opzioni o per modificare funzionalità più avanzate (come i protocolli proxy, l'health check o le policy L7), passa alla modalità modifica.", + "octavia_load_balancer_create_instance_banner_text_bold": "Dopo la creazione del Load Balancer, puoi inserire i membri tramite IP nella sezione membri della vista Pool", + "octavia_load_balancer_create_instance_banner_health_monitor_text": "In questa prima integrazione del Load Balancer nello Spazio Cliente, gli Health Monitors possono essere configurati solo dopo la creazione del Load Balancer.", + "octavia_load_balancer_create_name_title": "Informazioni", + "octavia_load_balancer_create_name_field_label": "Nome del Load Balancer", + "octavia_load_balancer_create_submit": "Crea un Load Balancer", + "octavia_load_balancer_create_banner": "Creazione del Load Balancer in corso. Ci vorranno solo pochi minuti.", + "octavia_load_balancer_create_name_field_help": "Può contenere esclusivamente numeri, lettere, underscore, trattini o punti." +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_pl_PL.json b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_pl_PL.json new file mode 100644 index 000000000000..98b1dbdcc109 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_pl_PL.json @@ -0,0 +1,42 @@ +{ + "octavia_load_balancer_create_title": "Stwórz Load Balancer", + "octavia_load_balancer_create_description": "Dzięki naszej usłudze Load Balancer możesz w bezpieczny i automatyczny sposób równoważyć obciążenie aplikacji na wielu węzłach w czasie rzeczywistym. W zależności od potrzeb Twojej aplikacji możesz wybrać jeden z kilku trybów, takich jak TCP, UDP lub HTTP/S, aby zrównoważyć jej obciążenie. W przypadku certyfikatów SSL/TLS oferujemy nieskomplikowaną integrację Let's encrypt lub możliwość pobrania Twoich własnych certyfikatów.", + "octavia_load_balancer_create_size_title": "Wybierz rozmiar Load Balancera", + "octavia_load_balancer_create_size_intro": "W zależności od potrzeb aplikacji w zakresie przepustowości sieci, liczby zapytań na sekundę lub połączeń SSL, możesz wybrać spośród różnych rozmiarów Load Balancera. Dowiedz się więcej o wydajności Load Balancera dla każdego rozmiaru", + "octavia_load_balancer_create_size_intro_link": "Odwiedź stronę produktową", + "octavia_load_balancer_create_size_flavour_title": "Rozmiar {{sizeCode}}", + "octavia_load_balancer_create_size_flavour_description_s": "Odpowiedni dla małych aplikacji Idealny do testów i środowisk programistycznych lub do małych projektów.", + "octavia_load_balancer_create_size_flavour_description_m": "Odpowiedni dla większości projektów o średnim natężeniu ruchu.", + "octavia_load_balancer_create_size_flavour_description_l": "W momencie, gdy ruch generowany przez Twoje aplikacje osiągnie najwyższą gęstość, możesz potrzebować Load Balancera o większej wydajności.", + "octavia_load_balancer_create_size_flavour_price_interval": " / godz.", + "octavia_load_balancer_create_region_title": "Wybierz region", + "octavia_load_balancer_create_region_intro": "Wybierz region Public Cloud, w którym hostowany będzie Twój Load Balancer. Spośród regionów Public Cloud, w których zdefiniowana jest sieć prywatna, wybierz ten, w którym hostowany będzie Load Balancer. Interesujący Cię region nie figuruje na liście? Aby uzyskać więcej informacji na temat dostępności funkcji dla każdego regionu, ", + "octavia_load_balancer_create_region_link": "Kliknij tutaj", + "octavia_load_balancer_create_floating_ip_title": "Przypisz publiczny adres IP", + "octavia_load_balancer_create_floating_ip_intro": "Aby odbierać ruch do sieci publicznej, musisz przypisać adres Floating IP do Twojego Load Balancera", + "octavia_load_balancer_create_floating_ip_field": "Przypisz publiczny adres IP (Floating IP)", + "octavia_load_balancer_create_floating_ip_field_no_floating_ip": "Brak publicznych adresów IP", + "octavia_load_balancer_create_floating_ip_field_new_floating_ip": "Nowy publiczny adres IP", + "octavia_load_balancer_create_floating_ip_new_information": "Adres Floating IP zostanie utworzony na koniec procesu konfiguracji.", + "octavia_load_balancer_create_floating_ip_new_price": "Nowy adres Floating IP będzie płatny {{price}}", + "octavia_load_balancer_create_floating_ip_new_price_interval": " / godz.", + "octavia_load_balancer_create_floating_ip_no_floating_ip_information": "Jeśli nie przypiszesz żadnego publicznego adresu IP, Twój Load Balancer będzie dostępny tylko z poziomu sieci prywatnej.", + "octavia_load_balancer_create_private_network_title": "Wybierz sieć prywatną", + "octavia_load_balancer_create_private_network_intro": "Konfiguracja sieci prywatnej, w której hostowany będzie Twój Load Balancer: jeśli chcesz, aby Floating IP był powiązany z Twoim Load Balancerem, subnet musi posiadać Gateway IP", + "octavia_load_balancer_create_private_network_field": "Wybierz sieć prywatną", + "octavia_load_balancer_create_private_network_field_subnet": "Wybierz subnet", + "octavia_load_balancer_create_private_network_no_subnet_text": "Wybrana sieć prywatna nie ma subnetu ze zdefiniowanym Gateway IP: wybierz lub Utwórz inną sieć prywatną.", + "octavia_load_balancer_create_private_network_no_gateway_text": "Wykryliśmy, że w wybranej sieci prywatnej / subnecie brakuje bramki (gateway). Stworzymy ją dla Ciebie w momencie dodawania Load Balancera", + "octavia_load_balancer_create_private_network_no_gateway_text_price": "Cena nowej bramki (gateway):", + "octavia_load_balancer_create_gateway_price_interval": " / godz.", + "octavia_load_balancer_create_instance_title": "Dodaj instancje", + "octavia_load_balancer_create_instance_intro": "Aby skonfigurować Load Balancer, zdefiniuj Twoje listeners (front-end), używając portu i protokołu. Następnie przypisz go do danego poola instancji (back-end), z którym możesz się łączyć na określonym porcie i protokole. Jeśli dysponujesz kilkoma protokołami i/lub portami na instancję, możesz skonfigurować kilka pooli na listener, zapoznając się z dokumentacją", + "octavia_load_balancer_create_instance_banner_text": "W tym kreatorze możesz dodać maksymalnie {{maxListeners}} listenery(-ów) z maksymalnie {{maxInstances}} instancjami. Aby uzyskać więcej opcji lub edytować bardziej zaawansowane funkcje (takie jak protokoły proxy, health check lub zasady L7), przejdź do trybu edycji.", + "octavia_load_balancer_create_instance_banner_text_bold": "Po utworzeniu Twojego Load Balancera będziesz mieć możliwość wpisania użytkowników za pośrednictwem adresów IP w sekcji Użytkownicy widoku Pool", + "octavia_load_balancer_create_instance_banner_health_monitor_text": "Przy pierwszej integracji Load Balancera z poziomu Panelu klienta, usługi Health Monitors można konfigurować tylko podczas tworzenia LoadBalancera.", + "octavia_load_balancer_create_name_title": "Informacje", + "octavia_load_balancer_create_name_field_label": "Nazwa Load Balancera", + "octavia_load_balancer_create_submit": "Stwórz Load Balancer", + "octavia_load_balancer_create_banner": "Trwa tworzenie Load Balancera. Zajmie to tylko kilka minut.", + "octavia_load_balancer_create_name_field_help": "Może zawierać jedynie cyfry, litery, podkreślenia, myślniki lub kropki." +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_pt_PT.json b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_pt_PT.json new file mode 100644 index 000000000000..7cbc842ca9dd --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/create/Messages_pt_PT.json @@ -0,0 +1,42 @@ +{ + "octavia_load_balancer_create_title": "Criar um Load Balancer", + "octavia_load_balancer_create_description": "Com o nosso serviço Load Balancer, pode equilibrar a carga da sua aplicação em tempo real em vários nós, de forma segura e automática. Em função das necessidades da sua aplicação, pode escolher entre vários modos, como TCP, UDP ou HTTP/S para equilibrar a carga da sua aplicação. Para os certificados SSL/TLS, propomos uma integração fácil de Let's encrypt ou pode também descarregar os seus próprios certificados.", + "octavia_load_balancer_create_size_title": "Selecionar o tamanho do Load Balancer", + "octavia_load_balancer_create_size_intro": "Dependendo das necessidades da sua aplicação em matéria de largura de banda de rede, número de pedidos por segundo ou de ligações SSL, pode escolher entre diferentes tamanhos de Load Balancer. Para saber mais sobre as capacidades de cada tamanho de Load Balancer,", + "octavia_load_balancer_create_size_intro_link": "consulte a página do produto", + "octavia_load_balancer_create_size_flavour_title": "Tamanho {{sizeCode}}", + "octavia_load_balancer_create_size_flavour_description_s": "Adequado para pequenas aplicações. Ideal para testes e ambientes de desenvolvimento ou para pequenos projetos.", + "octavia_load_balancer_create_size_flavour_description_m": "Adequado para a maioria dos projetos que lidam com tráfego médio.", + "octavia_load_balancer_create_size_flavour_description_l": "Quando o tráfego das suas aplicações atingir o seu pico de densidade, poderá necessitar dos melhores desempenhos do Load Balancer para fazer face ao mesmo.", + "octavia_load_balancer_create_size_flavour_price_interval": " /hora", + "octavia_load_balancer_create_region_title": "Selecione uma região", + "octavia_load_balancer_create_region_intro": "Selecione a região Public Cloud onde ficará alojado o seu Load Balancer. De entre as regiões Public Cloud onde existe uma rede privada definida, selecione a região onde o Load Balancer ficará alojado. Não encontra a região que deseja na lista? Para saber mais sobre a disponibilidade das funções por região, ", + "octavia_load_balancer_create_region_link": "Clique aqui", + "octavia_load_balancer_create_floating_ip_title": "Associar um IP público", + "octavia_load_balancer_create_floating_ip_intro": "Para receber tráfego público, deve associar um Floating IP ao seu Load Balancer", + "octavia_load_balancer_create_floating_ip_field": "Associar um IP público (Floating IP)", + "octavia_load_balancer_create_floating_ip_field_no_floating_ip": "Nenhum IP público", + "octavia_load_balancer_create_floating_ip_field_new_floating_ip": "Novo IP público", + "octavia_load_balancer_create_floating_ip_new_information": "A criação do seu Floating IP será efetuada no final do processo de configuração.", + "octavia_load_balancer_create_floating_ip_new_price": "O novo Floating IP será faturado a {{price}}", + "octavia_load_balancer_create_floating_ip_new_price_interval": " /hora", + "octavia_load_balancer_create_floating_ip_no_floating_ip_information": "Ao optar por não associar nenhum IP público, o seu Load Balancer estará apenas acessível a partir de uma rede privada.", + "octavia_load_balancer_create_private_network_title": "Escolha uma rede privada", + "octavia_load_balancer_create_private_network_intro": "Configurar a rede privada na qual será alojado o seu load balancer : se deseja que um Floating IP seja associado ao seu load balancer, a subrede deve ter um gateway IP", + "octavia_load_balancer_create_private_network_field": "Selecione uma rede privada", + "octavia_load_balancer_create_private_network_field_subnet": "Escolha uma subrede", + "octavia_load_balancer_create_private_network_no_subnet_text": "A rede privada selecionada não tem nenhuma subrede com gateway IP definido: escolha outra ou Crie outra rede privada.", + "octavia_load_balancer_create_private_network_no_gateway_text": "Detetámos que falta um gateway na rede privada/subrede selecionada. Iremos criar um para si no momento da criação do load balancer", + "octavia_load_balancer_create_private_network_no_gateway_text_price": "Preço do novo gateway:", + "octavia_load_balancer_create_gateway_price_interval": " /hora", + "octavia_load_balancer_create_instance_title": "Adicionar instâncias", + "octavia_load_balancer_create_instance_intro": "Para configurar o Load Balancer, defina os seus listeners (front-ends) com uma porta e um protocolo. Em seguida, atribua-o a um pool de instâncias (backends) acessíveis numa porta e num protocolo específicos. Se tiver vários protocolos e/ou portas por instância, pode configurar vários pools por listener consultando os manuais", + "octavia_load_balancer_create_instance_banner_text": "Pode adicionar até {{maxListeners}} listeners com {{maxInstances}} instâncias no máximo, neste assistente. Para mais opções ou para editar funcionalidades mais avançadas (como os protocolos proxy, o health check ou as políticas L7), passe para o modo de edição.", + "octavia_load_balancer_create_instance_banner_text_bold": "Pode introduzir membros via IP depois de criar o seu Load Balancer na secção membros da vista Pool", + "octavia_load_balancer_create_instance_banner_health_monitor_text": "Nesta primeira integração do Load Balancer na sua Área de Cliente, os Health Monitors só são configuráveis aquando da criação do Load Balancer.", + "octavia_load_balancer_create_name_title": "Informações", + "octavia_load_balancer_create_name_field_label": "Nome do Load Balancer", + "octavia_load_balancer_create_submit": "Criar um Load Balancer", + "octavia_load_balancer_create_banner": "A criação do seu Load Balancer está em curso. Isto levará apenas alguns minutos.", + "octavia_load_balancer_create_name_field_help": "Deve apenas conter algarismos, letras, underscores, traços ou pontos." +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_de_DE.json b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_de_DE.json new file mode 100644 index 000000000000..89f7bd24f03c --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_de_DE.json @@ -0,0 +1,14 @@ +{ + "order_catalog_price_free": "Inklusive", + "order_catalog_price_tax_excl_label": "{{price}} zzgl. MwSt.", + "order_catalog_price_tax_incl_label": "{{ price }} inkl. MwSt.", + "order_catalog_price_gst_excl_label": "{{ price }} zzgl. GST", + "order_catalog_price_gst_incl_label": "{{ price }} inkl. GST", + "order_catalog_price_interval_day": "Tag", + "order_catalog_price_interval_month": "Monat", + "order_catalog_price_interval_year": "Jahr", + "order_catalog_price_from": "Von", + "order_catalog_price_to": "um", + "order_catalog_price_interval_hour": "Stunde", + "order_catalog_price_beta_free": "Während der Beta-Phase kostenlos" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_en_GB.json b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_en_GB.json new file mode 100644 index 000000000000..e05d48778e9d --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_en_GB.json @@ -0,0 +1,14 @@ +{ + "order_catalog_price_free": "Included", + "order_catalog_price_tax_excl_label": "{{ price }} ex. VAT", + "order_catalog_price_tax_incl_label": "{{price}} incl. VAT", + "order_catalog_price_gst_excl_label": "{{ price }} ex. VAT", + "order_catalog_price_gst_incl_label": "{{ price }} incl. VAT", + "order_catalog_price_interval_day": "day", + "order_catalog_price_interval_month": "month", + "order_catalog_price_interval_year": "year", + "order_catalog_price_from": "From", + "order_catalog_price_to": "to", + "order_catalog_price_interval_hour": "hour", + "order_catalog_price_beta_free": "Free during the beta period" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_es_ES.json b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_es_ES.json new file mode 100644 index 000000000000..22811cda48cf --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_es_ES.json @@ -0,0 +1,14 @@ +{ + "order_catalog_price_free": "Incluido", + "order_catalog_price_tax_excl_label": "{{ price }} + IVA", + "order_catalog_price_tax_incl_label": "{{ price }} IVA incl.", + "order_catalog_price_gst_excl_label": "{{ price }} + GST", + "order_catalog_price_gst_incl_label": "{{ price }} GST incl.", + "order_catalog_price_interval_day": "día", + "order_catalog_price_interval_month": "mes", + "order_catalog_price_interval_year": "año", + "order_catalog_price_from": "De", + "order_catalog_price_to": "a", + "order_catalog_price_interval_hour": "hora", + "order_catalog_price_beta_free": "Gratuito durante la beta" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_fr_CA.json b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_fr_CA.json new file mode 100644 index 000000000000..5bb37d7e855a --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_fr_CA.json @@ -0,0 +1,14 @@ +{ + "order_catalog_price_free": "Inclus", + "order_catalog_price_tax_excl_label": "{{ price }} HT", + "order_catalog_price_tax_incl_label": "{{ price }} TTC", + "order_catalog_price_gst_excl_label": "{{ price }} ex. GST", + "order_catalog_price_gst_incl_label": "{{ price }} incl. GST", + "order_catalog_price_interval_day": "jour", + "order_catalog_price_interval_month": "mois", + "order_catalog_price_interval_year": "an", + "order_catalog_price_interval_hour": "heure", + "order_catalog_price_from": "De", + "order_catalog_price_to": "à", + "order_catalog_price_beta_free": "Gratuit pendant la bêta" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_fr_FR.json b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_fr_FR.json new file mode 100644 index 000000000000..5bb37d7e855a --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_fr_FR.json @@ -0,0 +1,14 @@ +{ + "order_catalog_price_free": "Inclus", + "order_catalog_price_tax_excl_label": "{{ price }} HT", + "order_catalog_price_tax_incl_label": "{{ price }} TTC", + "order_catalog_price_gst_excl_label": "{{ price }} ex. GST", + "order_catalog_price_gst_incl_label": "{{ price }} incl. GST", + "order_catalog_price_interval_day": "jour", + "order_catalog_price_interval_month": "mois", + "order_catalog_price_interval_year": "an", + "order_catalog_price_interval_hour": "heure", + "order_catalog_price_from": "De", + "order_catalog_price_to": "à", + "order_catalog_price_beta_free": "Gratuit pendant la bêta" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_it_IT.json b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_it_IT.json new file mode 100644 index 000000000000..283fcd8998fc --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_it_IT.json @@ -0,0 +1,14 @@ +{ + "order_catalog_price_free": "Incluso", + "order_catalog_price_tax_excl_label": "{{ price }} +IVA", + "order_catalog_price_tax_incl_label": "{{price}} IVA incl.", + "order_catalog_price_gst_excl_label": "{{ price }} GST escl.", + "order_catalog_price_gst_incl_label": "{{ price }} GST incl.", + "order_catalog_price_interval_day": "giorno", + "order_catalog_price_interval_month": "mese", + "order_catalog_price_interval_year": "anno", + "order_catalog_price_from": "Da", + "order_catalog_price_to": "a", + "order_catalog_price_interval_hour": "ora", + "order_catalog_price_beta_free": "Gratis durante la fase beta." +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_pl_PL.json b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_pl_PL.json new file mode 100644 index 000000000000..31965fdf7cb7 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_pl_PL.json @@ -0,0 +1,14 @@ +{ + "order_catalog_price_free": "Zawarte w cenie", + "order_catalog_price_tax_excl_label": "{{price}} netto", + "order_catalog_price_tax_incl_label": "{{price}} brutto", + "order_catalog_price_gst_excl_label": "{{price}} netto", + "order_catalog_price_gst_incl_label": "{{price}} brutto", + "order_catalog_price_interval_day": "dzień", + "order_catalog_price_interval_month": "miesiąc", + "order_catalog_price_interval_year": "rok", + "order_catalog_price_from": "Z", + "order_catalog_price_to": "o", + "order_catalog_price_interval_hour": "godzina", + "order_catalog_price_beta_free": "Bezpłatnie w wersji beta." +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_pt_PT.json b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_pt_PT.json new file mode 100644 index 000000000000..96fb3cd7d2f1 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/order-price/Messages_pt_PT.json @@ -0,0 +1,14 @@ +{ + "order_catalog_price_free": "Incluído", + "order_catalog_price_tax_excl_label": "{{ price }} + IVA", + "order_catalog_price_tax_incl_label": "{{ price }} IVA incl.", + "order_catalog_price_gst_excl_label": "{{ price }} + IVA", + "order_catalog_price_gst_incl_label": "{{ price }} c/ IVA incl.", + "order_catalog_price_interval_day": "dia", + "order_catalog_price_interval_month": "mês", + "order_catalog_price_interval_year": "ano", + "order_catalog_price_from": "De", + "order_catalog_price_to": "a", + "order_catalog_price_interval_hour": "hora", + "order_catalog_price_beta_free": "Gratuito em versão beta." +} diff --git a/packages/manager/apps/pci-load-balancer/src/constants.ts b/packages/manager/apps/pci-load-balancer/src/constants.ts index 2c19b867eba1..c9e75b9e7230 100644 --- a/packages/manager/apps/pci-load-balancer/src/constants.ts +++ b/packages/manager/apps/pci-load-balancer/src/constants.ts @@ -54,6 +54,30 @@ export const ACTION_LABELS = { [ACTIONS.REJECT]: 'Reject', }; +export const PRODUCT_LINK = { + FR: 'https://www.ovhcloud.com/fr/public-cloud/load-balancer/', + DE: 'https://www.ovhcloud.com/de/public-cloud/load-balancer/', + ES: 'https://www.ovhcloud.com/es-es/public-cloud/load-balancer/', + WE: 'https://www.ovhcloud.com/en-ie/public-cloud/load-balancer/', + IE: 'https://www.ovhcloud.com/en-ie/public-cloud/load-balancer/', + IT: 'https://www.ovhcloud.com/it/public-cloud/load-balancer/', + NL: 'https://www.ovhcloud.com/nl/public-cloud/load-balancer/', + PL: 'https://www.ovhcloud.com/pl/public-cloud/load-balancer/', + PT: 'https://www.ovhcloud.com/pt/public-cloud/load-balancer/', + GB: 'https://www.ovhcloud.com/en-gb/public-cloud/load-balancer/', + CA: 'https://www.ovhcloud.com/en-ca/public-cloud/load-balancer/', + QC: 'https://www.ovhcloud.com/fr-ca/public-cloud/load-balancer/', + WS: 'https://www.ovhcloud.com/es/public-cloud/load-balancer/', + MA: 'https://www.ovhcloud.com/fr-ma/public-cloud/load-balancer/', + SN: 'https://www.ovhcloud.com/fr-sn/public-cloud/load-balancer/', + TN: 'https://www.ovhcloud.com/fr-tn/public-cloud/load-balancer/', + AU: 'https://www.ovhcloud.com/en-au/public-cloud/load-balancer/', + SG: 'https://www.ovhcloud.com/en-sg/public-cloud/load-balancer/', + ASIA: 'https://www.ovhcloud.com/asia/public-cloud/load-balancer/', + US: 'https://us.ovhcloud.com/en/public-cloud/load-balancer/', + DEFAULT: 'https://www.ovhcloud.com/en/public-cloud/load-balancer/', +}; + export const LOAD_BALANCER_LOGS_SERVICE_GUIDE_LINK = { DEFAULT: 'https://help.ovhcloud.com/csm/en-public-cloud-network-loadbalancer-logs-forward?id=kb_article_view&sysparm_article=KB0062950', diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx b/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx new file mode 100644 index 000000000000..3660141d98b8 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx @@ -0,0 +1,111 @@ +import { + OsdsBreadcrumb, + OsdsLink, + OsdsText, +} from '@ovhcloud/ods-components/react'; +import { + Headers, + Notifications, + StepComponent, + useMe, + useProjectUrl, +} from '@ovh-ux/manager-react-components'; +import { useHref, useParams } from 'react-router-dom'; +import { useProject } from '@ovh-ux/manager-pci-common'; +import { useTranslation } from 'react-i18next'; +import { ODS_TEXT_LEVEL, ODS_TEXT_SIZE } from '@ovhcloud/ods-components'; +import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming'; +import SizeInputComponent from '@/pages/create/SizeInput.component'; +import { PRODUCT_LINK } from '@/constants'; +import { StepsEnum, useNewLoadBalancerStore } from '@/pages/create/store'; + +export default function CreatePage(): JSX.Element { + const projectHref = useProjectUrl('public-cloud'); + const backHref = useHref('..'); + const { projectId } = useParams(); + const { t } = useTranslation('octavia-load-balancer'); + const { t: tCreate } = useTranslation('create'); + const { t: tCommon } = useTranslation('pci-common'); + const { data: project } = useProject(); + const { me } = useMe(); + const store = useNewLoadBalancerStore(); + + const productPageLink = + PRODUCT_LINK[me?.ovhSubsidiary] || PRODUCT_LINK.DEFAULT; + + return ( + <> + + +
+ +
+ + + + + {tCreate('octavia_load_balancer_create_description')} + + +
+ { + store.check(StepsEnum.SIZE); + store.lock(StepsEnum.SIZE); + + store.open(StepsEnum.REGION); + }, + label: tCommon('common_stepper_next_button_label'), + isDisabled: store.size === null, + }} + > + + {tCreate('octavia_load_balancer_create_size_intro')}{' '} + + {tCreate('octavia_load_balancer_create_size_intro_link')} + + + + + +
+ + ); +} diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/SizeInput.component.tsx b/packages/manager/apps/pci-load-balancer/src/pages/create/SizeInput.component.tsx index 9f642258fed3..84321c36b757 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/SizeInput.component.tsx +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/SizeInput.component.tsx @@ -7,16 +7,10 @@ import { OsdsText } from '@ovhcloud/ods-components/react'; import { useTranslation } from 'react-i18next'; import { ODS_TEXT_LEVEL, ODS_TEXT_SIZE } from '@ovhcloud/ods-components'; import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming'; +import { TPlan } from '@/pages/create/store'; const SIZE_FLAVOUR_REGEX = /octavia-loadbalancer.loadbalancer-([sml]).hour.consumption/; -export type TPlan = { - code: string; - price: number; - label: string; - technicalName: string; -}; - const LabelComponent = ({ item, isSelected, diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts new file mode 100644 index 000000000000..814fdda6f3dd --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts @@ -0,0 +1,146 @@ +import { create } from 'zustand'; + +export type TPlan = { + code: string; + price: number; + label: string; + technicalName: string; +}; + +type TStep = { + isOpen: boolean; + isLocked: boolean; + isChecked: boolean; +}; + +export enum StepsEnum { + SIZE = 'SIZE', + REGION = 'REGION', +} + +export type TFormStore = { + size: TPlan; + steps: Map; + set: { + size: (val: TPlan) => void; + }; + open: (step: StepsEnum) => void; + close: (step: StepsEnum) => void; + + lock: (step: StepsEnum) => void; + unlock: (step: StepsEnum) => void; + + check: (step: StepsEnum) => void; + uncheck: (step: StepsEnum) => void; + + edit: (step: StepsEnum) => void; + + reset: () => void; +}; + +const initialSteps = () => + new Map([ + [ + StepsEnum.SIZE, + { + isOpen: true, + isLocked: false, + isChecked: false, + }, + ], + [ + StepsEnum.REGION, + { + isOpen: false, + isLocked: false, + isChecked: false, + }, + ], + ]); + +export const useNewLoadBalancerStore = create()((set, get) => ({ + size: null, + steps: initialSteps(), + set: { + size: (val: TPlan) => { + set({ + size: val, + }); + }, + }, + open: (id: StepsEnum) => { + set((state) => { + const steps = new Map(state.steps); + steps.get(id).isOpen = true; + return { + ...state, + steps, + }; + }); + }, + close: (id: StepsEnum) => { + set((state) => { + const steps = new Map(state.steps); + steps.get(id).isOpen = false; + return { + ...state, + steps, + }; + }); + }, + lock: (id: StepsEnum) => { + set((state) => { + const steps = new Map(state.steps); + steps.get(id).isLocked = true; + return { + ...state, + steps, + }; + }); + }, + unlock: (id: StepsEnum) => { + set((state) => { + const steps = new Map(state.steps); + steps.get(id).isLocked = false; + return { + ...state, + steps, + }; + }); + }, + check: (id: StepsEnum) => { + set((state) => { + const steps = new Map(state.steps); + steps.get(id).isChecked = true; + return { + ...state, + steps, + }; + }); + }, + uncheck: (id: StepsEnum) => { + set((state) => { + const steps = new Map(state.steps); + steps.get(id).isChecked = false; + return { + ...state, + steps, + }; + }); + }, + edit: (id: StepsEnum) => { + switch (id) { + case StepsEnum.SIZE: + break; + + default: + } + }, + reset() { + set(() => ({ + ...get(), + size: null, + steps: initialSteps(), + })); + }, +})); diff --git a/packages/manager/apps/pci-load-balancer/src/pages/listing/Listing.page.tsx b/packages/manager/apps/pci-load-balancer/src/pages/listing/Listing.page.tsx index 2195b18b3218..a6c1b12e0eb0 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/listing/Listing.page.tsx +++ b/packages/manager/apps/pci-load-balancer/src/pages/listing/Listing.page.tsx @@ -88,7 +88,7 @@ export default function ListingPage() { className="xs:mb-0.5 sm:mb-0" onClick={() => { clearNotifications(); - navigate('./create'); + navigate('../create'); }} > + import('@/pages/create/Create.page'))} + /> + } /> } /> From 48095845f1041ff34d6edde74df26accc7471aae Mon Sep 17 00:00:00 2001 From: Mohammed Hamdoune Date: Wed, 9 Oct 2024 14:37:48 +0200 Subject: [PATCH 03/10] feat(pci-load-balancer): create page second step ref: DTCORE-2668 Signed-off-by: Mohammed Hamdoune --- .../regions-list/Messages_de_DE.json | 7 ++ .../regions-list/Messages_en_GB.json | 7 ++ .../regions-list/Messages_es_ES.json | 7 ++ .../regions-list/Messages_fr_CA.json | 7 ++ .../regions-list/Messages_fr_FR.json | 7 ++ .../regions-list/Messages_it_IT.json | 7 ++ .../regions-list/Messages_pl_PL.json | 7 ++ .../regions-list/Messages_pt_PT.json | 7 ++ .../translations/regions/Messages_de_DE.json | 70 +++++++++++++++ .../translations/regions/Messages_en_GB.json | 70 +++++++++++++++ .../translations/regions/Messages_es_ES.json | 70 +++++++++++++++ .../translations/regions/Messages_fr_CA.json | 70 +++++++++++++++ .../translations/regions/Messages_fr_FR.json | 70 +++++++++++++++ .../translations/regions/Messages_it_IT.json | 70 +++++++++++++++ .../translations/regions/Messages_pl_PL.json | 70 +++++++++++++++ .../translations/regions/Messages_pt_PT.json | 70 +++++++++++++++ .../pci-load-balancer/src/api/data/network.ts | 20 +++++ .../pci-load-balancer/src/api/data/plans.ts | 26 ++++++ .../src/api/hook/useNetwork.tsx | 8 ++ .../src/api/hook/usePlans.ts | 85 +++++++++++++++++++ .../apps/pci-load-balancer/src/constants.ts | 27 ++++++ .../src/pages/create/Create.page.tsx | 85 ++++++++++++++++++- .../src/pages/create/store.ts | 9 ++ 23 files changed, 873 insertions(+), 3 deletions(-) create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_de_DE.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_en_GB.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_es_ES.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_fr_CA.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_it_IT.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_pl_PL.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_pt_PT.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_de_DE.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_en_GB.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_es_ES.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_fr_CA.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_it_IT.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_pl_PL.json create mode 100644 packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_pt_PT.json create mode 100644 packages/manager/apps/pci-load-balancer/src/api/data/plans.ts create mode 100644 packages/manager/apps/pci-load-balancer/src/api/hook/usePlans.ts diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_de_DE.json b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_de_DE.json new file mode 100644 index 000000000000..36d6844179b8 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_de_DE.json @@ -0,0 +1,7 @@ +{ + "pci_project_regions_list_continent_all": "Alle Standorte", + "pci_project_regions_list_empty": "Kein Standort verfügbar", + "pci_project_regions_list_add": "Bitte aktivieren Sie die letzten Standorte jeder Zone im Bereich Standorte.", + "pci_project_regions_list_region": "Standort", + "pci_project_regions_list_quota": "Mein Quota erhöhen" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_en_GB.json b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_en_GB.json new file mode 100644 index 000000000000..cdbd9524a4b3 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_en_GB.json @@ -0,0 +1,7 @@ +{ + "pci_project_regions_list_continent_all": "All locations", + "pci_project_regions_list_empty": "No locations are available", + "pci_project_regions_list_add": "Please activate the latest locations in each zone in the Locations section", + "pci_project_regions_list_region": "Location", + "pci_project_regions_list_quota": "Increase your quota" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_es_ES.json b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_es_ES.json new file mode 100644 index 000000000000..148495c3a517 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_es_ES.json @@ -0,0 +1,7 @@ +{ + "pci_project_regions_list_continent_all": "Todas las localizaciones", + "pci_project_regions_list_empty": "No hay localizaciones disponibles.", + "pci_project_regions_list_add": "Por favor, active las últimas localizaciones de cada zona en la sección Localizaciones.", + "pci_project_regions_list_region": "Localización", + "pci_project_regions_list_quota": "Aumentar los límites de mi cuota" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_fr_CA.json b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_fr_CA.json new file mode 100644 index 000000000000..917f9a4f479f --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_fr_CA.json @@ -0,0 +1,7 @@ +{ + "pci_project_regions_list_continent_all": "Toutes les localisations", + "pci_project_regions_list_empty": "Aucune localisation n'est disponible", + "pci_project_regions_list_add": "Nous vous invitons à activer les dernières localisations de chaque zone dans la partie Localisations", + "pci_project_regions_list_region": "Localisation", + "pci_project_regions_list_quota": "Augmenter mon quota" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_fr_FR.json b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_fr_FR.json new file mode 100644 index 000000000000..917f9a4f479f --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_fr_FR.json @@ -0,0 +1,7 @@ +{ + "pci_project_regions_list_continent_all": "Toutes les localisations", + "pci_project_regions_list_empty": "Aucune localisation n'est disponible", + "pci_project_regions_list_add": "Nous vous invitons à activer les dernières localisations de chaque zone dans la partie Localisations", + "pci_project_regions_list_region": "Localisation", + "pci_project_regions_list_quota": "Augmenter mon quota" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_it_IT.json b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_it_IT.json new file mode 100644 index 000000000000..07888b4a7268 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_it_IT.json @@ -0,0 +1,7 @@ +{ + "pci_project_regions_list_continent_all": "Tutte le Region", + "pci_project_regions_list_empty": "Nessuna Region disponibile", + "pci_project_regions_list_add": "Per attivare le ultime localizzazioni disponibili in ogni zona, accedi alla sezione Localizzazioni", + "pci_project_regions_list_region": "Localizzazione", + "pci_project_regions_list_quota": "Aumenta la tua quota" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_pl_PL.json b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_pl_PL.json new file mode 100644 index 000000000000..c97352642e0d --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_pl_PL.json @@ -0,0 +1,7 @@ +{ + "pci_project_regions_list_continent_all": "Wszystkie lokalizacje", + "pci_project_regions_list_empty": "Żadna z lokalizacji nie jest dostępna", + "pci_project_regions_list_add": "Włącz najnowsze lokalizacje każdej strefy w sekcji Lokalizacje", + "pci_project_regions_list_region": "Lokalizacja", + "pci_project_regions_list_quota": "Zwiększ limit" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_pt_PT.json b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_pt_PT.json new file mode 100644 index 000000000000..ede04f6e2975 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions-list/Messages_pt_PT.json @@ -0,0 +1,7 @@ +{ + "pci_project_regions_list_continent_all": "Todas as localizações", + "pci_project_regions_list_empty": "Nenhuma localização disponível", + "pci_project_regions_list_add": "Pode ativar as últimas localizações de cada zona na secção Localizações", + "pci_project_regions_list_region": "Localização", + "pci_project_regions_list_quota": "Aumentar o meu limite" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_de_DE.json b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_de_DE.json new file mode 100644 index 000000000000..8989050d7f25 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_de_DE.json @@ -0,0 +1,70 @@ +{ + "manager_components_region_SBG1": "Straßburg (SBG1)", + "manager_components_region_BHS1": "Beauharnois (BHS1)", + "manager_components_region_GRA1": "Gravelines (GRA1)", + "manager_components_region_SBG": "Straßburg", + "manager_components_region_SBG_micro": "Straßburg ({{ micro }})", + "manager_components_region_BHS": "Beauharnois", + "manager_components_region_BHS_micro": "Beauharnois ({{ micro }})", + "manager_components_region_ERI": "London", + "manager_components_region_ERI_micro": "London ({{ micro }})", + "manager_components_region_GRA": "Gravelines", + "manager_components_region_GRA_micro": "Gravelines ({{ micro }})", + "manager_components_region_LIM": "Limburg", + "manager_components_region_LIM_micro": "Limburg ({{ micro }})", + "manager_components_region_RBX": "Roubaix", + "manager_components_region_RBX_micro": "Roubaix ({{ micro }})", + "manager_components_region_WAW": "Warschau", + "manager_components_region_WAW_micro": "Warschau ({{ micro }})", + "manager_components_region_DE": "Frankfurt", + "manager_components_region_DE_micro": "Frankfurt ({{ micro }})", + "manager_components_region_UK": "London", + "manager_components_region_UK_micro": "London ({{ micro }})", + "manager_components_region_SGP": "Singapur", + "manager_components_region_SGP_micro": "Singapur ({{ micro }})", + "manager_components_region_SYD": "Sydney", + "manager_components_region_SYD_micro": "Sydney ({{ micro }})", + "manager_components_region_US": "USA", + "manager_components_region_US_micro": "Vereinigte Staaten ({{ micro }})", + "manager_components_region_localize": "Lokalisieren", + "manager_components_region_location_SBG": "Mitteleuropa (Frankreich)", + "manager_components_region_location_WAW": "Mitteleuropa (Polen)", + "manager_components_region_location_BHS": "Nordamerika (Kanada)", + "manager_components_region_location_ERI": "Westeuropa (Vereinigtes Königreich)", + "manager_components_region_location_GRA": "Westeuropa (Frankreich)", + "manager_components_region_location_LIM": "Mitteleuropa (Deutschland)", + "manager_components_region_location_RBX": "Westeuropa (Frankreich)", + "manager_components_region_location_DE": "Mitteleuropa (Deutschland)", + "manager_components_region_location_UK": "Westeuropa (Vereinigtes Königreich)", + "manager_components_region_location_SGP": "Asiatisch-pazifischer Raum (Singapur)", + "manager_components_region_location_SYD": "Ozeanien (Australien)", + "manager_components_region_location_US": "USA", + "manager_components_region_continent_SBG": "Mitteleuropa", + "manager_components_region_continent_WAW": "Mitteleuropa", + "manager_components_region_continent_BHS": "Nordamerika", + "manager_components_region_continent_GRA": "Westeuropa", + "manager_components_region_continent_RBX": "Westeuropa", + "manager_components_region_continent_DE": "Mitteleuropa", + "manager_components_region_continent_UK": "Westeuropa", + "manager_components_region_continent_SGP": "Asiatisch-pazifischer Raum ", + "manager_components_region_continent_SYD": "Ozeanien", + "manager_components_region_continent_US": "USA", + "manager_components_region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "manager_components_region_continent_SHA": "Westeuropa", + "manager_components_region_GS": "GS", + "manager_components_region_MAD": "Madrid", + "manager_components_region_BRU": "Brüssel", + "manager_components_region_GS_micro": "Gridscale ({{ micro }})", + "manager_components_region_MAD_micro": "Madrid ({{ micro }})", + "manager_components_region_BRU_micro": "Brüssel ({{ micro }})", + "manager_components_region_location_GS": "Westeuropa", + "manager_components_region_location_MAD": "Westeuropa", + "manager_components_region_location_BRU": "Westeuropa", + "manager_components_region_continent_GS": "Westeuropa", + "manager_components_region_continent_MAD": "Westeuropa", + "manager_components_region_continent_BRU": "Westeuropa", + "manager_components_region_MUM": "Mumbai", + "manager_components_region_MUM_micro": "Mumbai ({{ micro }})", + "manager_components_region_location_MUM": "Asiatisch-pazifischer Raum (Mumbai)", + "manager_components_region_continent_MUM": "Asiatisch-pazifischer Raum " +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_en_GB.json b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_en_GB.json new file mode 100644 index 000000000000..5af89d2ea1a4 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_en_GB.json @@ -0,0 +1,70 @@ +{ + "manager_components_region_SBG1": "Strasbourg (SBG1)", + "manager_components_region_BHS1": "Beauharnois (BHS1)", + "manager_components_region_GRA1": "Gravelines (GRA1)", + "manager_components_region_SBG": "Strasbourg", + "manager_components_region_SBG_micro": "Strasbourg ({{ micro }})", + "manager_components_region_BHS": "Beauharnois", + "manager_components_region_BHS_micro": "Beauharnois ({{ micro }})", + "manager_components_region_ERI": "London", + "manager_components_region_ERI_micro": "London ({{ micro }})", + "manager_components_region_GRA": "Gravelines", + "manager_components_region_GRA_micro": "Gravelines ({{ micro }})", + "manager_components_region_LIM": "Limburg", + "manager_components_region_LIM_micro": "Limburg ({{ micro }})", + "manager_components_region_RBX": "Roubaix", + "manager_components_region_RBX_micro": "Roubaix ({{ micro }})", + "manager_components_region_WAW": "Warsaw", + "manager_components_region_WAW_micro": "Warsaw ({{ micro }})", + "manager_components_region_DE": "Frankfurt", + "manager_components_region_DE_micro": "Frankfurt ({{ micro }})", + "manager_components_region_UK": "London", + "manager_components_region_UK_micro": "London ({{ micro }})", + "manager_components_region_SGP": "Singapore", + "manager_components_region_SGP_micro": "Singapore ({{ micro }})", + "manager_components_region_SYD": "Sydney", + "manager_components_region_SYD_micro": "Sydney ({{ micro }})", + "manager_components_region_US": "United States of America", + "manager_components_region_US_micro": "United States ({{ micro }})", + "manager_components_region_localize": "Locate", + "manager_components_region_location_SBG": "Central Europe (France)", + "manager_components_region_location_WAW": "Central Europe (Poland)", + "manager_components_region_location_BHS": "North America (Canada)", + "manager_components_region_location_ERI": "Western Europe (United Kingdom)", + "manager_components_region_location_GRA": "Western Europe (France)", + "manager_components_region_location_LIM": "Central Europe (Germany)", + "manager_components_region_location_RBX": "Western Europe (France)", + "manager_components_region_location_DE": "Central Europe (Germany)", + "manager_components_region_location_UK": "Western Europe (United Kingdom)", + "manager_components_region_location_SGP": "Asia Pacific (Singapore)", + "manager_components_region_location_SYD": "Oceania (Australia)", + "manager_components_region_location_US": "United States of America", + "manager_components_region_continent_SBG": "Central Europe", + "manager_components_region_continent_WAW": "Central Europe", + "manager_components_region_continent_BHS": "North America", + "manager_components_region_continent_GRA": "Western Europe", + "manager_components_region_continent_RBX": "Western Europe", + "manager_components_region_continent_DE": "Central Europe", + "manager_components_region_continent_UK": "Western Europe", + "manager_components_region_continent_SGP": "Asia Pacific ", + "manager_components_region_continent_SYD": "Oceania", + "manager_components_region_continent_US": "United States of America", + "manager_components_region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "manager_components_region_continent_SHA": "Western Europe", + "manager_components_region_GS": "GS", + "manager_components_region_MAD": "Madrid", + "manager_components_region_BRU": "Brussels", + "manager_components_region_GS_micro": "Gridscale ({{ micro }})", + "manager_components_region_MAD_micro": "Madrid ({{ micro }})", + "manager_components_region_BRU_micro": "Brussels ({{ micro }})", + "manager_components_region_location_GS": "Western Europe", + "manager_components_region_location_MAD": "Western Europe", + "manager_components_region_location_BRU": "Western Europe", + "manager_components_region_continent_GS": "Western Europe", + "manager_components_region_continent_MAD": "Western Europe", + "manager_components_region_continent_BRU": "Western Europe", + "manager_components_region_MUM": "Mumbai", + "manager_components_region_MUM_micro": "Mumbai ({{ micro }})", + "manager_components_region_location_MUM": "Asia Pacific (Mumbai)", + "manager_components_region_continent_MUM": "Asia Pacific " +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_es_ES.json b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_es_ES.json new file mode 100644 index 000000000000..264be5039efc --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_es_ES.json @@ -0,0 +1,70 @@ +{ + "manager_components_region_SBG1": "Estrasburgo (SBG1)", + "manager_components_region_BHS1": "Beauharnois (BHS1)", + "manager_components_region_GRA1": "Gravelines (GRA1)", + "manager_components_region_SBG": "Estrasburgo", + "manager_components_region_SBG_micro": "Estrasburgo ({{ micro }})", + "manager_components_region_BHS": "Beauharnois", + "manager_components_region_BHS_micro": "Beauharnois ({{ micro }})", + "manager_components_region_ERI": "Londres", + "manager_components_region_ERI_micro": "Londres ({{ micro }})", + "manager_components_region_GRA": "Gravelines", + "manager_components_region_GRA_micro": "Gravelines ({{ micro }})", + "manager_components_region_LIM": "Limburgo", + "manager_components_region_LIM_micro": "Limburgo ({{ micro }})", + "manager_components_region_RBX": "Roubaix", + "manager_components_region_RBX_micro": "Roubaix ({{ micro }})", + "manager_components_region_WAW": "Varsovia", + "manager_components_region_WAW_micro": "Varsovia ({{ micro }})", + "manager_components_region_DE": "Fráncfort", + "manager_components_region_DE_micro": "Fráncfort ({{ micro }})", + "manager_components_region_UK": "Londres", + "manager_components_region_UK_micro": "Londres ({{ micro }})", + "manager_components_region_SGP": "Singapur", + "manager_components_region_SGP_micro": "Singapur ({{ micro }})", + "manager_components_region_SYD": "Sídney", + "manager_components_region_SYD_micro": "Sídney ({{ micro }})", + "manager_components_region_US": "Estados Unidos", + "manager_components_region_US_micro": "Estados Unidos ({{ micro }})", + "manager_components_region_localize": "Localizar", + "manager_components_region_location_SBG": "Europa Central (Francia)", + "manager_components_region_location_WAW": "Europa Central (Polonia)", + "manager_components_region_location_BHS": "Norteamérica (Canadá)", + "manager_components_region_location_ERI": "Europa Occidental (Reino Unido)", + "manager_components_region_location_GRA": "Europa Occidental (Francia)", + "manager_components_region_location_LIM": "Europa Central (Alemania)", + "manager_components_region_location_RBX": "Europa Occidental (Francia)", + "manager_components_region_location_DE": "Europa Central (Alemania)", + "manager_components_region_location_UK": "Europa Occidental (Reino Unido)", + "manager_components_region_location_SGP": "Asia-Pacífico (Singapur)", + "manager_components_region_location_SYD": "Oceanía (Australia)", + "manager_components_region_location_US": "Estados Unidos", + "manager_components_region_continent_SBG": "Europa Central", + "manager_components_region_continent_WAW": "Europa Central", + "manager_components_region_continent_BHS": "Norteamérica", + "manager_components_region_continent_GRA": "Europa Occidental", + "manager_components_region_continent_RBX": "Europa Occidental", + "manager_components_region_continent_DE": "Europa Central", + "manager_components_region_continent_UK": "Europa Occidental", + "manager_components_region_continent_SGP": "Asia-Pacífico ", + "manager_components_region_continent_SYD": "Oceanía", + "manager_components_region_continent_US": "Estados Unidos", + "manager_components_region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "manager_components_region_continent_SHA": "Europa Occidental", + "manager_components_region_GS": "GS", + "manager_components_region_MAD": "Madrid", + "manager_components_region_BRU": "Bruselas", + "manager_components_region_GS_micro": "Gridscale ({{ micro }})", + "manager_components_region_MAD_micro": "Madrid ({{ micro }})", + "manager_components_region_BRU_micro": "Bruselas ({{ micro }})", + "manager_components_region_location_GS": "Europa Occidental", + "manager_components_region_location_MAD": "Europa Occidental", + "manager_components_region_location_BRU": "Europa Occidental", + "manager_components_region_continent_GS": "Europa Occidental", + "manager_components_region_continent_MAD": "Europa Occidental", + "manager_components_region_continent_BRU": "Europa Occidental", + "manager_components_region_MUM": "Mumbai", + "manager_components_region_MUM_micro": "Mumbai ({{ micro }})", + "manager_components_region_location_MUM": "Asia-Pacífico (Mumbai)", + "manager_components_region_continent_MUM": "Asia-Pacífico " +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_fr_CA.json b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_fr_CA.json new file mode 100644 index 000000000000..4cf07f789929 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_fr_CA.json @@ -0,0 +1,70 @@ +{ + "manager_components_region_SBG1": "Strasbourg (SBG1)", + "manager_components_region_BHS1": "Beauharnois (BHS1)", + "manager_components_region_GRA1": "Gravelines (GRA1)", + "manager_components_region_SBG": "Strasbourg", + "manager_components_region_SBG_micro": "Strasbourg ({{ micro }})", + "manager_components_region_BHS": "Beauharnois", + "manager_components_region_BHS_micro": "Beauharnois ({{ micro }})", + "manager_components_region_ERI": "Londres", + "manager_components_region_ERI_micro": "Londres ({{ micro }})", + "manager_components_region_GRA": "Gravelines", + "manager_components_region_GRA_micro": "Gravelines ({{ micro }})", + "manager_components_region_LIM": "Limburg", + "manager_components_region_LIM_micro": "Limburg ({{ micro }})", + "manager_components_region_RBX": "Roubaix", + "manager_components_region_RBX_micro": "Roubaix ({{ micro }})", + "manager_components_region_WAW": "Varsovie", + "manager_components_region_WAW_micro": "Varsovie ({{ micro }})", + "manager_components_region_DE": "Francfort", + "manager_components_region_DE_micro": "Francfort ({{ micro }})", + "manager_components_region_UK": "Londres", + "manager_components_region_UK_micro": "Londres ({{ micro }})", + "manager_components_region_SGP": "Singapour", + "manager_components_region_SGP_micro": "Singapour ({{ micro }})", + "manager_components_region_MUM": "Mumbai", + "manager_components_region_MUM_micro": "Mumbai ({{ micro }})", + "manager_components_region_SYD": "Sydney", + "manager_components_region_SYD_micro": "Sydney ({{ micro }})", + "manager_components_region_US": "États-Unis", + "manager_components_region_US_micro": "États-Unis ({{ micro }})", + "manager_components_region_GS": "GS", + "manager_components_region_MAD": "Madrid", + "manager_components_region_BRU": "Bruxelles", + "manager_components_region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "manager_components_region_GS_micro": "Gridscale ({{ micro }})", + "manager_components_region_MAD_micro": "Madrid ({{ micro }})", + "manager_components_region_BRU_micro": "Bruxelles ({{ micro }})", + "manager_components_region_localize": "Localiser", + "manager_components_region_location_SBG": "Europe centrale (France)", + "manager_components_region_location_WAW": "Europe centrale (Pologne)", + "manager_components_region_location_BHS": "Amérique du Nord (Canada)", + "manager_components_region_location_ERI": "Europe de l'Ouest (Grande-Bretagne)", + "manager_components_region_location_GRA": "Europe de l'Ouest (France)", + "manager_components_region_location_GS": "Western Europe", + "manager_components_region_location_MAD": "Western Europe", + "manager_components_region_location_BRU": "Western Europe", + "manager_components_region_location_LIM": "Europe centrale (Allemagne)", + "manager_components_region_location_RBX": "Europe de l'Ouest (France)", + "manager_components_region_location_DE": "Europe centrale (Allemagne)", + "manager_components_region_location_UK": "Europe de l'Ouest (Grande-Bretagne)", + "manager_components_region_location_SGP": "Asie Pacifique (Singapour)", + "manager_components_region_location_MUM": "Asie Pacifique (Mumbai)", + "manager_components_region_location_SYD": "Océanie (Australie)", + "manager_components_region_location_US": "États-Unis", + "manager_components_region_continent_SBG": "Europe centrale", + "manager_components_region_continent_WAW": "Europe centrale", + "manager_components_region_continent_BHS": "Amérique du Nord", + "manager_components_region_continent_GRA": "Europe de l'Ouest", + "manager_components_region_continent_RBX": "Europe de l'Ouest", + "manager_components_region_continent_GS": "Western Europe", + "manager_components_region_continent_MAD": "Western Europe", + "manager_components_region_continent_BRU": "Western Europe", + "manager_components_region_continent_DE": "Europe centrale", + "manager_components_region_continent_UK": "Europe de l'Ouest", + "manager_components_region_continent_SGP": "Asie Pacifique", + "manager_components_region_continent_MUM": "Asie Pacifique", + "manager_components_region_continent_SYD": "Océanie", + "manager_components_region_continent_US": "États-Unis", + "manager_components_region_continent_SHA": "Europe de l'Ouest" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_fr_FR.json b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_fr_FR.json new file mode 100644 index 000000000000..4cf07f789929 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_fr_FR.json @@ -0,0 +1,70 @@ +{ + "manager_components_region_SBG1": "Strasbourg (SBG1)", + "manager_components_region_BHS1": "Beauharnois (BHS1)", + "manager_components_region_GRA1": "Gravelines (GRA1)", + "manager_components_region_SBG": "Strasbourg", + "manager_components_region_SBG_micro": "Strasbourg ({{ micro }})", + "manager_components_region_BHS": "Beauharnois", + "manager_components_region_BHS_micro": "Beauharnois ({{ micro }})", + "manager_components_region_ERI": "Londres", + "manager_components_region_ERI_micro": "Londres ({{ micro }})", + "manager_components_region_GRA": "Gravelines", + "manager_components_region_GRA_micro": "Gravelines ({{ micro }})", + "manager_components_region_LIM": "Limburg", + "manager_components_region_LIM_micro": "Limburg ({{ micro }})", + "manager_components_region_RBX": "Roubaix", + "manager_components_region_RBX_micro": "Roubaix ({{ micro }})", + "manager_components_region_WAW": "Varsovie", + "manager_components_region_WAW_micro": "Varsovie ({{ micro }})", + "manager_components_region_DE": "Francfort", + "manager_components_region_DE_micro": "Francfort ({{ micro }})", + "manager_components_region_UK": "Londres", + "manager_components_region_UK_micro": "Londres ({{ micro }})", + "manager_components_region_SGP": "Singapour", + "manager_components_region_SGP_micro": "Singapour ({{ micro }})", + "manager_components_region_MUM": "Mumbai", + "manager_components_region_MUM_micro": "Mumbai ({{ micro }})", + "manager_components_region_SYD": "Sydney", + "manager_components_region_SYD_micro": "Sydney ({{ micro }})", + "manager_components_region_US": "États-Unis", + "manager_components_region_US_micro": "États-Unis ({{ micro }})", + "manager_components_region_GS": "GS", + "manager_components_region_MAD": "Madrid", + "manager_components_region_BRU": "Bruxelles", + "manager_components_region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "manager_components_region_GS_micro": "Gridscale ({{ micro }})", + "manager_components_region_MAD_micro": "Madrid ({{ micro }})", + "manager_components_region_BRU_micro": "Bruxelles ({{ micro }})", + "manager_components_region_localize": "Localiser", + "manager_components_region_location_SBG": "Europe centrale (France)", + "manager_components_region_location_WAW": "Europe centrale (Pologne)", + "manager_components_region_location_BHS": "Amérique du Nord (Canada)", + "manager_components_region_location_ERI": "Europe de l'Ouest (Grande-Bretagne)", + "manager_components_region_location_GRA": "Europe de l'Ouest (France)", + "manager_components_region_location_GS": "Western Europe", + "manager_components_region_location_MAD": "Western Europe", + "manager_components_region_location_BRU": "Western Europe", + "manager_components_region_location_LIM": "Europe centrale (Allemagne)", + "manager_components_region_location_RBX": "Europe de l'Ouest (France)", + "manager_components_region_location_DE": "Europe centrale (Allemagne)", + "manager_components_region_location_UK": "Europe de l'Ouest (Grande-Bretagne)", + "manager_components_region_location_SGP": "Asie Pacifique (Singapour)", + "manager_components_region_location_MUM": "Asie Pacifique (Mumbai)", + "manager_components_region_location_SYD": "Océanie (Australie)", + "manager_components_region_location_US": "États-Unis", + "manager_components_region_continent_SBG": "Europe centrale", + "manager_components_region_continent_WAW": "Europe centrale", + "manager_components_region_continent_BHS": "Amérique du Nord", + "manager_components_region_continent_GRA": "Europe de l'Ouest", + "manager_components_region_continent_RBX": "Europe de l'Ouest", + "manager_components_region_continent_GS": "Western Europe", + "manager_components_region_continent_MAD": "Western Europe", + "manager_components_region_continent_BRU": "Western Europe", + "manager_components_region_continent_DE": "Europe centrale", + "manager_components_region_continent_UK": "Europe de l'Ouest", + "manager_components_region_continent_SGP": "Asie Pacifique", + "manager_components_region_continent_MUM": "Asie Pacifique", + "manager_components_region_continent_SYD": "Océanie", + "manager_components_region_continent_US": "États-Unis", + "manager_components_region_continent_SHA": "Europe de l'Ouest" +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_it_IT.json b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_it_IT.json new file mode 100644 index 000000000000..b7f69f8e6435 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_it_IT.json @@ -0,0 +1,70 @@ +{ + "manager_components_region_SBG1": "Strasburgo (SBG1)", + "manager_components_region_BHS1": "Beauharnois (BHS1)", + "manager_components_region_GRA1": "Gravelines (GRA1)", + "manager_components_region_SBG": "Strasburgo", + "manager_components_region_SBG_micro": "Strasburgo ({{ micro }})", + "manager_components_region_BHS": "Beauharnois", + "manager_components_region_BHS_micro": "Beauharnois ({{ micro }})", + "manager_components_region_ERI": "Londra", + "manager_components_region_ERI_micro": "Londra ({{ micro }})", + "manager_components_region_GRA": "Gravelines", + "manager_components_region_GRA_micro": "Gravelines ({{ micro }})", + "manager_components_region_LIM": "Limburgo", + "manager_components_region_LIM_micro": "Limburgo ({{ micro }})", + "manager_components_region_RBX": "Roubaix", + "manager_components_region_RBX_micro": "Roubaix ({{ micro }})", + "manager_components_region_WAW": "Varsavia", + "manager_components_region_WAW_micro": "Varsavia ({{ micro }})", + "manager_components_region_DE": "Francoforte", + "manager_components_region_DE_micro": "Francoforte ({{ micro }})", + "manager_components_region_UK": "Londra", + "manager_components_region_UK_micro": "Londra ({{ micro }})", + "manager_components_region_SGP": "Singapore", + "manager_components_region_SGP_micro": "Singapore ({{ micro }})", + "manager_components_region_SYD": "Sydney", + "manager_components_region_SYD_micro": "Sydney ({{ micro }})", + "manager_components_region_US": "Stati Uniti", + "manager_components_region_US_micro": "Stati Uniti ({{ micro }})", + "manager_components_region_localize": "Localizza", + "manager_components_region_location_SBG": "Europa centrale (Francia)", + "manager_components_region_location_WAW": "Europa centrale (Polonia)", + "manager_components_region_location_BHS": "Nord America (Canada)", + "manager_components_region_location_ERI": "Europa Occidentale (Gran Bretagna)", + "manager_components_region_location_GRA": "Europa Occidentale (Francia)", + "manager_components_region_location_LIM": "Europa centrale (Germania)", + "manager_components_region_location_RBX": "Europa Occidentale (Francia)", + "manager_components_region_location_DE": "Europa centrale (Germania)", + "manager_components_region_location_UK": "Europa Occidentale (Gran Bretagna)", + "manager_components_region_location_SGP": "Asia Pacifica (Singapore)", + "manager_components_region_location_SYD": "Oceania (Australia)", + "manager_components_region_location_US": "Stati Uniti", + "manager_components_region_continent_SBG": "Europa centrale", + "manager_components_region_continent_WAW": "Europa centrale", + "manager_components_region_continent_BHS": "Nord America ", + "manager_components_region_continent_GRA": "Europa Occidentale", + "manager_components_region_continent_RBX": "Europa Occidentale", + "manager_components_region_continent_DE": "Europa centrale", + "manager_components_region_continent_UK": "Europa Occidentale", + "manager_components_region_continent_SGP": "Asia Pacifica ", + "manager_components_region_continent_SYD": "Oceania", + "manager_components_region_continent_US": "Stati Uniti", + "manager_components_region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "manager_components_region_continent_SHA": "Europa Occidentale", + "manager_components_region_GS": "GS", + "manager_components_region_MAD": "Madrid", + "manager_components_region_BRU": "Bruxelles", + "manager_components_region_GS_micro": "Gridscale ({{ micro }})", + "manager_components_region_MAD_micro": "Madrid ({{ micro }})", + "manager_components_region_BRU_micro": "Bruxelles ({{ micro }})", + "manager_components_region_location_GS": "Europa Occidentale", + "manager_components_region_location_MAD": "Europa Occidentale", + "manager_components_region_location_BRU": "Europa Occidentale", + "manager_components_region_continent_GS": "Europa Occidentale", + "manager_components_region_continent_MAD": "Europa Occidentale", + "manager_components_region_continent_BRU": "Europa Occidentale", + "manager_components_region_MUM": "Mumbai", + "manager_components_region_MUM_micro": "Mumbai ({{ micro }})", + "manager_components_region_location_MUM": "Asia Pacifica (Mumbai)", + "manager_components_region_continent_MUM": "Asia Pacifica " +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_pl_PL.json b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_pl_PL.json new file mode 100644 index 000000000000..19362917c32d --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_pl_PL.json @@ -0,0 +1,70 @@ +{ + "manager_components_region_SBG1": "Strasburg (SBG1)", + "manager_components_region_BHS1": "Beauharnois (BHS1)", + "manager_components_region_GRA1": "Gravelines (GRA1)", + "manager_components_region_SBG": "Strasburg", + "manager_components_region_SBG_micro": "Strasburg ({{ micro }})", + "manager_components_region_BHS": "Beauharnois", + "manager_components_region_BHS_micro": "Beauharnois ({{ micro }})", + "manager_components_region_ERI": "Londyn", + "manager_components_region_ERI_micro": "Londyn ({{ micro }})", + "manager_components_region_GRA": "Gravelines", + "manager_components_region_GRA_micro": "Gravelines ({{ micro }})", + "manager_components_region_LIM": "Limburg", + "manager_components_region_LIM_micro": "Limburg ({{ micro }})", + "manager_components_region_RBX": "Roubaix", + "manager_components_region_RBX_micro": "Roubaix ({{ micro }})", + "manager_components_region_WAW": "Warszawa", + "manager_components_region_WAW_micro": "Warszawa ({{ micro }})", + "manager_components_region_DE": "Frankfurt", + "manager_components_region_DE_micro": "Frankfurt ({{ micro }})", + "manager_components_region_UK": "Londyn", + "manager_components_region_UK_micro": "Londyn ({{ micro }})", + "manager_components_region_SGP": "Singapur", + "manager_components_region_SGP_micro": "Singapur ({{ micro }})", + "manager_components_region_SYD": "Sydney", + "manager_components_region_SYD_micro": "Sydney ({{ micro }})", + "manager_components_region_US": "Stany Zjednoczone", + "manager_components_region_US_micro": "Stany Zjednoczone ({{ micro }})", + "manager_components_region_localize": "Lokalizacja", + "manager_components_region_location_SBG": "Europa Środkowa (Francja)", + "manager_components_region_location_WAW": "Europa Środkowa (Polska)", + "manager_components_region_location_BHS": "Ameryka Północna (Kanada)", + "manager_components_region_location_ERI": "Europa Zachodnia (Wielka Brytania)", + "manager_components_region_location_GRA": "Europa Zachodnia (Francja)", + "manager_components_region_location_LIM": "Europa Środkowa (Niemcy)", + "manager_components_region_location_RBX": "Europa Zachodnia (Francja)", + "manager_components_region_location_DE": "Europa Środkowa (Niemcy)", + "manager_components_region_location_UK": "Europa Zachodnia (Wielka Brytania)", + "manager_components_region_location_SGP": "Azja-Pacyfik (Singapur)", + "manager_components_region_location_SYD": "Oceania (Australia)", + "manager_components_region_location_US": "Stany Zjednoczone", + "manager_components_region_continent_SBG": "Europa Środkowa", + "manager_components_region_continent_WAW": "Europa Środkowa", + "manager_components_region_continent_BHS": "Ameryka Północna", + "manager_components_region_continent_GRA": "Europa Zachodnia", + "manager_components_region_continent_RBX": "Europa Zachodnia", + "manager_components_region_continent_DE": "Europa Środkowa", + "manager_components_region_continent_UK": "Europa Zachodnia", + "manager_components_region_continent_SGP": "Azja-Pacyfik ", + "manager_components_region_continent_SYD": "Oceania", + "manager_components_region_continent_US": "Stany Zjednoczone", + "manager_components_region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "manager_components_region_continent_SHA": "Europa Zachodnia", + "manager_components_region_GS": "GS", + "manager_components_region_MAD": "Madryt", + "manager_components_region_BRU": "Bruksela", + "manager_components_region_GS_micro": "Gridscale ({{micro}})", + "manager_components_region_MAD_micro": "Madryt ({{micro}})", + "manager_components_region_BRU_micro": "Bruksela ({{micro}})", + "manager_components_region_location_GS": "Europa Zachodnia", + "manager_components_region_location_MAD": "Europa Zachodnia", + "manager_components_region_location_BRU": "Europa Zachodnia", + "manager_components_region_continent_GS": "Europa Zachodnia", + "manager_components_region_continent_MAD": "Europa Zachodnia", + "manager_components_region_continent_BRU": "Europa Zachodnia", + "manager_components_region_MUM": "Mumbaj", + "manager_components_region_MUM_micro": "Mumbaj ({{micro}})", + "manager_components_region_location_MUM": "Azja-Pacyfik (Mumbaj)", + "manager_components_region_continent_MUM": "Azja-Pacyfik " +} diff --git a/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_pt_PT.json b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_pt_PT.json new file mode 100644 index 000000000000..780dc85a1662 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/public/translations/regions/Messages_pt_PT.json @@ -0,0 +1,70 @@ +{ + "manager_components_region_SBG1": "Estrasburgo (SBG1)", + "manager_components_region_BHS1": "Beauharnois (BHS1)", + "manager_components_region_GRA1": "Gravelines (GRA1)", + "manager_components_region_SBG": "Estrasburgo", + "manager_components_region_SBG_micro": "Estrasburgo ({{ micro }})", + "manager_components_region_BHS": "Beauharnois", + "manager_components_region_BHS_micro": "Beauharnois ({{ micro }})", + "manager_components_region_ERI": "Londres", + "manager_components_region_ERI_micro": "Londres ({{ micro }})", + "manager_components_region_GRA": "Gravelines", + "manager_components_region_GRA_micro": "Gravelines ({{ micro }})", + "manager_components_region_LIM": "Limburgo", + "manager_components_region_LIM_micro": "Limburgo ({{ micro }})", + "manager_components_region_RBX": "Roubaix", + "manager_components_region_RBX_micro": "Roubaix ({{ micro }})", + "manager_components_region_WAW": "Varsóvia", + "manager_components_region_WAW_micro": "Varsóvia ({{ micro }})", + "manager_components_region_DE": "Frankfurt ", + "manager_components_region_DE_micro": "Frankfurt ({{ micro }})", + "manager_components_region_UK": "Londres", + "manager_components_region_UK_micro": "Londres ({{ micro }})", + "manager_components_region_SGP": "Singapura", + "manager_components_region_SGP_micro": "Singapura ({{ micro }})", + "manager_components_region_SYD": "Sydney", + "manager_components_region_SYD_micro": "Sydney ({{ micro }})", + "manager_components_region_US": "Estados Unidos", + "manager_components_region_US_micro": "Estados Unidos ({{ micro }})", + "manager_components_region_localize": "Localizar", + "manager_components_region_location_SBG": "Europa Central (França)", + "manager_components_region_location_WAW": "Europa Central (Polónia)", + "manager_components_region_location_BHS": "América do Norte (Canadá)", + "manager_components_region_location_ERI": "Europa Ocidental (Reino Unido)", + "manager_components_region_location_GRA": "Europa Ocidental (França)", + "manager_components_region_location_LIM": "Europa Central (Alemanha)", + "manager_components_region_location_RBX": "Europa Ocidental (França)", + "manager_components_region_location_DE": "Europa Central (Alemanha)", + "manager_components_region_location_UK": "Europa Ocidental (Reino Unido)", + "manager_components_region_location_SGP": "Ásia-Pacífico (Singapura)", + "manager_components_region_location_SYD": "Oceânia (Austrália)", + "manager_components_region_location_US": "Estados Unidos", + "manager_components_region_continent_SBG": "Europa Central", + "manager_components_region_continent_WAW": "Europa Central", + "manager_components_region_continent_BHS": "América do Norte", + "manager_components_region_continent_GRA": "Europa Ocidental", + "manager_components_region_continent_RBX": "Europa Ocidental", + "manager_components_region_continent_DE": "Europa Central", + "manager_components_region_continent_UK": "Europa Ocidental", + "manager_components_region_continent_SGP": "Ásia-Pacífico ", + "manager_components_region_continent_SYD": "Oceânia", + "manager_components_region_continent_US": "Estados Unidos", + "manager_components_region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "manager_components_region_continent_SHA": "Europa Ocidental", + "manager_components_region_GS": "GS", + "manager_components_region_MAD": "Madrid", + "manager_components_region_BRU": "Bruxelas", + "manager_components_region_GS_micro": "Gridscale ({{ micro }})", + "manager_components_region_MAD_micro": "Madrid ({{ micro }})", + "manager_components_region_BRU_micro": "Bruxelas ({{ micro }})", + "manager_components_region_location_GS": "Western Europe", + "manager_components_region_location_MAD": "Western Europe", + "manager_components_region_location_BRU": "Western Europe", + "manager_components_region_continent_GS": "Western Europe", + "manager_components_region_continent_MAD": "Western Europe", + "manager_components_region_continent_BRU": "Western Europe", + "manager_components_region_MUM": "Mumbai", + "manager_components_region_MUM_micro": "Mumbai ({{ micro }})", + "manager_components_region_location_MUM": "Ásia-Pacífico (Mumbai)", + "manager_components_region_continent_MUM": "Ásia-Pacífico " +} diff --git a/packages/manager/apps/pci-load-balancer/src/api/data/network.ts b/packages/manager/apps/pci-load-balancer/src/api/data/network.ts index 13ec57fa5dda..cb7b12de1541 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/data/network.ts +++ b/packages/manager/apps/pci-load-balancer/src/api/data/network.ts @@ -37,3 +37,23 @@ export const getSubnetByNetworkAndRegion = async ( return data; }; + +export type TPrivateNetwork = { + id: string; + name: string; + status: string; + type: string; + vlanId: number; + regions: { + openstackId: string; + region: string; + status: string; + }[]; +}; + +export const getPrivateNetworks = async (projectId: string) => { + const { data } = await v6.get( + `/cloud/project/${projectId}/network/private`, + ); + return data; +}; diff --git a/packages/manager/apps/pci-load-balancer/src/api/data/plans.ts b/packages/manager/apps/pci-load-balancer/src/api/data/plans.ts new file mode 100644 index 000000000000..e8831fa8da76 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/api/data/plans.ts @@ -0,0 +1,26 @@ +import { v6 } from '@ovh-ux/manager-core-api'; +import { AGORA_ADDON_FAMILY } from '@/constants'; + +export type TPlanResponse = { + plans: { + code: string; + regions: { + continentCode: string; + datacenter: string; + enabled: boolean; + name: string; + type: string; + }[]; + }[]; +}; + +export const getPlans = async ( + projectId: string, + ovhSubsidiary: string, +): Promise => { + const { data } = await v6.get( + `/cloud/project/${projectId}/capabilities/productAvailability?addonFamily=${AGORA_ADDON_FAMILY}&ovhSubsidiary=${ovhSubsidiary}`, + ); + + return data; +}; diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/useNetwork.tsx b/packages/manager/apps/pci-load-balancer/src/api/hook/useNetwork.tsx index f7be72d2403b..50d4ac1ab4b2 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/hook/useNetwork.tsx +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/useNetwork.tsx @@ -1,6 +1,7 @@ import { useQuery } from '@tanstack/react-query'; import { getPrivateNetworkByRegion, + getPrivateNetworks, getSubnetByNetworkAndRegion, } from '../data/network'; @@ -47,3 +48,10 @@ export const useSubnetByNetworkAndRegion = ({ enabled: !!networkId && !!subnetId, throwOnError: true, }); + +export const useGetPrivateNetworks = (projectId: string) => + useQuery({ + queryKey: ['project', projectId, 'private-networks'], + queryFn: () => getPrivateNetworks(projectId), + throwOnError: true, + }); diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/usePlans.ts b/packages/manager/apps/pci-load-balancer/src/api/hook/usePlans.ts new file mode 100644 index 000000000000..548e9fb8caeb --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/usePlans.ts @@ -0,0 +1,85 @@ +import { useQuery } from '@tanstack/react-query'; +import { useMe } from '@ovh-ux/manager-react-components'; +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { getPlans } from '../data/plans'; +import { SIZE_FLAVOUR_REGEX } from '@/constants'; +import { useGetPrivateNetworks } from '@/api/hook/useNetwork'; + +export const useGetPlans = (projectId: string) => { + const { me } = useMe(); + return useQuery({ + queryKey: ['project', projectId, 'plans'], + queryFn: () => getPlans(projectId, me?.ovhSubsidiary), + enabled: !!me, + throwOnError: true, + }); +}; + +export type TRegion = { + name: string; + isEnabled: boolean; + continentCode: string; + macroName: string; + microName: string; + continent: string; +}; +const getMacroRegion = (region: string) => { + const regionSubStrings = region.split('-'); + + const macroRegionMap = [ + null, + regionSubStrings[0].split(/(\d)/)[0], + regionSubStrings[0], + regionSubStrings[2], + regionSubStrings[2] === 'LZ' ? regionSubStrings[3] : regionSubStrings[2], + regionSubStrings[3], + ]; + return macroRegionMap[regionSubStrings.length] || 'Unknown_Macro_Region'; +}; +export const useGetRegions = (projectId: string): Map => { + const { data: response } = useGetPlans(projectId); + const { data: networks } = useGetPrivateNetworks(projectId); + const { t: tRegion } = useTranslation('regions'); + + return useMemo(() => { + const regions = new Map(); + if (response && networks) { + response?.plans.forEach((plan) => { + const match = plan.code.match(SIZE_FLAVOUR_REGEX); + if (match) { + // plan is included + const code = match[1]; + if (!regions.has(code)) { + regions.set(code, []); + } + plan.regions.forEach((region) => { + regions.get(code).push({ + name: region.name, + isEnabled: networks.some((network) => + network.regions.some((r) => r.region === region.name), + ), + continentCode: region.continentCode, + macroName: tRegion( + `manager_components_region_${getMacroRegion(region.name)}`, + ), + microName: tRegion( + `manager_components_region_${getMacroRegion( + region.name, + )}_micro`, + { micro: region.name }, + ), + continent: tRegion( + `manager_components_region_continent_${getMacroRegion( + region.name, + )}`, + ), + }); + }); + } + }); + } + console.log(regions); + return regions; + }, [response, networks]); +}; diff --git a/packages/manager/apps/pci-load-balancer/src/constants.ts b/packages/manager/apps/pci-load-balancer/src/constants.ts index c9e75b9e7230..b579216ad9ad 100644 --- a/packages/manager/apps/pci-load-balancer/src/constants.ts +++ b/packages/manager/apps/pci-load-balancer/src/constants.ts @@ -54,6 +54,9 @@ export const ACTION_LABELS = { [ACTIONS.REJECT]: 'Reject', }; +export const AGORA_ADDON_FAMILY = 'octavia-loadbalancer'; +export const SIZE_FLAVOUR_REGEX = /octavia-loadbalancer.loadbalancer-([sml]).hour.consumption/; + export const PRODUCT_LINK = { FR: 'https://www.ovhcloud.com/fr/public-cloud/load-balancer/', DE: 'https://www.ovhcloud.com/de/public-cloud/load-balancer/', @@ -78,6 +81,30 @@ export const PRODUCT_LINK = { DEFAULT: 'https://www.ovhcloud.com/en/public-cloud/load-balancer/', }; +export const REGION_AVAILABILITY_LINK = { + FR: 'https://www.ovhcloud.com/fr/public-cloud/regions-availability', + DE: 'https://www.ovhcloud.com/de/public-cloud/regions-availability/', + ES: 'https://www.ovhcloud.com/es-es/public-cloud/regions-availability/', + WE: 'https://www.ovhcloud.com/en-ie/public-cloud/regions-availability/', + IE: 'https://www.ovhcloud.com/en-ie/public-cloud/regions-availability/', + IT: 'https://www.ovhcloud.com/it/public-cloud/regions-availability/', + NL: 'https://www.ovhcloud.com/nl/public-cloud/regions-availability/', + PL: 'https://www.ovhcloud.com/pl/public-cloud/regions-availability/', + PT: 'https://www.ovhcloud.com/pt/public-cloud/regions-availability/', + GB: 'https://www.ovhcloud.com/en-gb/public-cloud/regions-availability/', + CA: 'https://www.ovhcloud.com/en-ca/public-cloud/regions-availability/', + QC: 'https://www.ovhcloud.com/fr-ca/public-cloud/regions-availability/', + WS: 'https://www.ovhcloud.com/es/public-cloud/regions-availability/', + MA: 'https://www.ovhcloud.com/fr-ma/public-cloud/regions-availability/', + SN: 'https://www.ovhcloud.com/fr-sn/public-cloud/regions-availability/', + TN: 'https://www.ovhcloud.com/fr-tn/public-cloud/regions-availability/', + AU: 'https://www.ovhcloud.com/en-au/public-cloud/regions-availability/', + SG: 'https://www.ovhcloud.com/en-sg/public-cloud/regions-availability/', + ASIA: 'https://www.ovhcloud.com/asia/public-cloud/regions-availability/', + US: 'https://us.ovhcloud.com/public-cloud/regions-availability/', + DEFAULT: 'https://www.ovhcloud.com/en/public-cloud/regions-availability/', +}; + export const LOAD_BALANCER_LOGS_SERVICE_GUIDE_LINK = { DEFAULT: 'https://help.ovhcloud.com/csm/en-public-cloud-network-loadbalancer-logs-forward?id=kb_article_view&sysparm_article=KB0062950', diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx b/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx index 3660141d98b8..d0c23c6d5543 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx @@ -7,6 +7,7 @@ import { Headers, Notifications, StepComponent, + TilesInputComponent, useMe, useProjectUrl, } from '@ovh-ux/manager-react-components'; @@ -14,10 +15,20 @@ import { useHref, useParams } from 'react-router-dom'; import { useProject } from '@ovh-ux/manager-pci-common'; import { useTranslation } from 'react-i18next'; import { ODS_TEXT_LEVEL, ODS_TEXT_SIZE } from '@ovhcloud/ods-components'; -import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming'; +import { + ODS_THEME_COLOR_INTENT, + ODS_THEME_TYPOGRAPHY_SIZE, +} from '@ovhcloud/ods-common-theming'; +import { clsx } from 'clsx'; +import { useState } from 'react'; import SizeInputComponent from '@/pages/create/SizeInput.component'; -import { PRODUCT_LINK } from '@/constants'; +import { PRODUCT_LINK, REGION_AVAILABILITY_LINK } from '@/constants'; import { StepsEnum, useNewLoadBalancerStore } from '@/pages/create/store'; +import { TRegion, useGetRegions } from '@/api/hook/usePlans'; + +type TState = { + selectedContinent: string | undefined; +}; export default function CreatePage(): JSX.Element { const projectHref = useProjectUrl('public-cloud'); @@ -26,12 +37,22 @@ export default function CreatePage(): JSX.Element { const { t } = useTranslation('octavia-load-balancer'); const { t: tCreate } = useTranslation('create'); const { t: tCommon } = useTranslation('pci-common'); + const { t: tRegionsList } = useTranslation('regions-list'); const { data: project } = useProject(); const { me } = useMe(); const store = useNewLoadBalancerStore(); + const [state, setState] = useState({ + selectedContinent: undefined, + }); + const productPageLink = PRODUCT_LINK[me?.ovhSubsidiary] || PRODUCT_LINK.DEFAULT; + const regionPageLink = + REGION_AVAILABILITY_LINK[me?.ovhSubsidiary] || + REGION_AVAILABILITY_LINK.DEFAULT; + + const regions = useGetRegions(projectId); return ( <> @@ -104,7 +125,65 @@ export default function CreatePage(): JSX.Element { isChecked={store.steps.get(StepsEnum.REGION).isChecked} isLocked={store.steps.get(StepsEnum.REGION).isLocked} order={2} - > + > + + {tCreate('octavia_load_balancer_create_region_intro')}{' '} + + {tCreate('octavia_load_balancer_create_region_link')} + + + + + items={(regions?.get(store.size?.code) || []).filter( + (region) => region.isEnabled, + )} + value={store.region} + onInput={(region) => store.set.region(region)} + label={(region) => region.name} + group={{ + by: (region) => region.continent, + label: (continent) => ( + +
+ {undefined === continent + ? tRegionsList('pci_project_regions_list_continent_all') + : continent} +
+
+ ), + showAllTab: true, + }} + stack={{ + by: (region) => region?.macroName, + label: (macroName) => macroName, + title: () => tRegionsList('pci_project_regions_list_region'), + onChange: (continent) => { + setState({ ...state, selectedContinent: continent }); + }, + }} + /> + ); diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts index 814fdda6f3dd..911c2733274d 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts @@ -1,4 +1,5 @@ import { create } from 'zustand'; +import { TRegion } from '@/api/hook/usePlans'; export type TPlan = { code: string; @@ -20,9 +21,11 @@ export enum StepsEnum { export type TFormStore = { size: TPlan; + region: TRegion; steps: Map; set: { size: (val: TPlan) => void; + region: (val: TRegion) => void; }; open: (step: StepsEnum) => void; close: (step: StepsEnum) => void; @@ -60,6 +63,7 @@ const initialSteps = () => export const useNewLoadBalancerStore = create()((set, get) => ({ size: null, + region: null, steps: initialSteps(), set: { size: (val: TPlan) => { @@ -67,6 +71,11 @@ export const useNewLoadBalancerStore = create()((set, get) => ({ size: val, }); }, + region: (val: TRegion) => { + set({ + region: val, + }); + }, }, open: (id: StepsEnum) => { set((state) => { From 8523470d2aa10e9677c5ca51c7a4d67848a72024 Mon Sep 17 00:00:00 2001 From: Mohammed Hamdoune Date: Wed, 9 Oct 2024 16:21:30 +0200 Subject: [PATCH 04/10] feat(pci-load-balancer): create page third step ref: DTCORE-2668 Signed-off-by: Mohammed Hamdoune --- .../pci-load-balancer/src/api/data/region.ts | 17 ++ .../src/api/hook/useRegion.ts | 10 + .../apps/pci-load-balancer/src/constants.ts | 1 + .../src/pages/create/Create.page.tsx | 179 +++++++++++++++++- .../src/pages/create/store.ts | 18 ++ 5 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 packages/manager/apps/pci-load-balancer/src/api/data/region.ts create mode 100644 packages/manager/apps/pci-load-balancer/src/api/hook/useRegion.ts diff --git a/packages/manager/apps/pci-load-balancer/src/api/data/region.ts b/packages/manager/apps/pci-load-balancer/src/api/data/region.ts new file mode 100644 index 000000000000..b1658d01b205 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/api/data/region.ts @@ -0,0 +1,17 @@ +import { v6 } from '@ovh-ux/manager-core-api'; + +export type TFloatingIp = { + associatedEntity: string; + id: string; + ip: string; + networkId: string; + status: string; +}; + +export const getFloatingIps = async (projectId: string, region: string) => { + const { data } = await v6.get( + `/cloud/project/${projectId}/region/${region}/floatingip`, + ); + + return data; +}; diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/useRegion.ts b/packages/manager/apps/pci-load-balancer/src/api/hook/useRegion.ts new file mode 100644 index 000000000000..e9065109a875 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/useRegion.ts @@ -0,0 +1,10 @@ +import { useQuery } from '@tanstack/react-query'; +import { getFloatingIps } from '../data/region'; + +export const useGetFloatingIps = (projectId: string, region: string) => + useQuery({ + queryKey: ['project', projectId, 'region', region, 'floating-ips'], + queryFn: () => getFloatingIps(projectId, region), + enabled: !!projectId && !!region, + throwOnError: true, + }); diff --git a/packages/manager/apps/pci-load-balancer/src/constants.ts b/packages/manager/apps/pci-load-balancer/src/constants.ts index b579216ad9ad..8c3734e5e298 100644 --- a/packages/manager/apps/pci-load-balancer/src/constants.ts +++ b/packages/manager/apps/pci-load-balancer/src/constants.ts @@ -56,6 +56,7 @@ export const ACTION_LABELS = { export const AGORA_ADDON_FAMILY = 'octavia-loadbalancer'; export const SIZE_FLAVOUR_REGEX = /octavia-loadbalancer.loadbalancer-([sml]).hour.consumption/; +export const AGORA_FLOATING_IP_REGEX = /floatingip.floatingip.hour.consumption/; export const PRODUCT_LINK = { FR: 'https://www.ovhcloud.com/fr/public-cloud/load-balancer/', diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx b/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx index d0c23c6d5543..a382965815b3 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx @@ -1,6 +1,10 @@ import { OsdsBreadcrumb, + OsdsFormField, OsdsLink, + OsdsMessage, + OsdsSelect, + OsdsSelectOption, OsdsText, } from '@ovhcloud/ods-components/react'; import { @@ -8,13 +12,18 @@ import { Notifications, StepComponent, TilesInputComponent, + useCatalogPrice, useMe, useProjectUrl, } from '@ovh-ux/manager-react-components'; import { useHref, useParams } from 'react-router-dom'; -import { useProject } from '@ovh-ux/manager-pci-common'; +import { useCatalog, useProject } from '@ovh-ux/manager-pci-common'; import { useTranslation } from 'react-i18next'; -import { ODS_TEXT_LEVEL, ODS_TEXT_SIZE } from '@ovhcloud/ods-components'; +import { + ODS_MESSAGE_TYPE, + ODS_TEXT_LEVEL, + ODS_TEXT_SIZE, +} from '@ovhcloud/ods-components'; import { ODS_THEME_COLOR_INTENT, ODS_THEME_TYPOGRAPHY_SIZE, @@ -22,9 +31,14 @@ import { import { clsx } from 'clsx'; import { useState } from 'react'; import SizeInputComponent from '@/pages/create/SizeInput.component'; -import { PRODUCT_LINK, REGION_AVAILABILITY_LINK } from '@/constants'; +import { + AGORA_FLOATING_IP_REGEX, + PRODUCT_LINK, + REGION_AVAILABILITY_LINK, +} from '@/constants'; import { StepsEnum, useNewLoadBalancerStore } from '@/pages/create/store'; import { TRegion, useGetRegions } from '@/api/hook/usePlans'; +import { useGetFloatingIps } from '@/api/hook/useRegion'; type TState = { selectedContinent: string | undefined; @@ -41,10 +55,16 @@ export default function CreatePage(): JSX.Element { const { data: project } = useProject(); const { me } = useMe(); const store = useNewLoadBalancerStore(); + const { data: catalog } = useCatalog(); + const { getFormattedHourlyCatalogPrice } = useCatalogPrice(5); const [state, setState] = useState({ selectedContinent: undefined, }); + const { data: floatingIps } = useGetFloatingIps( + projectId, + store.region?.name, + ); const productPageLink = PRODUCT_LINK[me?.ovhSubsidiary] || PRODUCT_LINK.DEFAULT; @@ -54,6 +74,28 @@ export default function CreatePage(): JSX.Element { const regions = useGetRegions(projectId); + const floatingIpsList = [ + { + associatedEntity: '', + id: 'create', + ip: tCreate( + 'octavia_load_balancer_create_floating_ip_field_new_floating_ip', + ), + networkId: '', + status: '', + }, + { + associatedEntity: '', + id: 'none', + ip: tCreate( + 'octavia_load_balancer_create_floating_ip_field_no_floating_ip', + ), + networkId: '', + status: '', + }, + ...(floatingIps || []), + ]; + return ( <> { + store.check(StepsEnum.REGION); + store.lock(StepsEnum.REGION); + + store.open(StepsEnum.PUBLIC_IP); + }, + label: tCommon('common_stepper_next_button_label'), + isDisabled: store.region === null, + }} > - items={(regions?.get(store.size?.code) || []).filter( (region) => region.isEnabled, @@ -184,6 +235,126 @@ export default function CreatePage(): JSX.Element { }} /> + { + store.check(StepsEnum.PUBLIC_IP); + store.lock(StepsEnum.PUBLIC_IP); + + store.open(StepsEnum.PUBLIC_IP); + }, + label: tCommon('common_stepper_next_button_label'), + isDisabled: store.publicIp === null, + }} + > + + {tCreate('octavia_load_balancer_create_floating_ip_intro')} + + + + {tCreate('octavia_load_balancer_create_floating_ip_field')} + + + { + const targetIp = floatingIpsList.find( + (ip) => ip.id === event.target.value, + ); + store.set.publicIp(targetIp); + }} + inline + > + + {tCreate('octavia_load_balancer_create_floating_ip_field')} + + {floatingIpsList.map((ip) => ( + + {ip.ip} + + ))} + + + + {store.publicIp?.id === 'create' && ( + +
+
+ + {tCreate( + 'octavia_load_balancer_create_floating_ip_new_information', + )} + +
+
+ + {tCreate( + 'octavia_load_balancer_create_floating_ip_new_price', + { + price: getFormattedHourlyCatalogPrice( + catalog?.addons.filter((addon) => + addon.planCode.match(AGORA_FLOATING_IP_REGEX), + )[0].pricings[0].price, + ), + }, + )} + + {tCreate( + 'octavia_load_balancer_create_floating_ip_new_price_interval', + )} + +
+
+
+ )} + {store.publicIp?.id === 'none' && ( + + + {tCreate( + 'octavia_load_balancer_create_floating_ip_no_floating_ip_information', + )} + + + )} +
); diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts index 911c2733274d..e2b9277fd6a5 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts @@ -1,5 +1,6 @@ import { create } from 'zustand'; import { TRegion } from '@/api/hook/usePlans'; +import { TFloatingIp } from '@/api/data/region'; export type TPlan = { code: string; @@ -17,15 +18,18 @@ type TStep = { export enum StepsEnum { SIZE = 'SIZE', REGION = 'REGION', + PUBLIC_IP = 'PUBLIC_IP', } export type TFormStore = { size: TPlan; region: TRegion; + publicIp: TFloatingIp; steps: Map; set: { size: (val: TPlan) => void; region: (val: TRegion) => void; + publicIp: (val: TFloatingIp) => void; }; open: (step: StepsEnum) => void; close: (step: StepsEnum) => void; @@ -59,11 +63,20 @@ const initialSteps = () => isChecked: false, }, ], + [ + StepsEnum.PUBLIC_IP, + { + isOpen: false, + isLocked: false, + isChecked: false, + }, + ], ]); export const useNewLoadBalancerStore = create()((set, get) => ({ size: null, region: null, + publicIp: null, steps: initialSteps(), set: { size: (val: TPlan) => { @@ -76,6 +89,11 @@ export const useNewLoadBalancerStore = create()((set, get) => ({ region: val, }); }, + publicIp: (val: TFloatingIp) => { + set({ + publicIp: val, + }); + }, }, open: (id: StepsEnum) => { set((state) => { From 243dcc441398df3a508a3b3dd0d165cff59e025e Mon Sep 17 00:00:00 2001 From: Mohammed Hamdoune Date: Thu, 10 Oct 2024 17:17:58 +0200 Subject: [PATCH 05/10] feat(pci-load-balancer): create page fourth step ref: DTCORE-2668 Signed-off-by: Mohammed Hamdoune --- .../pci-load-balancer/src/api/data/network.ts | 33 ++- .../pci-load-balancer/src/api/data/region.ts | 13 ++ .../src/api/hook/useNetwork.tsx | 23 +- .../src/api/hook/usePlans.ts | 1 - .../src/api/hook/useRegion.ts | 10 +- .../apps/pci-load-balancer/src/constants.ts | 6 + .../src/pages/create/Create.page.tsx | 210 +++++++++++++++--- .../src/pages/create/store.ts | 26 +++ 8 files changed, 280 insertions(+), 42 deletions(-) diff --git a/packages/manager/apps/pci-load-balancer/src/api/data/network.ts b/packages/manager/apps/pci-load-balancer/src/api/data/network.ts index cb7b12de1541..2a98255161c0 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/data/network.ts +++ b/packages/manager/apps/pci-load-balancer/src/api/data/network.ts @@ -19,12 +19,6 @@ export const getPrivateNetworkByRegion = async ( return data; }; -export type TSubnet = { - cidr: string; - gatewayIp: string; - id: string; -}; - export const getSubnetByNetworkAndRegion = async ( projectId: string, region: string, @@ -44,6 +38,7 @@ export type TPrivateNetwork = { status: string; type: string; vlanId: number; + visibility: string; regions: { openstackId: string; region: string; @@ -51,9 +46,35 @@ export type TPrivateNetwork = { }[]; }; +export type TSubnet = { + id: string; + name: string; + cidr: string; + ipVersion: number; + dhcpEnabled: boolean; + gatewayIp: string; + allocationPools: { + start: string; + end: string; + }[]; + hostRoutes: string[]; + dnsNameServers: string[]; +}; + export const getPrivateNetworks = async (projectId: string) => { const { data } = await v6.get( `/cloud/project/${projectId}/network/private`, ); return data; }; + +export const getPrivateNetworkSubnets = async ( + projectId: string, + region: string, + networkId: string, +): Promise => { + const { data } = await v6.get( + `/cloud/project/${projectId}/region/${region}/network/${networkId}/subnet`, + ); + return data; +}; diff --git a/packages/manager/apps/pci-load-balancer/src/api/data/region.ts b/packages/manager/apps/pci-load-balancer/src/api/data/region.ts index b1658d01b205..245cffc2fb8a 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/data/region.ts +++ b/packages/manager/apps/pci-load-balancer/src/api/data/region.ts @@ -1,4 +1,5 @@ import { v6 } from '@ovh-ux/manager-core-api'; +import { TPrivateNetwork } from '@/api/data/network'; export type TFloatingIp = { associatedEntity: string; @@ -6,6 +7,7 @@ export type TFloatingIp = { ip: string; networkId: string; status: string; + type: string; }; export const getFloatingIps = async (projectId: string, region: string) => { @@ -15,3 +17,14 @@ export const getFloatingIps = async (projectId: string, region: string) => { return data; }; + +export const getRegionPrivateNetworks = async ( + projectId: string, + region: string, +) => { + const { data } = await v6.get( + `/cloud/project/${projectId}/region/${region}/network`, + ); + + return data; +}; diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/useNetwork.tsx b/packages/manager/apps/pci-load-balancer/src/api/hook/useNetwork.tsx index 50d4ac1ab4b2..6396215d935f 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/hook/useNetwork.tsx +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/useNetwork.tsx @@ -2,6 +2,7 @@ import { useQuery } from '@tanstack/react-query'; import { getPrivateNetworkByRegion, getPrivateNetworks, + getPrivateNetworkSubnets, getSubnetByNetworkAndRegion, } from '../data/network'; @@ -17,7 +18,7 @@ export const usePrivateNetworkByRegion = ({ useQuery({ queryKey: ['project', projectId, 'region', region, 'network', networkId], queryFn: () => getPrivateNetworkByRegion(projectId, region, networkId), - enabled: !!networkId, + enabled: !!region && !!networkId, throwOnError: true, }); @@ -55,3 +56,23 @@ export const useGetPrivateNetworks = (projectId: string) => queryFn: () => getPrivateNetworks(projectId), throwOnError: true, }); + +export const useGetPrivateNetworkSubnets = ( + projectId: string, + region: string, + networkId: string, +) => + useQuery({ + queryKey: [ + 'project', + projectId, + 'region', + region, + 'network', + networkId, + 'subnets', + ], + queryFn: () => getPrivateNetworkSubnets(projectId, region, networkId), + enabled: !!projectId && !!region && !!networkId, + throwOnError: true, + }); diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/usePlans.ts b/packages/manager/apps/pci-load-balancer/src/api/hook/usePlans.ts index 548e9fb8caeb..63edc36fddf5 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/hook/usePlans.ts +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/usePlans.ts @@ -79,7 +79,6 @@ export const useGetRegions = (projectId: string): Map => { } }); } - console.log(regions); return regions; }, [response, networks]); }; diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/useRegion.ts b/packages/manager/apps/pci-load-balancer/src/api/hook/useRegion.ts index e9065109a875..990612d93512 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/hook/useRegion.ts +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/useRegion.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query'; -import { getFloatingIps } from '../data/region'; +import { getFloatingIps, getRegionPrivateNetworks } from '../data/region'; export const useGetFloatingIps = (projectId: string, region: string) => useQuery({ @@ -8,3 +8,11 @@ export const useGetFloatingIps = (projectId: string, region: string) => enabled: !!projectId && !!region, throwOnError: true, }); + +export const useGetRegionNetworks = (projectId: string, region: string) => + useQuery({ + queryKey: ['project', projectId, 'region', region, 'networks'], + queryFn: () => getRegionPrivateNetworks(projectId, region), + enabled: !!projectId && !!region, + throwOnError: true, + }); diff --git a/packages/manager/apps/pci-load-balancer/src/constants.ts b/packages/manager/apps/pci-load-balancer/src/constants.ts index 8c3734e5e298..077371f3e9b3 100644 --- a/packages/manager/apps/pci-load-balancer/src/constants.ts +++ b/packages/manager/apps/pci-load-balancer/src/constants.ts @@ -57,6 +57,12 @@ export const ACTION_LABELS = { export const AGORA_ADDON_FAMILY = 'octavia-loadbalancer'; export const SIZE_FLAVOUR_REGEX = /octavia-loadbalancer.loadbalancer-([sml]).hour.consumption/; export const AGORA_FLOATING_IP_REGEX = /floatingip.floatingip.hour.consumption/; +export const NETWORK_PRIVATE_VISIBILITY = 'private'; +export const FLOATING_IP_TYPE = { + NO_IP: 'none', + CREATE: 'create', + IP: 'ip', +}; export const PRODUCT_LINK = { FR: 'https://www.ovhcloud.com/fr/public-cloud/load-balancer/', diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx b/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx index a382965815b3..c89e239e15b6 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx @@ -29,16 +29,22 @@ import { ODS_THEME_TYPOGRAPHY_SIZE, } from '@ovhcloud/ods-common-theming'; import { clsx } from 'clsx'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import SizeInputComponent from '@/pages/create/SizeInput.component'; import { AGORA_FLOATING_IP_REGEX, + FLOATING_IP_TYPE, + NETWORK_PRIVATE_VISIBILITY, PRODUCT_LINK, REGION_AVAILABILITY_LINK, } from '@/constants'; import { StepsEnum, useNewLoadBalancerStore } from '@/pages/create/store'; import { TRegion, useGetRegions } from '@/api/hook/usePlans'; -import { useGetFloatingIps } from '@/api/hook/useRegion'; +import { useGetFloatingIps, useGetRegionNetworks } from '@/api/hook/useRegion'; +import { + useGetPrivateNetworks, + useGetPrivateNetworkSubnets, +} from '@/api/hook/useNetwork'; type TState = { selectedContinent: string | undefined; @@ -46,54 +52,88 @@ type TState = { export default function CreatePage(): JSX.Element { const projectHref = useProjectUrl('public-cloud'); + const backHref = useHref('..'); + const { projectId } = useParams(); + const { t } = useTranslation('octavia-load-balancer'); const { t: tCreate } = useTranslation('create'); const { t: tCommon } = useTranslation('pci-common'); const { t: tRegionsList } = useTranslation('regions-list'); - const { data: project } = useProject(); - const { me } = useMe(); - const store = useNewLoadBalancerStore(); - const { data: catalog } = useCatalog(); + const { getFormattedHourlyCatalogPrice } = useCatalogPrice(5); + const store = useNewLoadBalancerStore(); + const [state, setState] = useState({ selectedContinent: undefined, }); + + const { me } = useMe(); + + const { data: project } = useProject(); + const { data: catalog } = useCatalog(); + const { data: privateNetworks } = useGetRegionNetworks( + projectId, + store.region?.name, + ); const { data: floatingIps } = useGetFloatingIps( projectId, store.region?.name, ); - - const productPageLink = - PRODUCT_LINK[me?.ovhSubsidiary] || PRODUCT_LINK.DEFAULT; - const regionPageLink = - REGION_AVAILABILITY_LINK[me?.ovhSubsidiary] || - REGION_AVAILABILITY_LINK.DEFAULT; + const { data: subnets } = useGetPrivateNetworkSubnets( + projectId, + store.region?.name, + store.privateNetwork?.id, + ); const regions = useGetRegions(projectId); - const floatingIpsList = [ - { - associatedEntity: '', - id: 'create', - ip: tCreate( - 'octavia_load_balancer_create_floating_ip_field_new_floating_ip', - ), - networkId: '', - status: '', - }, - { - associatedEntity: '', - id: 'none', - ip: tCreate( - 'octavia_load_balancer_create_floating_ip_field_no_floating_ip', - ), - networkId: '', - status: '', - }, - ...(floatingIps || []), + const [floatingIpsList, privateNetworksList] = [ + [ + { + associatedEntity: '', + id: 'create', + ip: tCreate( + 'octavia_load_balancer_create_floating_ip_field_new_floating_ip', + ), + networkId: '', + status: '', + type: '', + }, + { + associatedEntity: '', + id: 'none', + ip: tCreate( + 'octavia_load_balancer_create_floating_ip_field_no_floating_ip', + ), + networkId: '', + status: '', + type: '', + }, + ...(floatingIps || []), + ], + (privateNetworks || []).filter( + (network) => network.visibility === NETWORK_PRIVATE_VISIBILITY, + ), + ]; + + const subnetsList = useMemo(() => { + console.log('hna', subnets); + if (!subnets) { + return []; + } + return store.publicIp?.type !== FLOATING_IP_TYPE.NO_IP + ? subnets.filter((subnet) => subnet.gatewayIp) + : subnets; + }, [subnets, store.publicIp]); + + const [productPageLink, regionPageLink] = [ + PRODUCT_LINK[me?.ovhSubsidiary] || PRODUCT_LINK.DEFAULT, + + REGION_AVAILABILITY_LINK[me?.ovhSubsidiary] || + REGION_AVAILABILITY_LINK.DEFAULT, ]; return ( @@ -246,7 +286,7 @@ export default function CreatePage(): JSX.Element { store.check(StepsEnum.PUBLIC_IP); store.lock(StepsEnum.PUBLIC_IP); - store.open(StepsEnum.PUBLIC_IP); + store.open(StepsEnum.PRIVATE_NETWORK); }, label: tCommon('common_stepper_next_button_label'), isDisabled: store.publicIp === null, @@ -355,6 +395,110 @@ export default function CreatePage(): JSX.Element { )} + { + store.check(StepsEnum.PRIVATE_NETWORK); + store.lock(StepsEnum.PRIVATE_NETWORK); + + store.open(StepsEnum.PRIVATE_NETWORK); + }, + label: tCommon('common_stepper_next_button_label'), + isDisabled: + subnetsList.length === 0 && + store.publicIp?.type !== FLOATING_IP_TYPE.NO_IP, + }} + > + + {tCreate('octavia_load_balancer_create_private_network_intro')} + + + + {tCreate('octavia_load_balancer_create_private_network_field')} + + + { + const targetNetwork = (privateNetworks || []).find( + (ip) => ip.id === event.target.value, + ); + store.set.privateNetwork(targetNetwork); + }} + inline + > + + {tCreate('octavia_load_balancer_create_private_network_field')} + + {privateNetworksList.map((network) => ( + + {network.name} + + ))} + + + + + {tCreate( + 'octavia_load_balancer_create_private_network_field_subnet', + )} + + + { + const targetSubnet = subnetsList.find( + (sub) => sub.id === event.target.value, + ); + store.set.subnet(targetSubnet); + }} + inline + > + + {tCreate( + 'octavia_load_balancer_create_private_network_field_subnet', + )} + + {subnetsList.map((subnet) => ( + + {subnet.cidr} + + ))} + + + ); diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts index e2b9277fd6a5..7bff81aaa33f 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts @@ -1,6 +1,7 @@ import { create } from 'zustand'; import { TRegion } from '@/api/hook/usePlans'; import { TFloatingIp } from '@/api/data/region'; +import { TPrivateNetwork, TSubnet } from '@/api/data/network'; export type TPlan = { code: string; @@ -19,17 +20,22 @@ export enum StepsEnum { SIZE = 'SIZE', REGION = 'REGION', PUBLIC_IP = 'PUBLIC_IP', + PRIVATE_NETWORK = 'PRIVATE_NETWORK', } export type TFormStore = { size: TPlan; region: TRegion; publicIp: TFloatingIp; + privateNetwork: TPrivateNetwork; + subnet: TSubnet; steps: Map; set: { size: (val: TPlan) => void; region: (val: TRegion) => void; publicIp: (val: TFloatingIp) => void; + privateNetwork: (val: TPrivateNetwork) => void; + subnet: (val: TSubnet) => void; }; open: (step: StepsEnum) => void; close: (step: StepsEnum) => void; @@ -71,12 +77,22 @@ const initialSteps = () => isChecked: false, }, ], + [ + StepsEnum.PRIVATE_NETWORK, + { + isOpen: false, + isLocked: false, + isChecked: false, + }, + ], ]); export const useNewLoadBalancerStore = create()((set, get) => ({ size: null, region: null, publicIp: null, + privateNetwork: null, + subnet: null, steps: initialSteps(), set: { size: (val: TPlan) => { @@ -94,6 +110,16 @@ export const useNewLoadBalancerStore = create()((set, get) => ({ publicIp: val, }); }, + privateNetwork: (val: TPrivateNetwork) => { + set({ + privateNetwork: val, + }); + }, + subnet: (val: TSubnet) => { + set({ + subnet: val, + }); + }, }, open: (id: StepsEnum) => { set((state) => { From 3f34f02037acc3be09859c24accec1e65b202f8b Mon Sep 17 00:00:00 2001 From: Mohammed Hamdoune Date: Fri, 11 Oct 2024 16:02:38 +0200 Subject: [PATCH 06/10] feat(pci-load-balancer): create page fifth step ref: DTCORE-2668 Signed-off-by: Mohammed Hamdoune --- .../create/InstanceTable.component.tsx | 4 +- .../apps/pci-load-balancer/src/constants.ts | 38 ++++++ .../src/pages/create/Create.page.tsx | 109 ++++++++++++++++-- .../src/pages/create/store.ts | 18 +++ 4 files changed, 159 insertions(+), 10 deletions(-) diff --git a/packages/manager/apps/pci-load-balancer/src/components/create/InstanceTable.component.tsx b/packages/manager/apps/pci-load-balancer/src/components/create/InstanceTable.component.tsx index 69c80cfd1ff1..06105e28c7c1 100644 --- a/packages/manager/apps/pci-load-balancer/src/components/create/InstanceTable.component.tsx +++ b/packages/manager/apps/pci-load-balancer/src/components/create/InstanceTable.component.tsx @@ -52,6 +52,7 @@ export interface InstanceTableProps { projectId: string; region: string; onChange: (config: ListenerConfiguration[]) => void; + className: string; } interface ListenerForm { @@ -70,6 +71,7 @@ export function InstanceTable({ projectId, region, onChange, + className, }: Readonly) { const { t } = useTranslation('instances-table'); const [listeners, setListeners] = useState([]); @@ -98,7 +100,7 @@ export function InstanceTable({ }, [listeners]); return ( -
+
{ - console.log('hna', subnets); if (!subnets) { return []; } @@ -129,11 +129,11 @@ export default function CreatePage(): JSX.Element { : subnets; }, [subnets, store.publicIp]); - const [productPageLink, regionPageLink] = [ + const [productPageLink, regionPageLink, gettingStartedLink] = [ PRODUCT_LINK[me?.ovhSubsidiary] || PRODUCT_LINK.DEFAULT, - REGION_AVAILABILITY_LINK[me?.ovhSubsidiary] || REGION_AVAILABILITY_LINK.DEFAULT, + GETTING_STARTED_LINK[me?.ovhSubsidiary] || GETTING_STARTED_LINK.DEFAULT, ]; return ( @@ -406,7 +406,7 @@ export default function CreatePage(): JSX.Element { store.check(StepsEnum.PRIVATE_NETWORK); store.lock(StepsEnum.PRIVATE_NETWORK); - store.open(StepsEnum.PRIVATE_NETWORK); + store.open(StepsEnum.INSTANCE); }, label: tCommon('common_stepper_next_button_label'), isDisabled: @@ -499,6 +499,97 @@ export default function CreatePage(): JSX.Element { + { + store.check(StepsEnum.INSTANCE); + store.lock(StepsEnum.INSTANCE); + + store.open(StepsEnum.INSTANCE); + }, + label: tCommon('common_stepper_next_button_label'), + }} + > + + {(_t) => ( + + + + )} + + +
+

+ + {tCreate( + 'octavia_load_balancer_create_instance_banner_text', + { + maxListeners: MAX_LISTENER, + maxInstances: MAX_INSTANCES_BY_LISTENER, + }, + )} + +

+

+ + + {tCreate( + 'octavia_load_balancer_create_instance_banner_text_bold', + )} + + +

+

+ + {tCreate( + 'octavia_load_balancer_create_instance_banner_health_monitor_text', + )} + +

+
+
+ { + store.set.listeners(config); + }} + /> +
); diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts index 7bff81aaa33f..add29f70e2f3 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts @@ -2,6 +2,7 @@ import { create } from 'zustand'; import { TRegion } from '@/api/hook/usePlans'; import { TFloatingIp } from '@/api/data/region'; import { TPrivateNetwork, TSubnet } from '@/api/data/network'; +import { ListenerConfiguration } from '@/components/create/InstanceTable.component'; export type TPlan = { code: string; @@ -21,6 +22,7 @@ export enum StepsEnum { REGION = 'REGION', PUBLIC_IP = 'PUBLIC_IP', PRIVATE_NETWORK = 'PRIVATE_NETWORK', + INSTANCE = 'INSTANCE', } export type TFormStore = { @@ -29,6 +31,7 @@ export type TFormStore = { publicIp: TFloatingIp; privateNetwork: TPrivateNetwork; subnet: TSubnet; + listeners: ListenerConfiguration[]; steps: Map; set: { size: (val: TPlan) => void; @@ -36,6 +39,7 @@ export type TFormStore = { publicIp: (val: TFloatingIp) => void; privateNetwork: (val: TPrivateNetwork) => void; subnet: (val: TSubnet) => void; + listeners: (val: ListenerConfiguration[]) => void; }; open: (step: StepsEnum) => void; close: (step: StepsEnum) => void; @@ -85,6 +89,14 @@ const initialSteps = () => isChecked: false, }, ], + [ + StepsEnum.INSTANCE, + { + isOpen: false, + isLocked: false, + isChecked: false, + }, + ], ]); export const useNewLoadBalancerStore = create()((set, get) => ({ @@ -93,6 +105,7 @@ export const useNewLoadBalancerStore = create()((set, get) => ({ publicIp: null, privateNetwork: null, subnet: null, + listeners: [], steps: initialSteps(), set: { size: (val: TPlan) => { @@ -120,6 +133,11 @@ export const useNewLoadBalancerStore = create()((set, get) => ({ subnet: val, }); }, + listeners: (val: ListenerConfiguration[]) => { + set({ + listeners: val, + }); + }, }, open: (id: StepsEnum) => { set((state) => { From 47305560c9eacaa4a16acd4320cf15c9bd88286a Mon Sep 17 00:00:00 2001 From: Mohammed Hamdoune Date: Tue, 15 Oct 2024 15:21:19 +0200 Subject: [PATCH 07/10] feat(pci-load-balancer): create page last step ref: DTCORE-2668 Signed-off-by: Mohammed Hamdoune --- .../pci-load-balancer/src/api/data/flavors.ts | 17 ++ .../api/data/{region.ts => floating-ips.ts} | 12 - .../src/api/data/gateways.ts | 34 +++ .../src/api/data/load-balancer.ts | 162 ++++++++++- .../pci-load-balancer/src/api/data/network.ts | 11 + .../pci-load-balancer/src/api/hook/flavors.ts | 23 ++ .../src/api/hook/gateways.ts | 22 ++ .../src/api/hook/useFloatingIps.ts | 10 + .../src/api/hook/useLoadBalancer.tsx | 46 ++++ .../src/api/hook/useNetwork.tsx | 12 + .../src/api/hook/useRegion.ts | 18 -- .../apps/pci-load-balancer/src/constants.ts | 3 + .../src/pages/create/Create.page.tsx | 255 +++++++++++++++--- .../src/pages/create/store.ts | 66 ++++- 14 files changed, 613 insertions(+), 78 deletions(-) create mode 100644 packages/manager/apps/pci-load-balancer/src/api/data/flavors.ts rename packages/manager/apps/pci-load-balancer/src/api/data/{region.ts => floating-ips.ts} (58%) create mode 100644 packages/manager/apps/pci-load-balancer/src/api/data/gateways.ts create mode 100644 packages/manager/apps/pci-load-balancer/src/api/hook/flavors.ts create mode 100644 packages/manager/apps/pci-load-balancer/src/api/hook/gateways.ts create mode 100644 packages/manager/apps/pci-load-balancer/src/api/hook/useFloatingIps.ts delete mode 100644 packages/manager/apps/pci-load-balancer/src/api/hook/useRegion.ts diff --git a/packages/manager/apps/pci-load-balancer/src/api/data/flavors.ts b/packages/manager/apps/pci-load-balancer/src/api/data/flavors.ts new file mode 100644 index 000000000000..389bfa922320 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/api/data/flavors.ts @@ -0,0 +1,17 @@ +import { v6 } from '@ovh-ux/manager-core-api'; +import { TPlan } from '@/pages/create/store'; +import { TFlavor } from '@/api/data/load-balancer'; + +export const getFlavor = async ( + projectId: string, + regionName: string, + size: TPlan, +): Promise => { + const { data } = await v6.get( + `/cloud/project/${projectId}/region/${regionName}/loadbalancing/flavor`, + ); + + return data.find( + (regionalizedFlavors) => regionalizedFlavors.name === size.technicalName, + ); +}; diff --git a/packages/manager/apps/pci-load-balancer/src/api/data/region.ts b/packages/manager/apps/pci-load-balancer/src/api/data/floating-ips.ts similarity index 58% rename from packages/manager/apps/pci-load-balancer/src/api/data/region.ts rename to packages/manager/apps/pci-load-balancer/src/api/data/floating-ips.ts index 245cffc2fb8a..df7f70ab00b7 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/data/region.ts +++ b/packages/manager/apps/pci-load-balancer/src/api/data/floating-ips.ts @@ -1,5 +1,4 @@ import { v6 } from '@ovh-ux/manager-core-api'; -import { TPrivateNetwork } from '@/api/data/network'; export type TFloatingIp = { associatedEntity: string; @@ -17,14 +16,3 @@ export const getFloatingIps = async (projectId: string, region: string) => { return data; }; - -export const getRegionPrivateNetworks = async ( - projectId: string, - region: string, -) => { - const { data } = await v6.get( - `/cloud/project/${projectId}/region/${region}/network`, - ); - - return data; -}; diff --git a/packages/manager/apps/pci-load-balancer/src/api/data/gateways.ts b/packages/manager/apps/pci-load-balancer/src/api/data/gateways.ts new file mode 100644 index 000000000000..59c2e9b2b136 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/api/data/gateways.ts @@ -0,0 +1,34 @@ +import { v6 } from '@ovh-ux/manager-core-api'; + +export type TSubnetGateway = { + externalInformation: { + ips: { + ip: string; + subnetId: string; + }[]; + networkId: string; + }; + id: string; + interfaces: { + id: string; + ip: string; + networkId: string; + subnetId: string; + }[]; + model: string; + name: string; + region: string; + status: string; +}; + +export const getSubnetGateways = async ( + projectId: string, + regionName: string, + subnetId: string, +) => { + const { data } = await v6.get( + `/cloud/project/${projectId}/region/${regionName}/gateway?subnetId=${subnetId}`, + ); + + return data; +}; diff --git a/packages/manager/apps/pci-load-balancer/src/api/data/load-balancer.ts b/packages/manager/apps/pci-load-balancer/src/api/data/load-balancer.ts index 4dca322056b3..9cdeabbcfce6 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/data/load-balancer.ts +++ b/packages/manager/apps/pci-load-balancer/src/api/data/load-balancer.ts @@ -1,5 +1,14 @@ import { v6 } from '@ovh-ux/manager-core-api'; -import { PROTOCOLS } from '@/constants'; +import { TInstance } from '@ovh-ux/manager-pci-common'; +import { + FLOATING_IP_CREATE_DESCRIPTION, + FLOATING_IP_TYPE, + PROTOCOLS, +} from '@/constants'; +import { TRegion } from '@/api/hook/usePlans'; +import { TPrivateNetwork, TSubnet } from '@/api/data/network'; +import { ListenerConfiguration } from '@/components/create/InstanceTable.component'; +import { TFloatingIp } from '@/api/data/floating-ips'; export enum LoadBalancerOperatingStatusEnum { ONLINE = 'online', @@ -188,3 +197,154 @@ export const editListener = async ({ return data; }; + +export type TCreateLoadBalancerParam = { + projectId: string; + flavor: TFlavor; + region: TRegion; + floatingIp: TFloatingIp; + privateNetwork: TPrivateNetwork; + subnet: TSubnet; + gateways: { id: string }[]; + listeners: ListenerConfiguration[]; + name: string; +}; + +export const createLoadBalancer = async ({ + projectId, + flavor, + region, + floatingIp, + privateNetwork, + subnet, + gateways, + listeners, + name, +}: Readonly) => { + type TNetworkParam = { + private: { + network: { + id: string; + subnetId: string; + }; + floatingIpCreate?: { + description: string; + }; + floatingIp?: { + id: string; + }; + gatewayCreate?: { + model: string; + name: string; + }; + gateway?: { + id: string; + }; + }; + }; + + const network: TNetworkParam = { + private: { + network: { + id: privateNetwork.id, + subnetId: subnet.id, + }, + }, + }; + + if (floatingIp.type === FLOATING_IP_TYPE.CREATE) { + network.private.floatingIpCreate = { + description: `${FLOATING_IP_CREATE_DESCRIPTION} ${name}`, + }; + } + + if ( + ![FLOATING_IP_TYPE.CREATE, FLOATING_IP_TYPE.NO_IP].includes(floatingIp.type) + ) { + network.private.floatingIp = { + id: floatingIp.id, + }; + } + + if (network.private.floatingIp || network.private.floatingIpCreate) { + if (!gateways?.length) { + network.private.gatewayCreate = { + model: 's', + name: `gateway-${region.name}`, + }; + } else { + network.private.gateway = { + id: gateways[0].id, + }; + } + } + + const formattedListeners = + listeners?.map((listener) => { + const pool: { + algorithm: string; + protocol: string; + healthMonitor?: { + name: string; + monitorType: string; + maxRetries: number; + delay: number; + timeout: number; + httpConfiguration: { + httpMethod: string; + urlPath: string; + }; + }; + members?: TInstance[]; + } = { + algorithm: 'roundRobin', + protocol: listener.protocol, + }; + + const instances = listener.instances?.reduce((filtered, instance) => { + if (Object.keys(instance).length > 0) { + filtered.push({ + address: instance.instance.ipAddresses[0].ip, + protocolPort: instance.port, + }); + } + return filtered; + }, []); + + if (listener.healthMonitor) { + pool.healthMonitor = { + name: `health-monitor-${listener.healthMonitor}`, + monitorType: listener.healthMonitor, + maxRetries: 4, + delay: 5, + timeout: 4, + httpConfiguration: { + httpMethod: 'GET', + urlPath: '/', + }, + }; + } + + if (instances.length) { + pool.members = instances; + } + + return { + port: listener.port, + protocol: listener.protocol, + pool, + }; + }) || []; + + const { data } = await v6.post( + `/cloud/project/${projectId}/region/${region.name}/loadbalancing/loadbalancer`, + { + flavorId: flavor.id, + network, + name, + listeners: formattedListeners, + }, + ); + + return data; +}; diff --git a/packages/manager/apps/pci-load-balancer/src/api/data/network.ts b/packages/manager/apps/pci-load-balancer/src/api/data/network.ts index 2a98255161c0..466530d92acb 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/data/network.ts +++ b/packages/manager/apps/pci-load-balancer/src/api/data/network.ts @@ -78,3 +78,14 @@ export const getPrivateNetworkSubnets = async ( ); return data; }; + +export const getRegionPrivateNetworks = async ( + projectId: string, + region: string, +) => { + const { data } = await v6.get( + `/cloud/project/${projectId}/region/${region}/network`, + ); + + return data; +}; diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/flavors.ts b/packages/manager/apps/pci-load-balancer/src/api/hook/flavors.ts new file mode 100644 index 000000000000..818011b4d10b --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/flavors.ts @@ -0,0 +1,23 @@ +import { useQuery } from '@tanstack/react-query'; +import { TPlan } from '@/pages/create/store'; +import { getFlavor } from '@/api/data/flavors'; + +export const useGetFlavor = ( + projectId: string, + regionName: string, + size: TPlan, +) => + useQuery({ + queryKey: [ + 'project', + projectId, + 'region', + regionName, + 'size', + size?.code, + 'flavor', + ], + queryFn: () => getFlavor(projectId, regionName, size), + enabled: !!projectId && !!regionName && !!size, + throwOnError: true, + }); diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/gateways.ts b/packages/manager/apps/pci-load-balancer/src/api/hook/gateways.ts new file mode 100644 index 000000000000..ad515c0ec901 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/gateways.ts @@ -0,0 +1,22 @@ +import { useQuery } from '@tanstack/react-query'; +import { getSubnetGateways } from '@/api/data/gateways'; + +export const useGetSubnetGateways = ( + projectId: string, + regionName: string, + subnetId: string, +) => + useQuery({ + queryKey: [ + 'project', + projectId, + 'region', + regionName, + 'subnet', + subnetId, + 'gateways', + ], + queryFn: () => getSubnetGateways(projectId, regionName, subnetId), + enabled: !!projectId && !!regionName && !!subnetId, + throwOnError: true, + }); diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/useFloatingIps.ts b/packages/manager/apps/pci-load-balancer/src/api/hook/useFloatingIps.ts new file mode 100644 index 000000000000..4c93122f67ef --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/useFloatingIps.ts @@ -0,0 +1,10 @@ +import { useQuery } from '@tanstack/react-query'; +import { getFloatingIps } from '../data/floating-ips'; + +export const useGetFloatingIps = (projectId: string, region: string) => + useQuery({ + queryKey: ['project', projectId, 'region', region, 'floating-ips'], + queryFn: () => getFloatingIps(projectId, region), + enabled: !!projectId && !!region, + throwOnError: true, + }); diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/useLoadBalancer.tsx b/packages/manager/apps/pci-load-balancer/src/api/hook/useLoadBalancer.tsx index 713d2bde38a0..baa64cee0f3f 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/hook/useLoadBalancer.tsx +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/useLoadBalancer.tsx @@ -14,6 +14,8 @@ import { updateLoadBalancerName, TLoadBalancerListener, editListener, + TCreateLoadBalancerParam, + createLoadBalancer, } from '../data/load-balancer'; import queryClient from '@/queryClient'; import { PROTOCOLS } from '@/constants'; @@ -331,3 +333,47 @@ export const useLoadBalancerListeners = ( ], ); }; + +export const useCreateLoadBalancer = ({ + projectId, + flavor, + region, + floatingIp, + privateNetwork, + subnet, + gateways, + listeners, + name, + onSuccess, + onError, +}: TCreateLoadBalancerParam & { + onError: (cause: Error) => void; + onSuccess: () => void; +}) => { + const mutation = useMutation({ + mutationFn: async () => + createLoadBalancer({ + projectId, + flavor, + region, + floatingIp, + privateNetwork, + subnet, + gateways, + listeners, + name, + }), + onError, + onSuccess: async () => { + // TDOO invalidate right query + // await queryClient.invalidateQueries({ + // queryKey: getAllLoadBalancersQueryKey(projectId), + // }); + onSuccess(); + }, + }); + return { + doCreateLoadBalancer: () => mutation.mutate(), + ...mutation, + }; +}; diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/useNetwork.tsx b/packages/manager/apps/pci-load-balancer/src/api/hook/useNetwork.tsx index 6396215d935f..b619eea6e4be 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/hook/useNetwork.tsx +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/useNetwork.tsx @@ -3,6 +3,7 @@ import { getPrivateNetworkByRegion, getPrivateNetworks, getPrivateNetworkSubnets, + getRegionPrivateNetworks, getSubnetByNetworkAndRegion, } from '../data/network'; @@ -76,3 +77,14 @@ export const useGetPrivateNetworkSubnets = ( enabled: !!projectId && !!region && !!networkId, throwOnError: true, }); + +export const useGetRegionPrivateNetworks = ( + projectId: string, + region: string, +) => + useQuery({ + queryKey: ['project', projectId, 'region', region, 'networks'], + queryFn: () => getRegionPrivateNetworks(projectId, region), + enabled: !!projectId && !!region, + throwOnError: true, + }); diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/useRegion.ts b/packages/manager/apps/pci-load-balancer/src/api/hook/useRegion.ts deleted file mode 100644 index 990612d93512..000000000000 --- a/packages/manager/apps/pci-load-balancer/src/api/hook/useRegion.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { getFloatingIps, getRegionPrivateNetworks } from '../data/region'; - -export const useGetFloatingIps = (projectId: string, region: string) => - useQuery({ - queryKey: ['project', projectId, 'region', region, 'floating-ips'], - queryFn: () => getFloatingIps(projectId, region), - enabled: !!projectId && !!region, - throwOnError: true, - }); - -export const useGetRegionNetworks = (projectId: string, region: string) => - useQuery({ - queryKey: ['project', projectId, 'region', region, 'networks'], - queryFn: () => getRegionPrivateNetworks(projectId, region), - enabled: !!projectId && !!region, - throwOnError: true, - }); diff --git a/packages/manager/apps/pci-load-balancer/src/constants.ts b/packages/manager/apps/pci-load-balancer/src/constants.ts index 25ce1f3634c9..1c18b502a306 100644 --- a/packages/manager/apps/pci-load-balancer/src/constants.ts +++ b/packages/manager/apps/pci-load-balancer/src/constants.ts @@ -211,6 +211,9 @@ export const GETTING_STARTED_LINK = { export const MAX_LISTENER = 5; export const MAX_INSTANCES_BY_LISTENER = 5; +export const LOAD_BALANCER_NAME_REGEX = /^[A-Za-z0-9_.-]+$/; +export const FLOATING_IP_CREATE_DESCRIPTION = + 'FIP created by OVHCloud Control Panel (Manager) for Load Balancer'; export const RULE_TYPES = { COOKIE: 'cookie', diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx b/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx index 5747e82854cf..2433c3563124 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx @@ -1,6 +1,8 @@ import { OsdsBreadcrumb, + OsdsButton, OsdsFormField, + OsdsInput, OsdsLink, OsdsMessage, OsdsSelect, @@ -14,12 +16,15 @@ import { TilesInputComponent, useCatalogPrice, useMe, + useNotifications, useProjectUrl, } from '@ovh-ux/manager-react-components'; -import { useHref, useParams } from 'react-router-dom'; +import { useHref, useNavigate, useParams } from 'react-router-dom'; import { useCatalog, useProject } from '@ovh-ux/manager-pci-common'; import { Translation, useTranslation } from 'react-i18next'; import { + ODS_BUTTON_VARIANT, + ODS_INPUT_TYPE, ODS_MESSAGE_TYPE, ODS_TEXT_LEVEL, ODS_TEXT_SIZE, @@ -29,29 +34,40 @@ import { ODS_THEME_TYPOGRAPHY_SIZE, } from '@ovhcloud/ods-common-theming'; import { clsx } from 'clsx'; -import { useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; +import { ApiError } from '@ovh-ux/manager-core-api'; import SizeInputComponent from '@/pages/create/SizeInput.component'; import { AGORA_FLOATING_IP_REGEX, FLOATING_IP_TYPE, GETTING_STARTED_LINK, + LOAD_BALANCER_NAME_REGEX, MAX_INSTANCES_BY_LISTENER, MAX_LISTENER, NETWORK_PRIVATE_VISIBILITY, PRODUCT_LINK, REGION_AVAILABILITY_LINK, } from '@/constants'; -import { StepsEnum, useNewLoadBalancerStore } from '@/pages/create/store'; +import { StepsEnum, useCreateStore } from '@/pages/create/store'; import { TRegion, useGetRegions } from '@/api/hook/usePlans'; -import { useGetFloatingIps, useGetRegionNetworks } from '@/api/hook/useRegion'; -import { useGetPrivateNetworkSubnets } from '@/api/hook/useNetwork'; +import { + useGetPrivateNetworkSubnets, + useGetRegionPrivateNetworks, +} from '@/api/hook/useNetwork'; import { InstanceTable } from '@/components/create/InstanceTable.component'; +import { useGetFloatingIps } from '@/api/hook/useFloatingIps'; +import { useGetSubnetGateways } from '@/api/hook/gateways'; +import { useGetFlavor } from '@/api/hook/flavors'; type TState = { selectedContinent: string | undefined; + isNameTouched: boolean; }; export default function CreatePage(): JSX.Element { + const navigate = useNavigate(); + const { addSuccess, addError } = useNotifications(); + const projectHref = useProjectUrl('public-cloud'); const backHref = useHref('..'); @@ -65,17 +81,18 @@ export default function CreatePage(): JSX.Element { const { getFormattedHourlyCatalogPrice } = useCatalogPrice(5); - const store = useNewLoadBalancerStore(); + const store = useCreateStore(); const [state, setState] = useState({ selectedContinent: undefined, + isNameTouched: false, }); const { me } = useMe(); const { data: project } = useProject(); const { data: catalog } = useCatalog(); - const { data: privateNetworks } = useGetRegionNetworks( + const { data: privateNetworks } = useGetRegionPrivateNetworks( projectId, store.region?.name, ); @@ -89,46 +106,68 @@ export default function CreatePage(): JSX.Element { store.privateNetwork?.id, ); + const { + data: subnetGateways, + isPending: isSubnetGatewaysPending, + } = useGetSubnetGateways(projectId, store.region?.name, store.subnet?.id); + const regions = useGetRegions(projectId); - const [floatingIpsList, privateNetworksList] = [ - [ - { - associatedEntity: '', - id: 'create', - ip: tCreate( - 'octavia_load_balancer_create_floating_ip_field_new_floating_ip', - ), - networkId: '', - status: '', - type: '', - }, - { - associatedEntity: '', - id: 'none', - ip: tCreate( - 'octavia_load_balancer_create_floating_ip_field_no_floating_ip', + const { data: flavor } = useGetFlavor( + projectId, + store.region?.name, + store.size, + ); + + const [floatingIpsList, privateNetworksList, subnetsList] = [ + useMemo( + () => [ + { + associatedEntity: null, + id: 'create', + ip: tCreate( + 'octavia_load_balancer_create_floating_ip_field_new_floating_ip', + ), + networkId: '', + status: '', + type: FLOATING_IP_TYPE.CREATE, + }, + { + associatedEntity: null, + id: 'none', + ip: tCreate( + 'octavia_load_balancer_create_floating_ip_field_no_floating_ip', + ), + networkId: '', + status: '', + type: FLOATING_IP_TYPE.NO_IP, + }, + ...(floatingIps || []) + .filter((ip) => !ip.associatedEntity) + .map((ip) => ({ + ...ip, + type: FLOATING_IP_TYPE.IP, + })), + ], + [floatingIps], + ), + useMemo( + () => + (privateNetworks || []).filter( + (network) => network.visibility === NETWORK_PRIVATE_VISIBILITY, ), - networkId: '', - status: '', - type: '', - }, - ...(floatingIps || []), - ], - (privateNetworks || []).filter( - (network) => network.visibility === NETWORK_PRIVATE_VISIBILITY, + [privateNetworks], ), + useMemo(() => { + if (!subnets) { + return []; + } + return store.publicIp?.type !== FLOATING_IP_TYPE.NO_IP + ? subnets.filter((subnet) => subnet.gatewayIp) + : subnets; + }, [subnets, store.publicIp]), ]; - const subnetsList = useMemo(() => { - if (!subnets) { - return []; - } - return store.publicIp?.type !== FLOATING_IP_TYPE.NO_IP - ? subnets.filter((subnet) => subnet.gatewayIp) - : subnets; - }, [subnets, store.publicIp]); - const [productPageLink, regionPageLink, gettingStartedLink] = [ PRODUCT_LINK[me?.ovhSubsidiary] || PRODUCT_LINK.DEFAULT, REGION_AVAILABILITY_LINK[me?.ovhSubsidiary] || @@ -136,6 +175,62 @@ export default function CreatePage(): JSX.Element { GETTING_STARTED_LINK[me?.ovhSubsidiary] || GETTING_STARTED_LINK.DEFAULT, ]; + useEffect(() => { + store.set.projectId(projectId); + }, []); + + useEffect(() => { + if (floatingIpsList.length > 0) { + store.set.publicIp(floatingIpsList[0]); + } + }, [floatingIpsList]); + + useEffect(() => { + if (privateNetworksList.length > 0) { + store.set.privateNetwork(privateNetworksList[0]); + } + }, [privateNetworksList]); + + useEffect(() => { + if (subnetsList.length > 0) { + store.set.subnet(subnetsList[0]); + } + }, [subnetsList]); + + useEffect(() => { + store.set.gateways(subnetGateways || []); + }, [subnetGateways]); + + const create = async () => { + await store.create( + flavor, + () => { + addSuccess( + + {(_t) => _t('octavia_load_balancer_create_banner')} + , + false, + ); + navigate('..'); + }, + (error: ApiError) => { + console.log(error); + + addError( + + {(_t) => + _t('octavia_load_balancer_global_error', { + // message: error.data.message, + // requestId: error.headers('X-Ovh-Queryid'), + }) + } + , + false, + ); + }, + ); + }; + return ( <> { - store.set.listeners(config); + onChange={(listeners) => { + store.set.listeners(listeners); }} /> + + 70) + ? tCommon('common_field_error_pattern') + : '' + } + > + + {tCreate('octavia_load_balancer_create_name_field_label')} + + + {tCreate('octavia_load_balancer_create_name_field_help')} + + + store.set.name(event.target.value as string) + } + className={ + state.isNameTouched && + (!store.name.match(LOAD_BALANCER_NAME_REGEX) || + store.name.length > 70) + ? 'bg-red-100 border-red-500 text-red-500 focus:text-red-500' + : 'border-color-[var(--ods-color-default-200)] bg-white' + } + onOdsInputBlur={() => { + setState({ + ...state, + isNameTouched: true, + }); + }} + /> + +
+ + {tCreate('octavia_load_balancer_create_title')} + + + {tCommon('common_cancel')} + +
+
); diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts index add29f70e2f3..a27e9140e631 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts @@ -1,8 +1,11 @@ import { create } from 'zustand'; +import { ApiError } from '@ovh-ux/manager-core-api'; import { TRegion } from '@/api/hook/usePlans'; -import { TFloatingIp } from '@/api/data/region'; import { TPrivateNetwork, TSubnet } from '@/api/data/network'; import { ListenerConfiguration } from '@/components/create/InstanceTable.component'; +import { TSubnetGateway } from '@/api/data/gateways'; +import { TFloatingIp } from '@/api/data/floating-ips'; +import { createLoadBalancer, TFlavor } from '@/api/data/load-balancer'; export type TPlan = { code: string; @@ -23,23 +26,30 @@ export enum StepsEnum { PUBLIC_IP = 'PUBLIC_IP', PRIVATE_NETWORK = 'PRIVATE_NETWORK', INSTANCE = 'INSTANCE', + NAME = 'NAME', } -export type TFormStore = { +export type TCreateStore = { + projectId: string; size: TPlan; region: TRegion; publicIp: TFloatingIp; privateNetwork: TPrivateNetwork; subnet: TSubnet; + gateways: TSubnetGateway[]; listeners: ListenerConfiguration[]; + name: string; steps: Map; set: { + projectId: (val: string) => void; size: (val: TPlan) => void; region: (val: TRegion) => void; publicIp: (val: TFloatingIp) => void; privateNetwork: (val: TPrivateNetwork) => void; subnet: (val: TSubnet) => void; + gateways: (val: TSubnetGateway[]) => void; listeners: (val: ListenerConfiguration[]) => void; + name: (val: string) => void; }; open: (step: StepsEnum) => void; close: (step: StepsEnum) => void; @@ -53,6 +63,12 @@ export type TFormStore = { edit: (step: StepsEnum) => void; reset: () => void; + + create: ( + flavor: TFlavor, + onSuccess: () => void, + onError: (cause: ApiError) => void, + ) => Promise; }; const initialSteps = () => @@ -97,17 +113,33 @@ const initialSteps = () => isChecked: false, }, ], + [ + StepsEnum.NAME, + { + isOpen: false, + isLocked: false, + isChecked: false, + }, + ], ]); -export const useNewLoadBalancerStore = create()((set, get) => ({ +export const useCreateStore = create()((set, get) => ({ + projectId: '', size: null, region: null, publicIp: null, privateNetwork: null, subnet: null, + gateways: [], listeners: [], + name: '', steps: initialSteps(), set: { + projectId: (val: string) => { + set({ + projectId: val, + }); + }, size: (val: TPlan) => { set({ size: val, @@ -133,11 +165,21 @@ export const useNewLoadBalancerStore = create()((set, get) => ({ subnet: val, }); }, + gateways: (val: TSubnetGateway[]) => { + set({ + gateways: val, + }); + }, listeners: (val: ListenerConfiguration[]) => { set({ listeners: val, }); }, + name: (val: string) => { + set({ + name: val, + }); + }, }, open: (id: StepsEnum) => { set((state) => { @@ -214,4 +256,22 @@ export const useNewLoadBalancerStore = create()((set, get) => ({ steps: initialSteps(), })); }, + create: async (flavor: TFlavor, onSuccess, onError) => { + try { + await createLoadBalancer({ + projectId: get().projectId, + flavor, + region: get().region, + floatingIp: get().publicIp, + privateNetwork: get().privateNetwork, + subnet: get().subnet, + gateways: get().gateways, + listeners: get().listeners, + name: get().name, + }); + onSuccess(); + } catch (e) { + onError(e); + } + }, })); From 4194ab0fbe56f7e117310d5bacf13fbe9e303aeb Mon Sep 17 00:00:00 2001 From: Mohammed Hamdoune Date: Wed, 16 Oct 2024 15:58:39 +0200 Subject: [PATCH 08/10] feat(pci-load-balancer): refacto ref: DTCORE-2668 Signed-off-by: Mohammed Hamdoune --- .../detail/nodepools/new/New.page.spec.tsx | 12 - .../new/__snapshots__/New.page.spec.tsx.snap | 174 ------ .../src/api/hook/useAddons.ts | 32 + .../api/hook/{flavors.ts => useFlavors.ts} | 0 .../api/hook/{gateways.ts => useGateways.ts} | 0 .../src/api/hook/usePlans.ts | 71 --- .../src/api/hook/useRegions.ts | 82 +++ .../apps/pci-load-balancer/src/constants.ts | 1 + .../src/pages/create/Create.page.tsx | 560 ++++++++++++------ .../src/pages/create/SizeInput.component.tsx | 36 +- .../src/pages/create/store.ts | 65 +- 11 files changed, 554 insertions(+), 479 deletions(-) delete mode 100644 packages/manager/apps/pci-kubernetes/src/pages/detail/nodepools/new/New.page.spec.tsx delete mode 100644 packages/manager/apps/pci-kubernetes/src/pages/detail/nodepools/new/__snapshots__/New.page.spec.tsx.snap create mode 100644 packages/manager/apps/pci-load-balancer/src/api/hook/useAddons.ts rename packages/manager/apps/pci-load-balancer/src/api/hook/{flavors.ts => useFlavors.ts} (100%) rename packages/manager/apps/pci-load-balancer/src/api/hook/{gateways.ts => useGateways.ts} (100%) create mode 100644 packages/manager/apps/pci-load-balancer/src/api/hook/useRegions.ts diff --git a/packages/manager/apps/pci-kubernetes/src/pages/detail/nodepools/new/New.page.spec.tsx b/packages/manager/apps/pci-kubernetes/src/pages/detail/nodepools/new/New.page.spec.tsx deleted file mode 100644 index a575ab380e81..000000000000 --- a/packages/manager/apps/pci-kubernetes/src/pages/detail/nodepools/new/New.page.spec.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { describe } from 'vitest'; -import { render } from '@testing-library/react'; -import { wrapper } from '@/wrapperRenders'; -import NewPage from './New.page'; - -describe('New', () => { - it('should render correctly', () => { - const { container } = render(, { wrapper }); - - expect(container).toMatchSnapshot(); - }); -}); diff --git a/packages/manager/apps/pci-kubernetes/src/pages/detail/nodepools/new/__snapshots__/New.page.spec.tsx.snap b/packages/manager/apps/pci-kubernetes/src/pages/detail/nodepools/new/__snapshots__/New.page.spec.tsx.snap deleted file mode 100644 index 72f8df239840..000000000000 --- a/packages/manager/apps/pci-kubernetes/src/pages/detail/nodepools/new/__snapshots__/New.page.spec.tsx.snap +++ /dev/null @@ -1,174 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`New > should render correctly 1`] = ` -
- - kube_common_create_node_pool - -
-
-
-
- - 1 - -
-
-
-
-
- kube_add_node_pool_config_title -
-
-
- - - kube_add_node_pool_name_label - - - -
-
-
-
-
-
-
- - 2 - -
-
-
-
-
- kube_common_node_pool_title -
-
-
-
-
-
-
-
- - 3 - -
-
-
-
-
- kube_common_node_pool_autoscaling_title -
-
-
-
-
-
-
-
- - 4 - -
-
-
-
-
- kube_add_billing_anti_affinity_title -
-
-
-
-
-`; diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/useAddons.ts b/packages/manager/apps/pci-load-balancer/src/api/hook/useAddons.ts new file mode 100644 index 000000000000..929a1949eb7f --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/useAddons.ts @@ -0,0 +1,32 @@ +import { useCatalog } from '@ovh-ux/manager-pci-common'; +import { useMemo } from 'react'; +import { TAddon } from '@/pages/create/store'; + +const SIZE_FLAVOUR_REGEX = /octavia-loadbalancer.loadbalancer-([sml]).hour.consumption/; + +export const useGetAddons = () => { + const { data: catalog, isPending: isCatalogPending } = useCatalog(); + + return { + data: useMemo( + () => + (catalog?.addons + ? catalog.addons.reduce((filtered: TAddon[], addon) => { + const found = addon.planCode.match(SIZE_FLAVOUR_REGEX); + if (found) { + filtered.push({ + code: found[1], + price: addon.pricings[0].price, + label: found[1].toUpperCase(), + technicalName: addon.blobs.technical.name, + }); + } + return filtered; + }, []) + : [] + ).sort((a, b) => a.price - b.price), + [catalog], + ), + isPending: isCatalogPending, + }; +}; diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/flavors.ts b/packages/manager/apps/pci-load-balancer/src/api/hook/useFlavors.ts similarity index 100% rename from packages/manager/apps/pci-load-balancer/src/api/hook/flavors.ts rename to packages/manager/apps/pci-load-balancer/src/api/hook/useFlavors.ts diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/gateways.ts b/packages/manager/apps/pci-load-balancer/src/api/hook/useGateways.ts similarity index 100% rename from packages/manager/apps/pci-load-balancer/src/api/hook/gateways.ts rename to packages/manager/apps/pci-load-balancer/src/api/hook/useGateways.ts diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/usePlans.ts b/packages/manager/apps/pci-load-balancer/src/api/hook/usePlans.ts index 63edc36fddf5..e0a276ae362d 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/hook/usePlans.ts +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/usePlans.ts @@ -1,10 +1,6 @@ import { useQuery } from '@tanstack/react-query'; import { useMe } from '@ovh-ux/manager-react-components'; -import { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; import { getPlans } from '../data/plans'; -import { SIZE_FLAVOUR_REGEX } from '@/constants'; -import { useGetPrivateNetworks } from '@/api/hook/useNetwork'; export const useGetPlans = (projectId: string) => { const { me } = useMe(); @@ -15,70 +11,3 @@ export const useGetPlans = (projectId: string) => { throwOnError: true, }); }; - -export type TRegion = { - name: string; - isEnabled: boolean; - continentCode: string; - macroName: string; - microName: string; - continent: string; -}; -const getMacroRegion = (region: string) => { - const regionSubStrings = region.split('-'); - - const macroRegionMap = [ - null, - regionSubStrings[0].split(/(\d)/)[0], - regionSubStrings[0], - regionSubStrings[2], - regionSubStrings[2] === 'LZ' ? regionSubStrings[3] : regionSubStrings[2], - regionSubStrings[3], - ]; - return macroRegionMap[regionSubStrings.length] || 'Unknown_Macro_Region'; -}; -export const useGetRegions = (projectId: string): Map => { - const { data: response } = useGetPlans(projectId); - const { data: networks } = useGetPrivateNetworks(projectId); - const { t: tRegion } = useTranslation('regions'); - - return useMemo(() => { - const regions = new Map(); - if (response && networks) { - response?.plans.forEach((plan) => { - const match = plan.code.match(SIZE_FLAVOUR_REGEX); - if (match) { - // plan is included - const code = match[1]; - if (!regions.has(code)) { - regions.set(code, []); - } - plan.regions.forEach((region) => { - regions.get(code).push({ - name: region.name, - isEnabled: networks.some((network) => - network.regions.some((r) => r.region === region.name), - ), - continentCode: region.continentCode, - macroName: tRegion( - `manager_components_region_${getMacroRegion(region.name)}`, - ), - microName: tRegion( - `manager_components_region_${getMacroRegion( - region.name, - )}_micro`, - { micro: region.name }, - ), - continent: tRegion( - `manager_components_region_continent_${getMacroRegion( - region.name, - )}`, - ), - }); - }); - } - }); - } - return regions; - }, [response, networks]); -}; diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/useRegions.ts b/packages/manager/apps/pci-load-balancer/src/api/hook/useRegions.ts new file mode 100644 index 000000000000..8b387d12b0bc --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/useRegions.ts @@ -0,0 +1,82 @@ +import { useTranslation } from 'react-i18next'; +import { useMemo } from 'react'; +import { useGetPrivateNetworks } from '@/api/hook/useNetwork'; +import { SIZE_FLAVOUR_REGEX } from '@/constants'; +import { useGetPlans } from '@/api/hook/usePlans'; + +export type TRegion = { + name: string; + isEnabled: boolean; + continentCode: string; + macroName: string; + microName: string; + continent: string; +}; +const getMacroRegion = (region: string) => { + const regionSubStrings = region.split('-'); + + const macroRegionMap = [ + null, + regionSubStrings[0].split(/(\d)/)[0], + regionSubStrings[0], + regionSubStrings[2], + regionSubStrings[2] === 'LZ' ? regionSubStrings[3] : regionSubStrings[2], + regionSubStrings[3], + ]; + return macroRegionMap[regionSubStrings.length] || 'Unknown_Macro_Region'; +}; +export const useGetRegions = ( + projectId: string, +): { data: Map; isPending: boolean } => { + const { + data: plansResponse, + isPending: isPlansResponsePending, + } = useGetPlans(projectId); + const { + data: networks, + isPending: isNetworksPending, + } = useGetPrivateNetworks(projectId); + const { t: tRegion } = useTranslation('regions'); + + return { + data: useMemo(() => { + const regions = new Map(); + if (plansResponse && networks) { + plansResponse?.plans.forEach((plan) => { + const match = plan.code.match(SIZE_FLAVOUR_REGEX); + if (match) { + const code = match[1]; + if (!regions.has(code)) { + regions.set(code, []); + } + plan.regions.forEach((region) => { + regions.get(code).push({ + name: region.name, + isEnabled: networks.some((network) => + network.regions.some((r) => r.region === region.name), + ), + continentCode: region.continentCode, + macroName: tRegion( + `manager_components_region_${getMacroRegion(region.name)}`, + ), + microName: tRegion( + `manager_components_region_${getMacroRegion( + region.name, + )}_micro`, + { micro: region.name }, + ), + continent: tRegion( + `manager_components_region_continent_${getMacroRegion( + region.name, + )}`, + ), + }); + }); + } + }); + } + return regions; + }, [plansResponse, networks]), + isPending: isPlansResponsePending || isNetworksPending, + }; +}; diff --git a/packages/manager/apps/pci-load-balancer/src/constants.ts b/packages/manager/apps/pci-load-balancer/src/constants.ts index 1c18b502a306..ecb57e3ce336 100644 --- a/packages/manager/apps/pci-load-balancer/src/constants.ts +++ b/packages/manager/apps/pci-load-balancer/src/constants.ts @@ -214,6 +214,7 @@ export const MAX_INSTANCES_BY_LISTENER = 5; export const LOAD_BALANCER_NAME_REGEX = /^[A-Za-z0-9_.-]+$/; export const FLOATING_IP_CREATE_DESCRIPTION = 'FIP created by OVHCloud Control Panel (Manager) for Load Balancer'; +export const AGORA_GATEWAY_REGEX = /gateway.s.hour.consumption/; export const RULE_TYPES = { COOKIE: 'cookie', diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx b/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx index 2433c3563124..2e7cdc3a753e 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx @@ -7,6 +7,7 @@ import { OsdsMessage, OsdsSelect, OsdsSelectOption, + OsdsSpinner, OsdsText, } from '@ovhcloud/ods-components/react'; import { @@ -39,6 +40,7 @@ import { ApiError } from '@ovh-ux/manager-core-api'; import SizeInputComponent from '@/pages/create/SizeInput.component'; import { AGORA_FLOATING_IP_REGEX, + AGORA_GATEWAY_REGEX, FLOATING_IP_TYPE, GETTING_STARTED_LINK, LOAD_BALANCER_NAME_REGEX, @@ -49,15 +51,16 @@ import { REGION_AVAILABILITY_LINK, } from '@/constants'; import { StepsEnum, useCreateStore } from '@/pages/create/store'; -import { TRegion, useGetRegions } from '@/api/hook/usePlans'; +import { TRegion, useGetRegions } from '@/api/hook/useRegions'; import { useGetPrivateNetworkSubnets, useGetRegionPrivateNetworks, } from '@/api/hook/useNetwork'; import { InstanceTable } from '@/components/create/InstanceTable.component'; import { useGetFloatingIps } from '@/api/hook/useFloatingIps'; -import { useGetSubnetGateways } from '@/api/hook/gateways'; -import { useGetFlavor } from '@/api/hook/flavors'; +import { useGetSubnetGateways } from '@/api/hook/useGateways'; +import { useGetFlavor } from '@/api/hook/useFlavors'; +import { useGetAddons } from '@/api/hook/useAddons'; type TState = { selectedContinent: string | undefined; @@ -91,16 +94,20 @@ export default function CreatePage(): JSX.Element { const { me } = useMe(); const { data: project } = useProject(); + const { data: addons, isPending: isAddonsPending } = useGetAddons(); const { data: catalog } = useCatalog(); - const { data: privateNetworks } = useGetRegionPrivateNetworks( - projectId, - store.region?.name, - ); - const { data: floatingIps } = useGetFloatingIps( - projectId, - store.region?.name, - ); - const { data: subnets } = useGetPrivateNetworkSubnets( + const { + data: privateNetworks, + isPending: isPrivateNetworksPending, + } = useGetRegionPrivateNetworks(projectId, store.region?.name); + const { + data: floatingIps, + isPending: isFloatingIpsPending, + } = useGetFloatingIps(projectId, store.region?.name); + const { + data: subnets, + isPending: isSubnetsPending, + } = useGetPrivateNetworkSubnets( projectId, store.region?.name, store.privateNetwork?.id, @@ -111,12 +118,14 @@ export default function CreatePage(): JSX.Element { isPending: isSubnetGatewaysPending, } = useGetSubnetGateways(projectId, store.region?.name, store.subnet?.id); - const regions = useGetRegions(projectId); + const { data: regions, isPending: isRegionsPending } = useGetRegions( + projectId, + ); const { data: flavor } = useGetFlavor( projectId, store.region?.name, - store.size, + store.addon, ); const [floatingIpsList, privateNetworksList, subnetsList] = [ @@ -176,6 +185,7 @@ export default function CreatePage(): JSX.Element { ]; useEffect(() => { + store.reset(); store.set.projectId(projectId); }, []); @@ -201,12 +211,13 @@ export default function CreatePage(): JSX.Element { store.set.gateways(subnetGateways || []); }, [subnetGateways]); + // TODO gateways messages const create = async () => { await store.create( flavor, () => { addSuccess( - + {(_t) => _t('octavia_load_balancer_create_banner')} , false, @@ -214,16 +225,23 @@ export default function CreatePage(): JSX.Element { navigate('..'); }, (error: ApiError) => { - console.log(error); - + console.log('error', error); addError( - - {(_t) => - _t('octavia_load_balancer_global_error', { - // message: error.data.message, - // requestId: error.headers('X-Ovh-Queryid'), - }) - } + + {(_t) => ( + ; + }).headers['x-ovh-queryid'], + }), + }} + > + )} , false, ); @@ -237,7 +255,7 @@ export default function CreatePage(): JSX.Element { items={[ { href: projectHref, - label: project.description, + label: project?.description, }, { href: backHref, @@ -278,7 +296,22 @@ export default function CreatePage(): JSX.Element { store.open(StepsEnum.REGION); }, label: tCommon('common_stepper_next_button_label'), - isDisabled: store.size === null, + isDisabled: store.addon === null, + }} + edit={{ + action: () => { + store.unlock(StepsEnum.SIZE); + store.uncheck(StepsEnum.SIZE); + store.open(StepsEnum.SIZE); + store.reset( + StepsEnum.REGION, + StepsEnum.PUBLIC_IP, + StepsEnum.PRIVATE_NETWORK, + StepsEnum.INSTANCE, + StepsEnum.NAME, + ); + }, + label: tCommon('common_stepper_modify_this_step'), }} > - + {isAddonsPending ? ( +
+ +
+ ) : ( + + )} { + store.unlock(StepsEnum.REGION); + store.uncheck(StepsEnum.REGION); + store.open(StepsEnum.REGION); + store.reset( + StepsEnum.PUBLIC_IP, + StepsEnum.PRIVATE_NETWORK, + StepsEnum.INSTANCE, + StepsEnum.NAME, + ); + }, + label: tCommon('common_stepper_modify_this_step'), + }} > - - {tCreate('octavia_load_balancer_create_region_intro')}{' '} - + - {tCreate('octavia_load_balancer_create_region_link')} - - - - items={(regions?.get(store.size?.code) || []).filter( - (region) => region.isEnabled, - )} - value={store.region} - onInput={(region) => store.set.region(region)} - label={(region) => region.name} - group={{ - by: (region) => region.continent, - label: (continent) => ( - -
+ {tCreate('octavia_load_balancer_create_region_link')} + + +
+ {isRegionsPending ? ( +
+ +
+ ) : ( + + items={(regions?.get(store.addon?.code) || []).filter( + (region) => region.isEnabled, + )} + value={store.region} + onInput={(region) => store.set.region(region)} + label={(region) => region.name} + group={{ + by: (region) => region.continent, + label: (continent) => ( + - {undefined === continent - ? tRegionsList('pci_project_regions_list_continent_all') - : continent} -
- - ), - showAllTab: true, - }} - stack={{ - by: (region) => region?.macroName, - label: (macroName) => macroName, - title: () => tRegionsList('pci_project_regions_list_region'), - onChange: (continent) => { - setState({ ...state, selectedContinent: continent }); - }, - }} - /> +
+ {undefined === continent + ? tRegionsList('pci_project_regions_list_continent_all') + : continent} +
+ + ), + showAllTab: true, + }} + stack={{ + by: (region) => region?.macroName, + label: (macroName) => macroName, + title: () => tRegionsList('pci_project_regions_list_region'), + onChange: (continent) => { + setState({ ...state, selectedContinent: continent }); + }, + }} + /> + )} { + store.unlock(StepsEnum.PUBLIC_IP); + store.uncheck(StepsEnum.PUBLIC_IP); + store.open(StepsEnum.PUBLIC_IP); + store.reset( + StepsEnum.PRIVATE_NETWORK, + StepsEnum.INSTANCE, + StepsEnum.NAME, + ); + }, + label: tCommon('common_stepper_modify_this_step'), + }} > {tCreate('octavia_load_balancer_create_floating_ip_intro')} - - - {tCreate('octavia_load_balancer_create_floating_ip_field')} - - - { - const targetIp = floatingIpsList.find( - (ip) => ip.id === event.target.value, - ); - store.set.publicIp(targetIp); - }} - inline - > + {isFloatingIpsPending ? ( +
+ +
+ ) : ( + {tCreate('octavia_load_balancer_create_floating_ip_field')} - {floatingIpsList.map((ip) => ( - - {ip.ip} - - ))} -
-
- + { + const targetIp = floatingIpsList.find( + (ip) => ip.id === event.target.value, + ); + store.set.publicIp(targetIp); + }} + inline + > + + {tCreate('octavia_load_balancer_create_floating_ip_field')} + + {floatingIpsList.map((ip) => ( + + {ip.ip} + + ))} + + + )} {store.publicIp?.id === 'create' && ( { + store.unlock(StepsEnum.PRIVATE_NETWORK); + store.uncheck(StepsEnum.PRIVATE_NETWORK); + store.open(StepsEnum.PRIVATE_NETWORK); + store.reset(StepsEnum.INSTANCE, StepsEnum.NAME); + }, + label: tCommon('common_stepper_modify_this_step'), + }} > {tCreate('octavia_load_balancer_create_private_network_intro')} - - - {tCreate('octavia_load_balancer_create_private_network_field')} - - - { - const targetNetwork = (privateNetworks || []).find( - (ip) => ip.id === event.target.value, - ); - store.set.privateNetwork(targetNetwork); - }} - inline - > + {isPrivateNetworksPending ? ( +
+ +
+ ) : ( + {tCreate('octavia_load_balancer_create_private_network_field')} - {privateNetworksList.map((network) => ( - - {network.name} - - ))} -
-
- - - {tCreate( - 'octavia_load_balancer_create_private_network_field_subnet', - )} - - - { - const targetSubnet = subnetsList.find( - (sub) => sub.id === event.target.value, - ); - store.set.subnet(targetSubnet); - }} - inline - > - { + const targetNetwork = (privateNetworks || []).find( + (ip) => ip.id === event.target.value, + ); + store.set.privateNetwork(targetNetwork); + }} + inline > - {tCreate( - 'octavia_load_balancer_create_private_network_field_subnet', + + {tCreate( + 'octavia_load_balancer_create_private_network_field', + )} + + {privateNetworksList.map((network) => ( + + {network.name} + + ))} + + + )} + {isSubnetsPending ? ( +
+ +
+ ) : ( + <> + + + {tCreate( + 'octavia_load_balancer_create_private_network_field_subnet', + )} + + + { + const targetSubnet = subnetsList.find( + (sub) => sub.id === event.target.value, + ); + store.set.subnet(targetSubnet); + }} + inline + > + + {tCreate( + 'octavia_load_balancer_create_private_network_field_subnet', + )} + + {subnetsList.map((subnet) => ( + + {subnet.cidr} + + ))} + + + {subnetsList.length === 0 && + store.publicIp?.type !== FLOATING_IP_TYPE.NO_IP && ( + +
+

+ + + +

+
+
)} - - {subnetsList.map((subnet) => ( - - {subnet.cidr} - - ))} - - + + )} + {isSubnetGatewaysPending ? ( +
+ +
+ ) : ( + <> + {store.subnet && + store.gateways.length !== 0 && + store.publicIp.type !== FLOATING_IP_TYPE.NO_IP && ( + +
+

+ + {tCreate( + 'octavia_load_balancer_create_private_network_no_gateway_text', + )} + +

+

+ + {tCreate( + 'octavia_load_balancer_create_private_network_no_gateway_text_price', + )} + {getFormattedHourlyCatalogPrice( + catalog.addons.filter((addon) => + addon.planCode.match(AGORA_GATEWAY_REGEX), + )[0].pricings[0].price, + )} + +

+
+
+ )} + + )}
{ + store.unlock(StepsEnum.INSTANCE); + store.uncheck(StepsEnum.INSTANCE); + store.open(StepsEnum.INSTANCE); + store.reset(StepsEnum.NAME); + }, + label: tCommon('common_stepper_modify_this_step'), + }} > {(_t) => ( diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/SizeInput.component.tsx b/packages/manager/apps/pci-load-balancer/src/pages/create/SizeInput.component.tsx index 84321c36b757..3db005033789 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/SizeInput.component.tsx +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/SizeInput.component.tsx @@ -1,4 +1,3 @@ -import { useCatalog } from '@ovh-ux/manager-pci-common'; import { TilesInputComponent, useCatalogPrice, @@ -7,15 +6,13 @@ import { OsdsText } from '@ovhcloud/ods-components/react'; import { useTranslation } from 'react-i18next'; import { ODS_TEXT_LEVEL, ODS_TEXT_SIZE } from '@ovhcloud/ods-components'; import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming'; -import { TPlan } from '@/pages/create/store'; - -const SIZE_FLAVOUR_REGEX = /octavia-loadbalancer.loadbalancer-([sml]).hour.consumption/; +import { TAddon } from '@/pages/create/store'; const LabelComponent = ({ item, isSelected, }: Readonly<{ - item: TPlan; + item: TAddon; isSelected: boolean; }>) => { const { t: tCreate } = useTranslation('create'); @@ -61,30 +58,17 @@ const LabelComponent = ({ }; export default function SizeInputComponent({ + addons, value = null, onInput, -}: Readonly<{ value?: TPlan; onInput: (item: TPlan) => void }>): JSX.Element { - const { data: catalog, isPending: isCatalogPending } = useCatalog(); - - const plans = (catalog?.addons - ? catalog.addons.reduce((filtered: TPlan[], addon) => { - const found = addon.planCode.match(SIZE_FLAVOUR_REGEX); - if (found) { - filtered.push({ - code: found[1], - price: addon.pricings[0].price, - label: found[1].toUpperCase(), - technicalName: addon.blobs.technical.name, - }); - } - return filtered; - }, []) - : [] - ).sort((a, b) => a.price - b.price); - +}: Readonly<{ + addons: TAddon[]; + value?: TAddon; + onInput: (item: TAddon) => void; +}>): JSX.Element { return ( - - items={plans} + + items={addons} value={value} onInput={onInput} label={(item) => ( diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts index a27e9140e631..f91db8341984 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts @@ -1,13 +1,13 @@ import { create } from 'zustand'; import { ApiError } from '@ovh-ux/manager-core-api'; -import { TRegion } from '@/api/hook/usePlans'; import { TPrivateNetwork, TSubnet } from '@/api/data/network'; import { ListenerConfiguration } from '@/components/create/InstanceTable.component'; import { TSubnetGateway } from '@/api/data/gateways'; import { TFloatingIp } from '@/api/data/floating-ips'; import { createLoadBalancer, TFlavor } from '@/api/data/load-balancer'; +import { TRegion } from '@/api/hook/useRegions'; -export type TPlan = { +export type TAddon = { code: string; price: number; label: string; @@ -31,7 +31,7 @@ export enum StepsEnum { export type TCreateStore = { projectId: string; - size: TPlan; + addon: TAddon; region: TRegion; publicIp: TFloatingIp; privateNetwork: TPrivateNetwork; @@ -42,7 +42,7 @@ export type TCreateStore = { steps: Map; set: { projectId: (val: string) => void; - size: (val: TPlan) => void; + addon: (val: TAddon) => void; region: (val: TRegion) => void; publicIp: (val: TFloatingIp) => void; privateNetwork: (val: TPrivateNetwork) => void; @@ -62,7 +62,7 @@ export type TCreateStore = { edit: (step: StepsEnum) => void; - reset: () => void; + reset: (...param: StepsEnum[]) => void; create: ( flavor: TFlavor, @@ -125,7 +125,7 @@ const initialSteps = () => export const useCreateStore = create()((set, get) => ({ projectId: '', - size: null, + addon: null, region: null, publicIp: null, privateNetwork: null, @@ -140,9 +140,9 @@ export const useCreateStore = create()((set, get) => ({ projectId: val, }); }, - size: (val: TPlan) => { + addon: (val: TAddon) => { set({ - size: val, + addon: val, }); }, region: (val: TRegion) => { @@ -249,12 +249,49 @@ export const useCreateStore = create()((set, get) => ({ default: } }, - reset() { - set(() => ({ - ...get(), - size: null, - steps: initialSteps(), - })); + reset(...steps: StepsEnum[]) { + if (!steps.length) { + set(() => ({ + ...get(), + addon: null, + region: null, + publicIp: null, + privateNetwork: null, + subnet: null, + gateways: [], + listeners: [], + name: '', + steps: initialSteps(), + })); + } else { + steps.forEach((step) => { + get().close(step); + get().unlock(step); + get().uncheck(step); + switch (step) { + case StepsEnum.SIZE: + break; + case StepsEnum.REGION: + get().set.region(null); + break; + case StepsEnum.PUBLIC_IP: + get().set.publicIp(null); + break; + case StepsEnum.PRIVATE_NETWORK: + get().set.privateNetwork(null); + get().set.subnet(null); + get().set.gateways([]); + break; + case StepsEnum.INSTANCE: + get().set.listeners([]); + break; + case StepsEnum.NAME: + get().set.name(''); + break; + default: + } + }); + } }, create: async (flavor: TFlavor, onSuccess, onError) => { try { From 1316cf3713a6b6433e84d0620c005857db3db894 Mon Sep 17 00:00:00 2001 From: Mohammed Hamdoune Date: Thu, 17 Oct 2024 17:32:56 +0200 Subject: [PATCH 09/10] feat(pci-load-balancer): tracking ref: DTCORE-2668 Signed-off-by: Mohammed Hamdoune --- .../apps/pci-load-balancer/package.json | 1 + .../pci-load-balancer/src/api/data/flavors.ts | 6 +- .../src/api/data/load-balancer.ts | 2 +- .../src/api/hook/useFlavors.ts | 10 +- .../apps/pci-load-balancer/src/constants.ts | 22 +++ .../src/hooks/useTranslatedLinkReference.ts | 36 +++++ .../src/pages/create/Create.page.tsx | 129 +++++++++++++++--- .../src/pages/create/store.ts | 55 ++------ yarn.lock | 5 + 9 files changed, 191 insertions(+), 75 deletions(-) create mode 100644 packages/manager/apps/pci-load-balancer/src/hooks/useTranslatedLinkReference.ts diff --git a/packages/manager/apps/pci-load-balancer/package.json b/packages/manager/apps/pci-load-balancer/package.json index 0922ab1919f0..dc2d5a7a90bb 100644 --- a/packages/manager/apps/pci-load-balancer/package.json +++ b/packages/manager/apps/pci-load-balancer/package.json @@ -42,6 +42,7 @@ "react-i18next": "^14.1.2", "react-router-dom": "^6.24.1", "react-use": "^17.5.0", + "uuid": "^10.0.0", "zustand": "^4.5.4" }, "devDependencies": { diff --git a/packages/manager/apps/pci-load-balancer/src/api/data/flavors.ts b/packages/manager/apps/pci-load-balancer/src/api/data/flavors.ts index 389bfa922320..8060da2c74d8 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/data/flavors.ts +++ b/packages/manager/apps/pci-load-balancer/src/api/data/flavors.ts @@ -1,17 +1,17 @@ import { v6 } from '@ovh-ux/manager-core-api'; -import { TPlan } from '@/pages/create/store'; +import { TAddon } from '@/pages/create/store'; import { TFlavor } from '@/api/data/load-balancer'; export const getFlavor = async ( projectId: string, regionName: string, - size: TPlan, + addon: TAddon, ): Promise => { const { data } = await v6.get( `/cloud/project/${projectId}/region/${regionName}/loadbalancing/flavor`, ); return data.find( - (regionalizedFlavors) => regionalizedFlavors.name === size.technicalName, + (regionalizedFlavors) => regionalizedFlavors.name === addon.technicalName, ); }; diff --git a/packages/manager/apps/pci-load-balancer/src/api/data/load-balancer.ts b/packages/manager/apps/pci-load-balancer/src/api/data/load-balancer.ts index 9cdeabbcfce6..a55a15de594d 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/data/load-balancer.ts +++ b/packages/manager/apps/pci-load-balancer/src/api/data/load-balancer.ts @@ -5,10 +5,10 @@ import { FLOATING_IP_TYPE, PROTOCOLS, } from '@/constants'; -import { TRegion } from '@/api/hook/usePlans'; import { TPrivateNetwork, TSubnet } from '@/api/data/network'; import { ListenerConfiguration } from '@/components/create/InstanceTable.component'; import { TFloatingIp } from '@/api/data/floating-ips'; +import { TRegion } from '@/api/hook/useRegions'; export enum LoadBalancerOperatingStatusEnum { ONLINE = 'online', diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/useFlavors.ts b/packages/manager/apps/pci-load-balancer/src/api/hook/useFlavors.ts index 818011b4d10b..4652ab31ab44 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/hook/useFlavors.ts +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/useFlavors.ts @@ -1,11 +1,11 @@ import { useQuery } from '@tanstack/react-query'; -import { TPlan } from '@/pages/create/store'; import { getFlavor } from '@/api/data/flavors'; +import { TAddon } from '@/pages/create/store'; export const useGetFlavor = ( projectId: string, regionName: string, - size: TPlan, + addon: TAddon, ) => useQuery({ queryKey: [ @@ -14,10 +14,10 @@ export const useGetFlavor = ( 'region', regionName, 'size', - size?.code, + addon?.code, 'flavor', ], - queryFn: () => getFlavor(projectId, regionName, size), - enabled: !!projectId && !!regionName && !!size, + queryFn: () => getFlavor(projectId, regionName, addon), + enabled: !!projectId && !!regionName && !!addon, throwOnError: true, }); diff --git a/packages/manager/apps/pci-load-balancer/src/constants.ts b/packages/manager/apps/pci-load-balancer/src/constants.ts index ecb57e3ce336..2cf18fce0d5e 100644 --- a/packages/manager/apps/pci-load-balancer/src/constants.ts +++ b/packages/manager/apps/pci-load-balancer/src/constants.ts @@ -216,6 +216,28 @@ export const FLOATING_IP_CREATE_DESCRIPTION = 'FIP created by OVHCloud Control Panel (Manager) for Load Balancer'; export const AGORA_GATEWAY_REGEX = /gateway.s.hour.consumption/; +export const TRACKING_NAME = + 'pci::projects::project::octavia-loadbalancer::add'; +const TRACKING_ROOT = `PublicCloud::${TRACKING_NAME}`; +export const LOAD_BALANCER_CREATION_TRACKING = { + ROOT: TRACKING_ROOT, + GO_TO_PRODUCT_PAGE: `${TRACKING_ROOT}::goto-product-page`, + GO_TO_REGION_AVAILABILITY: `${TRACKING_ROOT}::goto-region-availability`, + CREATE_PRIVATE_NETWORK: `${TRACKING_ROOT}::create-private-network`, + GO_TO_INSTANCE_DOCUMENTATION: `${TRACKING_ROOT}::goto-documentation`, + CANCEL: `${TRACKING_ROOT}::cancel`, + SUBMIT: `${TRACKING_ROOT}::confirm`, + CONFIRM: `octavia-loadbalancer::confirm-creation`, + ERROR: `${TRACKING_ROOT}-error`, + SUCCESS: `${TRACKING_ROOT}-success`, + FINISH_STEP_1: 'loadbalancer_octavia_add_size', + FINISH_STEP_2: 'loadbalancer_octavia_add_region', + FINISH_STEP_3: 'loadbalancer_octavia_add_ip', + FINISH_STEP_4: 'loadbalancer_octavia_add_network', + FINISH_STEP_5: 'loadbalancer_octavia_add_instances', + SKIP_STEP_5: 'loadbalancer_octavia_add_instances_skip', +}; + export const RULE_TYPES = { COOKIE: 'cookie', FILE_TYPE: 'fileType', diff --git a/packages/manager/apps/pci-load-balancer/src/hooks/useTranslatedLinkReference.ts b/packages/manager/apps/pci-load-balancer/src/hooks/useTranslatedLinkReference.ts new file mode 100644 index 000000000000..d383046e7c09 --- /dev/null +++ b/packages/manager/apps/pci-load-balancer/src/hooks/useTranslatedLinkReference.ts @@ -0,0 +1,36 @@ +import { useContext, useEffect, useRef } from 'react'; +import { ShellContext } from '@ovh-ux/manager-react-shell-client'; + +export const useTranslatedLinkReference = () => { + const { tracking } = useContext(ShellContext).shell; + const ref = useRef(null); + + useEffect(() => { + if (ref.current) { + const anchors = ref.current.querySelectorAll('a'); + anchors.forEach((anchor) => { + const { trackName, trackOn, trackType, handled } = anchor.dataset; + if (!handled) { + anchor.classList.add( + 'font-bold', + 'no-underline', + 'text-blue-600', + 'hover:underline', + ); + + anchor.setAttribute('data-handled', 'true'); + if (trackOn === 'click') { + anchor.addEventListener('click', () => { + tracking.trackClick({ + name: trackName, + type: trackType || 'action', + }); + }); + } + } + }); + } + }, [ref.current]); + + return ref; +}; diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx b/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx index 2e7cdc3a753e..49dfeada5b4b 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/Create.page.tsx @@ -13,12 +13,12 @@ import { import { Headers, Notifications, - StepComponent, TilesInputComponent, useCatalogPrice, useMe, useNotifications, useProjectUrl, + StepComponent, } from '@ovh-ux/manager-react-components'; import { useHref, useNavigate, useParams } from 'react-router-dom'; import { useCatalog, useProject } from '@ovh-ux/manager-pci-common'; @@ -35,14 +35,16 @@ import { ODS_THEME_TYPOGRAPHY_SIZE, } from '@ovhcloud/ods-common-theming'; import { clsx } from 'clsx'; -import React, { useEffect, useMemo, useState } from 'react'; +import { useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { ApiError } from '@ovh-ux/manager-core-api'; +import { ShellContext } from '@ovh-ux/manager-react-shell-client'; import SizeInputComponent from '@/pages/create/SizeInput.component'; import { AGORA_FLOATING_IP_REGEX, AGORA_GATEWAY_REGEX, FLOATING_IP_TYPE, GETTING_STARTED_LINK, + LOAD_BALANCER_CREATION_TRACKING, LOAD_BALANCER_NAME_REGEX, MAX_INSTANCES_BY_LISTENER, MAX_LISTENER, @@ -61,6 +63,7 @@ import { useGetFloatingIps } from '@/api/hook/useFloatingIps'; import { useGetSubnetGateways } from '@/api/hook/useGateways'; import { useGetFlavor } from '@/api/hook/useFlavors'; import { useGetAddons } from '@/api/hook/useAddons'; +import { useTranslatedLinkReference } from '@/hooks/useTranslatedLinkReference'; type TState = { selectedContinent: string | undefined; @@ -68,6 +71,20 @@ type TState = { }; export default function CreatePage(): JSX.Element { + const { tracking } = useContext(ShellContext).shell; + const instanceTrack = useTranslatedLinkReference(); + const networkTrack = useTranslatedLinkReference(); + + const trackStep = useCallback( + (step: number) => { + tracking.trackClick({ + name: LOAD_BALANCER_CREATION_TRACKING[`FINISH_STEP_${step}`], + type: 'action', + }); + }, + [tracking], + ); + const navigate = useNavigate(); const { addSuccess, addError } = useNotifications(); @@ -177,13 +194,6 @@ export default function CreatePage(): JSX.Element { }, [subnets, store.publicIp]), ]; - const [productPageLink, regionPageLink, gettingStartedLink] = [ - PRODUCT_LINK[me?.ovhSubsidiary] || PRODUCT_LINK.DEFAULT, - REGION_AVAILABILITY_LINK[me?.ovhSubsidiary] || - REGION_AVAILABILITY_LINK.DEFAULT, - GETTING_STARTED_LINK[me?.ovhSubsidiary] || GETTING_STARTED_LINK.DEFAULT, - ]; - useEffect(() => { store.reset(); store.set.projectId(projectId); @@ -211,11 +221,41 @@ export default function CreatePage(): JSX.Element { store.set.gateways(subnetGateways || []); }, [subnetGateways]); - // TODO gateways messages + useEffect(() => { + if (store.region) { + const date = new Date(); + const maxRandomNumber = 9999; + + store.set.name( + `LB_${store.addon.code.toUpperCase()}_${ + store.region.name + }-${date.getDate()}${date.getMonth() + 1}-${(Math.floor( + Math.random() * maxRandomNumber, + ) * + new Date().getMilliseconds()) % + maxRandomNumber}`, + ); + } + }, [store.region]); + const create = async () => { + tracking.trackClick({ + name: LOAD_BALANCER_CREATION_TRACKING.SUBMIT, + type: 'action', + }); + + tracking.trackClick({ + name: `${LOAD_BALANCER_CREATION_TRACKING.CONFIRM}::${store.addon.code}::${store.region.name}`, + type: 'action', + }); + await store.create( flavor, () => { + tracking.trackPage({ + name: LOAD_BALANCER_CREATION_TRACKING.SUCCESS, + type: 'navigation', + }); addSuccess( {(_t) => _t('octavia_load_balancer_create_banner')} @@ -225,7 +265,10 @@ export default function CreatePage(): JSX.Element { navigate('..'); }, (error: ApiError) => { - console.log('error', error); + tracking.trackPage({ + name: LOAD_BALANCER_CREATION_TRACKING.ERROR, + type: 'navigation', + }); addError( {(_t) => ( @@ -249,6 +292,15 @@ export default function CreatePage(): JSX.Element { ); }; + const cancel = () => { + tracking.trackClick({ + name: LOAD_BALANCER_CREATION_TRACKING.CANCEL, + type: 'action', + }); + store.reset(); + navigate('..'); + }; + return ( <> { + trackStep(1); + store.check(StepsEnum.SIZE); store.lock(StepsEnum.SIZE); @@ -321,7 +375,7 @@ export default function CreatePage(): JSX.Element { > {tCreate('octavia_load_balancer_create_size_intro')}{' '} {tCreate('octavia_load_balancer_create_size_intro_link')} @@ -347,6 +401,8 @@ export default function CreatePage(): JSX.Element { order={2} next={{ action: () => { + trackStep(2); + store.check(StepsEnum.REGION); store.lock(StepsEnum.REGION); @@ -378,7 +434,10 @@ export default function CreatePage(): JSX.Element { > {tCreate('octavia_load_balancer_create_region_intro')}{' '} {tCreate('octavia_load_balancer_create_region_link')} @@ -442,6 +501,8 @@ export default function CreatePage(): JSX.Element { order={3} next={{ action: () => { + trackStep(3); + store.check(StepsEnum.PUBLIC_IP); store.lock(StepsEnum.PUBLIC_IP); @@ -497,6 +558,7 @@ export default function CreatePage(): JSX.Element { store.set.publicIp(targetIp); }} inline + {...(floatingIpsList.length === 0 ? { disabled: true } : {})} > { + trackStep(4); + store.check(StepsEnum.PRIVATE_NETWORK); store.lock(StepsEnum.PRIVATE_NETWORK); @@ -621,9 +685,6 @@ export default function CreatePage(): JSX.Element { > {tCreate('octavia_load_balancer_create_private_network_field')} - { - // TODO disable selects if no data - } { + trackStep(5); + store.check(StepsEnum.INSTANCE); store.lock(StepsEnum.INSTANCE); @@ -805,6 +869,22 @@ export default function CreatePage(): JSX.Element { }, label: tCommon('common_stepper_modify_this_step'), }} + skip={{ + action: () => { + tracking.trackClick({ + name: LOAD_BALANCER_CREATION_TRACKING.SKIP_STEP_5, + type: 'action', + }); + store.set.listeners([]); + + store.check(StepsEnum.INSTANCE); + store.lock(StepsEnum.INSTANCE); + + store.open(StepsEnum.NAME); + }, + label: tCommon('common_stepper_skip_this_step'), + hint: `${tCommon('common_stepper_optional_label')}`, + }} > {(_t) => ( @@ -815,10 +895,14 @@ export default function CreatePage(): JSX.Element { className="mb-4" > @@ -948,6 +1032,7 @@ export default function CreatePage(): JSX.Element { inline variant={ODS_BUTTON_VARIANT.ghost} color={ODS_THEME_COLOR_INTENT.info} + onClick={cancel} > {tCommon('common_cancel')} diff --git a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts index f91db8341984..eacd0db443ce 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts +++ b/packages/manager/apps/pci-load-balancer/src/pages/create/store.ts @@ -60,8 +60,6 @@ export type TCreateStore = { check: (step: StepsEnum) => void; uncheck: (step: StepsEnum) => void; - edit: (step: StepsEnum) => void; - reset: (...param: StepsEnum[]) => void; create: ( @@ -71,8 +69,8 @@ export type TCreateStore = { ) => Promise; }; -const initialSteps = () => - new Map([ +const initialSteps = () => { + const entries: [[StepsEnum, TStep]] = [ [ StepsEnum.SIZE, { @@ -81,47 +79,24 @@ const initialSteps = () => isChecked: false, }, ], - [ + ...[ StepsEnum.REGION, - { - isOpen: false, - isLocked: false, - isChecked: false, - }, - ], - [ StepsEnum.PUBLIC_IP, - { - isOpen: false, - isLocked: false, - isChecked: false, - }, - ], - [ StepsEnum.PRIVATE_NETWORK, - { - isOpen: false, - isLocked: false, - isChecked: false, - }, - ], - [ StepsEnum.INSTANCE, - { - isOpen: false, - isLocked: false, - isChecked: false, - }, - ], - [ StepsEnum.NAME, + ].map((step: StepsEnum) => [ + step, { isOpen: false, isLocked: false, isChecked: false, - }, - ], - ]); + } as TStep, + ]), + ] as [[StepsEnum, TStep]]; + + return new Map(entries); +}; export const useCreateStore = create()((set, get) => ({ projectId: '', @@ -241,14 +216,6 @@ export const useCreateStore = create()((set, get) => ({ }; }); }, - edit: (id: StepsEnum) => { - switch (id) { - case StepsEnum.SIZE: - break; - - default: - } - }, reset(...steps: StepsEnum[]) { if (!steps.length) { set(() => ({ diff --git a/yarn.lock b/yarn.lock index 4c210de42144..4e76a371e4f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28056,6 +28056,11 @@ uuid@9.0.1, uuid@^9.0.0, uuid@^9.0.1: resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== +uuid@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" From 1c2ef45c7e2a21d13ec69c36606cbbb666873bc9 Mon Sep 17 00:00:00 2001 From: Mohammed Hamdoune Date: Thu, 17 Oct 2024 17:51:59 +0200 Subject: [PATCH 10/10] feat(manager-react-components): add skip to stepcomponent ref: DTCORE-2668 Signed-off-by: Mohammed Hamdoune --- .../container/step/Step.component.tsx | 202 ++++++++++-------- 1 file changed, 117 insertions(+), 85 deletions(-) diff --git a/packages/manager-react-components/src/components/container/step/Step.component.tsx b/packages/manager-react-components/src/components/container/step/Step.component.tsx index 6ba8a5feb9da..00d32b72b0ec 100644 --- a/packages/manager-react-components/src/components/container/step/Step.component.tsx +++ b/packages/manager-react-components/src/components/container/step/Step.component.tsx @@ -14,6 +14,7 @@ import { import { v4 as uuidV4 } from 'uuid'; import { ODS_BUTTON_SIZE, + ODS_BUTTON_VARIANT, ODS_ICON_NAME, ODS_ICON_SIZE, ODS_SPINNER_SIZE, @@ -38,6 +39,12 @@ export type TStepProps = { label: string | JSX.Element; isDisabled?: boolean; }; + skip?: { + action: (id: string) => void; + label: string | JSX.Element; + isDisabled?: boolean; + hint?: string; + }; children?: JSX.Element | JSX.Element[]; }; @@ -52,105 +59,130 @@ export const StepComponent = ({ children, next, edit, -}: TStepProps): JSX.Element => { - return ( -
-
- {isChecked ? ( - - ) : ( + skip, +}: TStepProps): JSX.Element => ( +
+
+ {isChecked ? ( + + ) : ( +
+ + {order} + +
+ )} +
+
+
+
- + {skip?.hint &&
{skip.hint}
} +
+ {edit?.action && isLocked && ( +
+ { + if (!edit.isDisabled) { + edit.action(id); + } + }} > - {order} - + {edit.label} +
)}
-
-
+ {isOpen && ( + <> + {subtitle &&
{subtitle}
}
- {title} + } + > + {children} +
- {edit?.action && isLocked && ( -
- { - if (!edit.isDisabled) { - edit.action(id); - } - }} - > - {edit.label} - -
- )} -
- {isOpen && ( - <> - {subtitle &&
{subtitle}
} -
+ {next?.action && ( +
+ { + next.action(id); + }} + className="w-fit" + {...(next.isDisabled ? { disabled: true } : {})} + > + {next.label} + +
+ )} + {skip?.action && ( +
+ { + skip.action(id); + }} + className="w-fit" + {...(skip.isDisabled ? { disabled: true } : {})} + > + {skip.label} + +
)} - > - } - > - {children} -
- {next?.action && !isLocked && ( -
- { - next.action(id); - }} - className="w-fit" - {...(next.isDisabled ? { disabled: true } : {})} - > - {next.label} - -
- )} - - )} -
-
- ); -}; + )} + + )} +
+
+); export default StepComponent;