From 76c8c1c796961deab39a808119a15e6fb864078e Mon Sep 17 00:00:00 2001 From: Chris Fradet Date: Tue, 5 Nov 2024 09:22:26 +0100 Subject: [PATCH 01/17] feat(pci-ai-endpoints): create AI Endpoints react app ref:AIS-859 Signed-off-by: Chris Fradet --- .../manager/apps/pci-ai-endpoints/README.md | 3 + .../manager/apps/pci-ai-endpoints/cucumber.js | 20 +++ .../e2e/features/error.feature | 12 ++ .../e2e/features/onboarding.feature | 7 ++ .../e2e/step-definitions/error.step.ts | 53 ++++++++ .../e2e/step-definitions/onboarding.step.ts | 32 +++++ .../pci-ai-endpoints/e2e/utils/constants.ts | 7 ++ .../apps/pci-ai-endpoints/e2e/utils/index.tsx | 2 + .../pci-ai-endpoints/e2e/utils/network.ts | 28 +++++ .../manager/apps/pci-ai-endpoints/index.html | 22 ++++ .../mocks/example/example-data.json | 11 ++ .../pci-ai-endpoints/mocks/example/example.ts | 30 +++++ .../apps/pci-ai-endpoints/mocks/index.ts | 1 + .../apps/pci-ai-endpoints/package.json | 61 +++++++++ .../pci-ai-endpoints/playwright.config.ts | 20 +++ .../apps/pci-ai-endpoints/postcss.config.js | 6 + .../dashboard/Messages_fr_FR.json | 7 ++ .../translations/listing/Messages_fr_FR.json | 4 + .../onboarding/Messages_fr_FR.json | 13 ++ .../pci-ai-endpoints/Messages_fr_FR.json | 6 + .../error/Messages_fr_FR.json | 8 ++ .../manager/apps/pci-ai-endpoints/src/App.tsx | 35 ++++++ .../src/assets/error-banner-oops.png | Bin 0 -> 60234 bytes .../src/components/Breadcrumb/Breadcrumb.tsx | 32 +++++ .../src/components/Error/Error.scss | 18 +++ .../src/components/Error/Error.tsx | 52 ++++++++ .../src/components/Loading/Loading.tsx | 12 ++ .../src/data/api/pci-ai-endpoints.ts | 17 +++ .../src/hooks/breadcrumb/useBreadcrumb.tsx | 89 +++++++++++++ .../src/hooks/guide/useGuideUtils.tsx | 100 +++++++++++++++ .../apps/pci-ai-endpoints/src/index.scss | 1 + .../apps/pci-ai-endpoints/src/index.tsx | 51 ++++++++ .../apps/pci-ai-endpoints/src/pages/404.tsx | 7 ++ .../dashboard/general-informations/index.tsx | 7 ++ .../src/pages/dashboard/index.tsx | 90 ++++++++++++++ .../src/pages/dashboard/tab2/index.tsx | 7 ++ .../apps/pci-ai-endpoints/src/pages/index.tsx | 13 ++ .../pci-ai-endpoints/src/pages/layout.tsx | 31 +++++ .../src/pages/listing/index.tsx | 117 ++++++++++++++++++ .../src/pages/onboarding/index.scss | 10 ++ .../src/pages/onboarding/index.tsx | 66 ++++++++++ .../src/pages/onboarding/onboarding-img.png | Bin 0 -> 8250 bytes .../src/pci-ai-endpoints.config.ts | 8 ++ .../src/routes/routes.constant.ts | 7 ++ .../pci-ai-endpoints/src/routes/routes.tsx | 82 ++++++++++++ .../pci-ai-endpoints/src/tracking.constant.ts | 20 +++ .../apps/pci-ai-endpoints/src/vite-hmr.ts | 5 + .../apps/pci-ai-endpoints/tailwind.config.js | 14 +++ .../apps/pci-ai-endpoints/tsconfig.json | 27 ++++ .../apps/pci-ai-endpoints/vite.config.mjs | 8 ++ yarn.lock | 3 + 51 files changed, 1282 insertions(+) create mode 100644 packages/manager/apps/pci-ai-endpoints/README.md create mode 100644 packages/manager/apps/pci-ai-endpoints/cucumber.js create mode 100644 packages/manager/apps/pci-ai-endpoints/e2e/features/error.feature create mode 100644 packages/manager/apps/pci-ai-endpoints/e2e/features/onboarding.feature create mode 100644 packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/error.step.ts create mode 100644 packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/onboarding.step.ts create mode 100644 packages/manager/apps/pci-ai-endpoints/e2e/utils/constants.ts create mode 100644 packages/manager/apps/pci-ai-endpoints/e2e/utils/index.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/e2e/utils/network.ts create mode 100644 packages/manager/apps/pci-ai-endpoints/index.html create mode 100644 packages/manager/apps/pci-ai-endpoints/mocks/example/example-data.json create mode 100644 packages/manager/apps/pci-ai-endpoints/mocks/example/example.ts create mode 100644 packages/manager/apps/pci-ai-endpoints/mocks/index.ts create mode 100644 packages/manager/apps/pci-ai-endpoints/package.json create mode 100644 packages/manager/apps/pci-ai-endpoints/playwright.config.ts create mode 100644 packages/manager/apps/pci-ai-endpoints/postcss.config.js create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/dashboard/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/listing/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-ai-endpoints/src/App.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/assets/error-banner-oops.png create mode 100644 packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/components/Error/Error.scss create mode 100644 packages/manager/apps/pci-ai-endpoints/src/components/Error/Error.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/components/Loading/Loading.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/data/api/pci-ai-endpoints.ts create mode 100644 packages/manager/apps/pci-ai-endpoints/src/hooks/breadcrumb/useBreadcrumb.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/index.scss create mode 100644 packages/manager/apps/pci-ai-endpoints/src/index.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/pages/404.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/general-informations/index.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/index.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/tab2/index.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/pages/index.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/pages/layout.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/pages/listing/index.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.scss create mode 100644 packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/onboarding-img.png create mode 100644 packages/manager/apps/pci-ai-endpoints/src/pci-ai-endpoints.config.ts create mode 100644 packages/manager/apps/pci-ai-endpoints/src/routes/routes.constant.ts create mode 100644 packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/tracking.constant.ts create mode 100644 packages/manager/apps/pci-ai-endpoints/src/vite-hmr.ts create mode 100644 packages/manager/apps/pci-ai-endpoints/tailwind.config.js create mode 100644 packages/manager/apps/pci-ai-endpoints/tsconfig.json create mode 100644 packages/manager/apps/pci-ai-endpoints/vite.config.mjs diff --git a/packages/manager/apps/pci-ai-endpoints/README.md b/packages/manager/apps/pci-ai-endpoints/README.md new file mode 100644 index 000000000000..83a4913691fd --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/README.md @@ -0,0 +1,3 @@ +# @ovh-ux/manager-pci-ai-endpoints-app + +> Manage AI Endpoints (usage and tokens) through OVHcloud control panel diff --git a/packages/manager/apps/pci-ai-endpoints/cucumber.js b/packages/manager/apps/pci-ai-endpoints/cucumber.js new file mode 100644 index 000000000000..8e6abbfbca8f --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/cucumber.js @@ -0,0 +1,20 @@ +const isCI = process.env.CI; + +module.exports = { + default: { + paths: ['e2e/features/**/*.feature'], + require: [ + '../../../../playwright-helpers/bdd-setup.ts', + 'e2e/**/*.step.ts', + ], + requireModule: ['ts-node/register'], + format: [ + 'summary', + isCI ? 'progress' : 'progress-bar', + !isCI && ['html', 'e2e/reports/cucumber-results-report.html'], + !isCI && ['usage-json', 'e2e/reports/cucumber-usage-report.json'], + ].filter(Boolean), + formatOptions: { snippetInterface: 'async-await' }, + retry: 1, + }, +}; diff --git a/packages/manager/apps/pci-ai-endpoints/e2e/features/error.feature b/packages/manager/apps/pci-ai-endpoints/e2e/features/error.feature new file mode 100644 index 000000000000..e20e56f5f592 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/e2e/features/error.feature @@ -0,0 +1,12 @@ +Feature: Error + + Scenario Outline: Display an error if request fails + Given The service to fetch the data is + When User navigates to Home page + Then User "" the list of data + Then User sees error + + Examples: + | apiOk | sees | anyError | + | OK | sees | no | + | KO | doesn't see | an | diff --git a/packages/manager/apps/pci-ai-endpoints/e2e/features/onboarding.feature b/packages/manager/apps/pci-ai-endpoints/e2e/features/onboarding.feature new file mode 100644 index 000000000000..1f3e77078742 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/e2e/features/onboarding.feature @@ -0,0 +1,7 @@ +Feature: Onboarding page + + Scenario: User wants to find informations related to pci-ai-endpoints + Given User has 0 elements in the Listing page + When User navigates to Listing page + Then User gets redirected to Onboarding page + Then User sees 3 guides diff --git a/packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/error.step.ts b/packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/error.step.ts new file mode 100644 index 000000000000..e6a5279653c3 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/error.step.ts @@ -0,0 +1,53 @@ +import { Given, When, Then } from '@cucumber/cucumber'; +import { expect } from '@playwright/test'; +import { ICustomWorld } from '../../../../../../playwright-helpers'; +import { ConfigParams, getUrl, setupNetwork } from '../utils'; +import { title } from '../../public/translations/listing/Messages_fr_FR.json'; +import { + manager_error_page_title, + manager_error_page_action_home_label, + manager_error_page_action_reload_label, +} from '../../public/translations/pci-ai-endpoints/error/Messages_fr_FR.json'; + +Given('The service to fetch the data is {word}', function( + this: ICustomWorld, + apiState: 'OK' | 'KO', +) { + this.handlersConfig.isKo = apiState === 'KO'; +}); + +When('User navigates to Home page', async function( + this: ICustomWorld, +) { + await setupNetwork(this); + await this.page.goto(this.testContext.initialUrl || getUrl('root'), { + waitUntil: 'load', + }); +}); + +Then('User {string} the list of data', async function( + this: ICustomWorld, + see: 'sees' | "doesn't see", +) { + if (see === 'sees') { + const titleElement = await this.page.getByText(title); + await expect(titleElement).toBeVisible(); + } +}); + +Then('User sees {word} error', async function( + this: ICustomWorld, + anyError: 'an' | 'no', +) { + if (anyError === 'an') { + await expect(this.page.getByText(manager_error_page_title)).toBeVisible(); + + await expect( + this.page.getByText(manager_error_page_action_home_label), + ).toBeVisible(); + + await expect( + this.page.getByText(manager_error_page_action_reload_label), + ).toBeVisible(); + } +}); diff --git a/packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/onboarding.step.ts b/packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/onboarding.step.ts new file mode 100644 index 000000000000..67b20b6e52c8 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/onboarding.step.ts @@ -0,0 +1,32 @@ +import { Given, When, Then } from '@cucumber/cucumber'; +import { expect } from '@playwright/test'; +import { ICustomWorld } from '../../../../../../playwright-helpers'; +import { ConfigParams, getUrl, setupNetwork } from '../utils'; + +Given('User has {int} elements in the Listing page', function( + this: ICustomWorld, + nb: number, +) { + this.handlersConfig.nb = nb; +}); + +When('User navigates to Listing page', async function( + this: ICustomWorld, +) { + await setupNetwork(this); + await this.page.goto(getUrl('listing'), { waitUntil: 'load' }); +}); + +Then('User gets redirected to Onboarding page', async function( + this: ICustomWorld, +) { + await expect(this.page).toHaveURL(getUrl('onboarding')); +}); + +Then('User sees {int} guides', async function( + this: ICustomWorld, + nbGuides: number, +) { + const guides = await this.page.locator('osds-tile'); + await expect(guides).toHaveCount(nbGuides); +}); diff --git a/packages/manager/apps/pci-ai-endpoints/e2e/utils/constants.ts b/packages/manager/apps/pci-ai-endpoints/e2e/utils/constants.ts new file mode 100644 index 000000000000..866e6dadabe9 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/e2e/utils/constants.ts @@ -0,0 +1,7 @@ +import { urls } from '../../src/routes/routes.constant'; + +export const appUrl = 'http://localhost:9001/app'; + +export type AppRoute = keyof typeof urls; + +export const getUrl = (route: AppRoute) => `${appUrl}/#${urls[route]}`; diff --git a/packages/manager/apps/pci-ai-endpoints/e2e/utils/index.tsx b/packages/manager/apps/pci-ai-endpoints/e2e/utils/index.tsx new file mode 100644 index 000000000000..24a453c58aa9 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/e2e/utils/index.tsx @@ -0,0 +1,2 @@ +export * from './network'; +export * from './constants'; diff --git a/packages/manager/apps/pci-ai-endpoints/e2e/utils/network.ts b/packages/manager/apps/pci-ai-endpoints/e2e/utils/network.ts new file mode 100644 index 000000000000..46d894b9a08e --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/e2e/utils/network.ts @@ -0,0 +1,28 @@ +import { BrowserContext } from '@playwright/test'; +import { + ICustomWorld, + toPlaywrightMockHandler, + Handler, +} from '../../../../../../playwright-helpers'; +import { + GetAuthenticationMocks, + getAuthenticationMocks, +} from '../../../../../../playwright-helpers/mocks/auth'; +import { getExampleMocks, GetExampleMocksParams } from '../../mocks'; + +export type ConfigParams = GetAuthenticationMocks & GetExampleMocksParams; + +export const getConfig = (params: ConfigParams): Handler[] => + [getAuthenticationMocks, getExampleMocks].flatMap((getMocks) => + getMocks(params), + ); + +export const setupNetwork = async (world: ICustomWorld) => + Promise.all( + getConfig({ + ...((world?.handlersConfig as ConfigParams) || ({} as ConfigParams)), + isAuthMocked: true, + }) + .reverse() + .map(toPlaywrightMockHandler(world.context as BrowserContext)), + ); diff --git a/packages/manager/apps/pci-ai-endpoints/index.html b/packages/manager/apps/pci-ai-endpoints/index.html new file mode 100644 index 000000000000..ec4e92d05007 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/index.html @@ -0,0 +1,22 @@ + + + + + + + + OVHcloud + + + + + +
+ + + diff --git a/packages/manager/apps/pci-ai-endpoints/mocks/example/example-data.json b/packages/manager/apps/pci-ai-endpoints/mocks/example/example-data.json new file mode 100644 index 000000000000..16b09c4e47fd --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/mocks/example/example-data.json @@ -0,0 +1,11 @@ +[ + { + "id": 20374 + }, + { + "id": 20375 + }, + { + "id": 20379 + } +] diff --git a/packages/manager/apps/pci-ai-endpoints/mocks/example/example.ts b/packages/manager/apps/pci-ai-endpoints/mocks/example/example.ts new file mode 100644 index 000000000000..77c033e7b392 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/mocks/example/example.ts @@ -0,0 +1,30 @@ +import { Handler } from '../../../../../../playwright-helpers'; +import exampleList from './example-data.json'; + +export type GetExampleMocksParams = { isKo?: boolean; nb?: number }; + +export const getExampleMocks = ({ + isKo, + nb = Number.POSITIVE_INFINITY, +}: GetExampleMocksParams): Handler[] => [ + { + url: '*', + response: isKo + ? { + message: 'Example error', + } + : exampleList.slice(0, nb), + status: isKo ? 500 : 200, + api: 'v6', + }, + { + url: '*', + response: isKo + ? { + message: 'Example error', + } + : exampleList.slice(0, nb), + status: isKo ? 500 : 200, + api: 'v2', + }, +]; diff --git a/packages/manager/apps/pci-ai-endpoints/mocks/index.ts b/packages/manager/apps/pci-ai-endpoints/mocks/index.ts new file mode 100644 index 000000000000..4356d0ac05ac --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/mocks/index.ts @@ -0,0 +1 @@ +export * from './example/example'; diff --git a/packages/manager/apps/pci-ai-endpoints/package.json b/packages/manager/apps/pci-ai-endpoints/package.json new file mode 100644 index 000000000000..250b6741576f --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/package.json @@ -0,0 +1,61 @@ +{ + "name": "@ovh-ux/manager-pci-ai-endpoints-app", + "version": "0.0.0", + "private": true, + "description": "Manage AI Endpoints (usage and tokens) through OVHcloud control panel", + "repository": { + "type": "git", + "url": "git+https://github.com/ovh/manager.git", + "directory": "packages/manager/apps/pci-ai-endpoints" + }, + "license": "BSD-3-Clause", + "author": "OVH SAS", + "scripts": { + "build": "tsc && vite build", + "dev": "tsc && vite", + "start": "lerna exec --stream --scope='@ovh-ux/manager-pci-ai-endpoints-app' --include-dependencies -- npm run build --if-present", + "start:dev": "lerna exec --stream --scope='@ovh-ux/manager-pci-ai-endpoints-app' --include-dependencies -- npm run dev --if-present", + "start:watch": "lerna exec --stream --parallel --scope='@ovh-ux/manager-pci-ai-endpoints-app' --include-dependencies -- npm run dev:watch --if-present", + "test:e2e": "tsc && node ../../../../scripts/run-playwright-bdd.js", + "test:e2e:ci": "tsc && node ../../../../scripts/run-playwright-bdd.js --ci" + }, + "dependencies": { + "@ovh-ux/manager-config": "^7.5.3-alpha.0", + "@ovh-ux/manager-core-api": "^0.9.0-alpha.0", + "@ovh-ux/manager-core-utils": "*", + "@ovh-ux/manager-pci-common": "^0.8.0-alpha.3", + "@ovh-ux/manager-react-components": "^1.41.0-alpha.2", + "@ovh-ux/manager-react-core-application": "^0.10.8-alpha.0", + "@ovh-ux/manager-react-shell-client": "^0.8.0-alpha.0", + "@ovh-ux/manager-tailwind-config": "*", + "@ovh-ux/request-tagger": "*", + "@ovh-ux/shell": "^3.11.0-alpha.0", + "@ovhcloud/ods-common-core": "17.2.1", + "@ovhcloud/ods-common-theming": "17.2.1", + "@ovhcloud/ods-components": "17.2.1", + "@ovhcloud/ods-theme-blue-jeans": "17.2.1", + "@tanstack/react-query": "^5.51.21", + "@tanstack/react-query-devtools": "^5.51.21", + "axios": "^1.1.2", + "clsx": "^1.2.1", + "i18next": "^23.8.2", + "i18next-http-backend": "^2.4.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-i18next": "^14.0.5", + "react-router-dom": "^6.3.0", + "tailwindcss": "^3.4.4" + }, + "devDependencies": { + "@cucumber/cucumber": "^10.3.1", + "@ovh-ux/manager-vite-config": "*", + "@playwright/test": "^1.41.2", + "typescript": "^5.1.6", + "vite": "^5.2.13" + }, + "regions": [ + "CA", + "EU", + "US" + ] +} diff --git a/packages/manager/apps/pci-ai-endpoints/playwright.config.ts b/packages/manager/apps/pci-ai-endpoints/playwright.config.ts new file mode 100644 index 000000000000..feb249bcbe3f --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/playwright.config.ts @@ -0,0 +1,20 @@ +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + workers: 3, + fullyParallel: false, + timeout: 30 * 1000, + reporter: [['html', { open: 'on-failure' }]], + expect: { + timeout: 20000, + }, + use: { + // Collect trace when retrying the failed test. + trace: 'retain-on-failure', + }, + testMatch: '**/*.e2e.ts', + webServer: { + command: 'yarn run dev', + url: 'http://localhost:9000/', + }, +}); diff --git a/packages/manager/apps/pci-ai-endpoints/postcss.config.js b/packages/manager/apps/pci-ai-endpoints/postcss.config.js new file mode 100644 index 000000000000..12a703d900da --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/dashboard/Messages_fr_FR.json b/packages/manager/apps/pci-ai-endpoints/public/translations/dashboard/Messages_fr_FR.json new file mode 100644 index 000000000000..f42a27b3366a --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/dashboard/Messages_fr_FR.json @@ -0,0 +1,7 @@ +{ + "title": "Dashboard page", + "error_service": "No services info", + "general_informations": "Informations générales", + "tab2": "Tab 2", + "back_link": "Retour à la liste" +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/listing/Messages_fr_FR.json b/packages/manager/apps/pci-ai-endpoints/public/translations/listing/Messages_fr_FR.json new file mode 100644 index 000000000000..c882bc92cfec --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/listing/Messages_fr_FR.json @@ -0,0 +1,4 @@ +{ + "title": "Listing page", + "listing_resultats": "résultats" +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json new file mode 100644 index 000000000000..08cb4cbb9107 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json @@ -0,0 +1,13 @@ +{ + "title": "pci-ai-endpoints", + "description": "Découvrez des services de stockage managés qui s’appuient sur le système de fichiers OpenZFS. Bénéficiez en quelques clics d’espaces de stockage centralisés pour entreposer ou sauvegarder vos données et fichiers.", + "orderButtonLabel": "Commander un pci-ai-endpoints", + "moreInfoButtonLabel": "En savoir plus sur pci-ai-endpoints", + "guideCategory": "Tutoriel", + "guide1Title": "Premiers pas avec un pci-ai-endpoints", + "guide1Description": "Découvrez comment gérer un NAS-HA depuis l'espace-client OVHcloud", + "guide2Title": "Monter votre NAS via un partage NFS", + "guide2Description": "Découvrez comment monter un NAS via un partage NFS", + "guide3Title": "Monter votre NAS sur Windows Server via CIFS", + "guide3Description": "Découvrez comment monter un NAS sur Windows Server via le protocole CIFS" +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_fr_FR.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_fr_FR.json new file mode 100644 index 000000000000..b794794292e0 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_fr_FR.json @@ -0,0 +1,6 @@ +{ + "title": "Bienvenue uapp", + "crumb": "pci-ai-endpoints", + "tabs_2": "Tabs 2", + "onboarding": "Onboarding" +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_fr_FR.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_fr_FR.json new file mode 100644 index 000000000000..2c575c63588e --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_fr_FR.json @@ -0,0 +1,8 @@ +{ + "manager_error_page_title": "Oops …!", + "manager_error_page_button_cancel": "Annuler", + "manager_error_page_detail_code": "Code d'erreur : ", + "manager_error_page_action_reload_label": "Réessayer", + "manager_error_page_action_home_label": "Retour à la page d'accueil", + "manager_error_page_default": "Une erreur est survenue lors du chargement de la page." +} diff --git a/packages/manager/apps/pci-ai-endpoints/src/App.tsx b/packages/manager/apps/pci-ai-endpoints/src/App.tsx new file mode 100644 index 000000000000..9bedb39c2cae --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/App.tsx @@ -0,0 +1,35 @@ +import React, { useEffect, useContext } from 'react'; +import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; +import { odsSetup } from '@ovhcloud/ods-common-core'; +import { ShellContext } from '@ovh-ux/manager-react-shell-client'; +import { RouterProvider, createHashRouter } from 'react-router-dom'; +import { Routes } from './routes/routes'; + +odsSetup(); + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 300_000, + }, + }, +}); + +function App() { + const { shell } = useContext(ShellContext); + const router = createHashRouter(Routes); + + useEffect(() => { + shell.ux.hidePreloader(); + }, []); + + return ( + + + + + ); +} + +export default App; diff --git a/packages/manager/apps/pci-ai-endpoints/src/assets/error-banner-oops.png b/packages/manager/apps/pci-ai-endpoints/src/assets/error-banner-oops.png new file mode 100644 index 0000000000000000000000000000000000000000..413028afad19a473424e11656d3c6b0400a79fdf GIT binary patch literal 60234 zcmeFZXE@vM`!`NRjnoP?Ba~RBX6-#=6m3;$?NYS1D6xs6R#eg&wP|TfZEBNhgVqW) zYeXrrDhO4pf1mI7^ZOmgeH{OX_rv>1t~|KjSI+l!Ugvnd&htvr4GTCEBQGNr6%~_- zvB51WDuCws_Zj*N=bz@5d*Jhd8gUD*M^!ZhS)-!Tp)xVhy^W;aoMWi*u#OZXe+Q*( zz7aHytB!A0?5pxl`{8MRJyrDQvm`miiQBKik}qqo8DFu=xqb_4tC)6{6H`>FuRlD}=I#*{G zwts8?KKt9@_NI=+)*SR`eJYJlsr|teV)%D{z{dNKA$Rg;I5c#m#&=HDOv&eHZ~VhT z)S!36xD7oAAYSM44SFdUFjE@#z-&@1G3JTKSed!U2e%be!MA~Kce9TO>KSvh%=QNt z>K;i08DsT|SjB;yFu)6nRNupJl<5cl0ui%J`vpvnwStIN=@XAjGfk3|GcobkeW3qN zW{eF@>&Aq>caTu_pudj&mX`x}sNWyV0epUQzSMtKL?*Mnd9^B05aS>b)w*2v)i1qc z6W}wwIQ{bCmg$`7opTxgBWg|k`8*E~VU6KKZRwqdN7f$|hDG0gllbo{Qxf#IN9(Pe z^B1o7Cb5`%C3-{++dTaDEoGJIzoiTy;#>p%o&C>GhoW^vH1{Gq3s`1hHb8n))B@pqBZxi7Ahj)9$G_REJhx- zJ}K|4>tv6S&is!aG?-XP48vvStBqND!|N+edk54<CE5Pjqj^pf8W2`)C;YK zYObq!NE>ja|3`_8v7eK=%MMb+K!bIrVVpCmnhcRySDntduI8vt9;=7GQsmDa^KIKg zU)?^;+t7yOqswOg)jwlwug3iD(+bUb-mWDp`b+;kM*me2k&S%yeF}>v1s#A{x|!9q z);ZotWRj-&-$(U`Yf0UDXJ3xS5cn_a|BCYehW{~^|Jyr(OlGy+<>gsMc?rM?l79GmCds*d1l$W%63CnE&tl={eJ}zR54(r>agE(ggVABKsd?IW!#=Cql!Pw2n6s zctpWop8oF{uj)8u-`QqwwTEd@wyi;HH7-2~i@(~op`E8&ek|!4yM6u(k-wR$qfa*y z@m_~VlU-U3I~|e3n!h2;D#VyYo?#@O+#_&SlOAx2$!d4A?TiRrHDvDm(!-*y5wXO# zKLmksZM16_nm9NnLw&|io`bz;|d-?qp1T8+8{PKb1NwV!D^*WlH^ z&O=DPwkER%xqDt95*k6q6%Rhnic@V_IGXs-nt#}R_ERQ0^h4V9_9FS)jXaq5HzBj)v)CLvruZOe64v$+OmfnCVS0$ zBeHNe<@hJ107_>Q`7|iZN}y&~SVmBbPl(F;kJ5(%Tl$KK2i2Sr}K9s<#i$z`CUzDtibA=XLerDPwaBen< z>f(C~VDcndPqo_biaZ#p_QujQM}`Artz8#TB4}C7CRJaIEb#c-&C}V#?|xm8tcNZ- z=+a=MDKzyaTKnvzEqCZCGayGpbh7~vYE4@3#qQ-=5pgyDzBEhb@)x<%9jhUw;ZA}wM z3ew2Y!J#yMan~(k(P#a@rwN_4N!KZEb+KDDA9I;-V=7H=f->53qmv zhf>eTODwB=D099PUSCG>aLj{yz$aId9BL9= zH)90y_z5^8)e~~h@xAy$Ib4uNR?yR^$Pwm~hY@J6{rW8}#DaNSN-NDK*UynPFx2Vp zwv(M-zh&<6-RNGAo2WXfw0EyOs4Zsh-^%IfAb$jDPiPr-jSW2|UhRGh==S+AH-TlL zHhM@o6>p|L@T8akf#QX!mvH^QiNOsFBvh(Sjt;EGw1x~O(+bH0`Il}dVW|=dfw3Am z-ux>8-IiTCm4lB_FY_<@Fh;5N_BQ^o7kZ|2Z3r=!G$XKu;Y-hfOk?JtM*?A_VmoX$ zd3@-C>z3MxrU}DI!(zrVye?}QF7%O;Ho?>7XEv~}MXriA+yu~JHDzdXvIdfg-$L@h zm4nbT{idT29p5uWGKL{CH|AA(qvd7XM)EcNYrebsxS6w7u*@}&@&r%VtUR4F;a<$; zRVTaG%OQDL>{_3=qX!#|7N)^7>56AB4hI0gt}|FB-2TB1Tlu?lbNJfd$5)xSE(0bV z!?)<}>4cvqUy=+P!HSCm2H4;bLI`d9hs6#E&Z3)*zVmm-_CE3ITaz1{1GE-C7?x|O zROgB-GmQ)|RI=sx+nF8Fo={Y+vq&~5ijhAJF4m1Lr@s@_oeo9i%LR)_x063Y#T!AM zhZRG;x>qB{3Q7;218sC~1&Vi%QYQolmK4g#Ht*p1K74V(Mrn{rOj*~^P?q+Q2XH<0 z%D!D+%qNU@9V`qy4pS$Qx!TVM6Z-yQzfE0*iLA6ESWdFmF6=?FmXZh$DNTKcC2ycU zv?$K&$POWkxpvP=;%Lp&ck!d6PqN(fXM%hC{zuMLw0EPcdq|r7fh4l*Afe@S@902$ ziw5yEFeAY0Z`PgM`nwC?GY+M`yQbTG2>pT{R!zAWZ|E{K+kW~%yhvO)SP`(rzT)+A zAcT<$1uXw%9}{pJ=#0aQ9q0OWw$R22%?v@&^WISsg$ zb6D-Y%_?$8bgg_s#RQ!pc~-jVP+8H6oT32{s7}E$-qJyJsMGPxCIvePADymxFb?FN+#L}s0 zq{?x!AUD{XFV*dgGP7Swz=*eI(CA%zBWX2tLghM&o(^|>FHeow8ilIdxfUJD{G-w2+_P>ebM12 zW^VJ!Yu&xNhE7Mc9ELhCs2KFXLb}3+&)A$Brl;?Agzb~pETdbuFB($;*>q`Tf&idU z60Hr+mUy$zyoj(p)~<8CD&*FP86z2|f9time7vKg)(LHRy64$Xr?Ur#>o#38SON9B zZj8%#qx327_TWjBfFr;o*((ySTJpm9=VxbRjiMTlnE29z#E%)9SPQj>8y|{*_D#HW zGU{q%cd>hQcoDS>$yx%7K0@Ed2U!>Yp4`*hayF&^`?GOa6_MSoMgxIHcE!lof|4x{#-cRT8R4?bF7aZb-=VqkWl;=D%d zzc9Nqlq|SdF@Q>Ph}mo|`kj^qbPfV@P}>QzJ zeYIrO`bxJ*uNreTrc_Ly$UfU3b{9!Var_oKW8Ly2Su$)Fv02-D&}$T=1eK|yRfaEF ztrck}p*8|)?F3s&X^#7P^-~*4Jt&}zP6`#y+|nNNN%|?nBvA7jB8*YSS&2t4*2yIzxzYg&93qsFvkA_ z7$2z>XnnaxcZ{vQqjMXW1Z)7RM54^0eDoJkX|MD?3VCj1U6eSYW$3kxT(^p}M~0oi z{Ecm4I)H`{@exyQirAYGS^)~c1}wRaC6#7%TUzQ~7kVxz)2y2HoB>1OSl0gCGW_^B zm7Qbj+QZzJFL`0w(*$E@H^;y^7tK!A zgddt~x8K)pb+}2=g{voe*Fr0mSKsxPPwm z?+F11Vyvh}pQTe!z^MVK(|m&Od$Va+fjH8z%bE2YrQ@!T|G3pa`)wRm^Klw)f;d1W zl?q_}V&Q36Sy3230Y;z<-XekKt^KMQCjN==W*^om@%QqE_0JGjQOO_+aY=fz_sh7~~g%2zpDR^(8> zzHsd;5U;k8v34+V&@st5xkWyqfO4bnR$MLca-f2*hd{q8&1qh^Di4$#U##?E-V~!{ zf(ico(nYe3So-lo;Yf}bWujo;bFSM_y&&1^8o9`-=1KV;(j1*&so=YxonZ#(BhiB+%y~e`SNaX z;0Qg*q*!Zz*?E=L5ta++U{zpv9qoNuW^c5V8p)OKVmlW5i8lJM`fh#M#v{kFnNrR8 z_tTLjELz@zS#ve6y;V{{&NKtJg9#EFDUO?yzEL6%{L3mfU@;V>ZE(QnBh*{Hgs5@V z0`$?Mv;7JyQ{?T_70cKTQ!eCNPb>WD5r@x1F`2Ec6JlzG>*X5me@HL6p>;sbTh+;x zQsuu3`gv_n;)PdZE-|aFVplx5AS#avF|_O&u*)+*Du&pY#m{!S_sExm2=qdzKtN*A z`vsK<{+(ihyBOLdB9A-hH{7K4qIain>(~0yd(U-CIFRux* zv_THL8I=xA9+7DHG&CmUMVsBlfbTm8xbk0UMCn)83?onU$JB#yg~TCdTRd|s!B9KA zQqE^-Uoxoy4@<5YO`mTGMGPL0lT=;#2+tV8mq^cA;%c=vbhYP?QUmgpww5qny?tK- zKyy`drhJk%tMT2^I_La=165!WQUd@$8GIJ=(9L3B>7(_+>E~B{(p6`aB8eyjGm!Tf za`z?=W+pqEx&|t*%3Lv1fvKS0TCRYZaWGw-8rD<}_|_rg86bHF3@ZH9Kk0EXlyMrl z^sv4ARa)oSf@pd*Om99~&PeAomY>1xkRh<a5e z_NP3Kx|aO6+WW!r7Hk7p?L4I(LJ&|BX;Gx36sg-e{|I;$>Rj3hbFXnP)Jnhno=atg zAaw6!L}I0XnpztYxVy-;*?IDndA(ra0 z>|3{Rj?1h@kdj`#+2Rx@)Xx@oAlSLpvG>3DGS(DAKC)jvIzE$iBMrXzi`vfN4nI;6$@ zR=E-KGvtSW8dfaFJsmNX?etezGxCy>SW;|yBU|3|!_;63@(ozR$L&=Oi%+r^f|zY^ zvrx6s!UZl9F`(-&f=!kCJD@vZ<@PH7;wqz-^*Jq?+Fi=_K+f)N)1W#K=oOE!Sd38h z(Y?WX$yp^d_S;R?8vPPuu*zSR5VRvJD;0TU@@(=TN;=zr9dMO5f|iGx-%r;Jfaz9a zz7SNWV9-g#5}#9v7#HENLvBPfW=)dt8ex?U?3lcRWPFCwoZsYh!+O@*)Fq^&xWZz` zZE;q`>SxKp*&`rPN(m?wBkZ%;L+3XxjotAqu>ng&;*z6qD(BH){;=*v!Sv@6P!5>O z)qaUdq4ndJUtFqH?^CimHXqEnHz3zRg}%!cC;Bq-YmYu8Im}GCg;=lu>J_FCmW_5#AxJJ zH^@zKYq_(dQ~t=~1MIrMCOJ0ozVc@@?LGOm*b6MHp8fvcn5~+OZS9vToQm1+24kHi z0}$7>y{GPLINTzFqJz7cPUU(bW8X>X!!Hj;>m9(S%iQ(#j8X#(cAI@U3TkGinq{VX zI_a<%Gb8mjuf_OxZU+A1%|zG8YV`~7Hm!2c-WR(U@nXPFR+($YnWcB2RBmX$*qU4@ zHAB`BI2L`l-pXow{XDx?cxlcsoUp%f?ms^#*ALmY9SAx6QzLjcaiIK9m4Ih!SFV4= z(=%U77wFFDoOcagsmAq0^9xu?(-CajQ2u0Iu{WO!Lu=N?^iHUm@emN(EnGvd_vbk} zVK3_$J;~D^6b7jIq$5=NtW?wF`2moxpIQ?O8TsAz3M@@5)r!FOJrQ%5$}U7g}=bMa751g-U&=up! zT`}`M4R!^q^dm*>AD`-S~z50RjJ9-MFe<|9lry6MnjLQ*d7!bAzggH@k zDt<7jx95+A*pGCp(r&@bkJfGJr8v*=9M9k@ekgxS3Rs^7~=HLt+m9@i=@ z3V{HC4p?Ay>Bl`CGD9Kz2T9x?kk-sTh$Am3Sr8X{9uqu)Y^z{_?N`R)4H+qgI*a1A z$8Lp!Pv~0Q-k?lSu{w*`)+lO6?I>_OuonnTiqmQr;S0V9u=na@cictl^lW9uDpP^k`*ma-K|JU7-AWaog~abvFbXH|_Cm68Z}v|$%w z{Eyo>8mhI)C=J~+!5f#H_%4(rzK% zogee?^e)R`4p&59N>q?y;tWCjXsztM>E)ViHsrNOQtpz4F+Rb_nO{Se${zlX0u$hZ z%_9wJCBs%GAkaECU)X|;L&ddC2vuy^h*E$V3crP%mt`_BM$YN-Lv_nRlyCivr6{ zq9mb*Kl-cA$?kSK%Z5V<06T93l3=b49qRWG4%6s=@kV@zfDXCk{4>`MlQtZ&qM%6+ z>h=WQoa&KiKM$PzdF9#4^47=^pGS|b%xUK63|DbX5x}qd=`aM6l42g$H`I}j@e@W?Qo|aIThmM0Z z$8#W0nES*#e(2I#*xchuCd}m{aU|BKDmA{m#WGxMX1A9+=$%|pJx$UcDMC0Z~V5<-Y}g_MVLV zaWgL{GE7)DyR_B7<~bKU0p>fG3{EKwYt(n~{jhrw z1a=k)2nLgMFF1f>h$UBW)P+FvL!8b>k(;p@270+}Vhk*Q$d8%0CObb4ORLk?B!GCw z(M&BAcIW*1OMZbRR+N%lttE1X)QI6{mOC~6X_-vHnWSPSOA}{Qot9Eb${ha_)kF4t zav5KHK9+@Vl~IiW{Rd7X$w2=dmUlzkC}@kcA`CZ&{c=V_ERGM8Ef|P zF$HO7eph8aV;1i@f3zWHQJF}Si=wsS;R$ln<~vB51XN9TJWE?Yy8+g%NDQvdQ){Wd z*?0U&OpCDAf0><`@`B^X!@T?pyNx2YKi-ap1Jg7J}<*|E6?@@hyoc7%s zd9h8g$rJ3)4xv-9^j^t5o6e4>+a_Z9-&Q#^?I?DM$RdSwIGAV`968i8r9G-N z)$V8ezQg)z5q3k;?MiK&1_fz~x9_nEfVNcS({1#*f=+j^ZnX-*+zK`#&7XXm>dhZW z+H(ZqX3&D)({2*h$}7MZN4IA}kMg_}@L}f|A<^yoR=v{T2iKYp(A(+m@A+x9kSg~i zgF`kUSArUUz1O@3hBVpguNcOvs#C`LZwB0Uwe+jhj_(%wC;cTX|LXcZ@AX}|8H$~) zbZk4&V@R*AOgG*LqGR*yGL3LT%pcQJO|!8gY5n_kz$q%LCIMLVJz4NIGG?Ti(-z1S zn`^~I1)=5E)or5^;(k`HO&tPh9vLy0Ew28dM%~ls{A#!vD7bY_>3hOxgXKy<{MNf` z7zd6dOA6Mx$R>_9g2SXhNeT3b@+lp%N@l&$C{kQnxPJQVAMRF+#}ft(is1^g*?hi<8Ld+Uyh**IkK~7<5e|2rkX*1w>D$ zDO{M4m0gfECU8X%nwcZf57Q$iuT0A-6;7bwdBI`3;Z{6iYYHJz`MehX7O`(1NmsY; zemOJ#b*UO@xXRBPXng|np5QxCfKw9V&q&6j4W0KINE!Vn}vtD&-5Bj!62zjJK9Cte(JUF!QQ3HmQb;B(XDaV#N1eh z5u4i~ptDpF<_meCy3OAe1EllZnwyYQMOrKAe!+hts+m;;mUSe^+ zV3z)8igYZkqq?6xcAr)UENQJSZIF%l+WW?T(J^=ESV(Vf9UdNJ?iaoMtGJafF!k*H zNS5Q>@Rjj&zoQwo6Fl-~p4O(2{(W!9!RmwOc^=o*qtqiGO945B|7n-O-*uo;Y7~q> zAb!X|D!$Z8LKIg0$^~%_g6Xd4(v`!#hg00ThL?aq)qZx~NK_|LxJGrom;HT~ZREO< z%o}>ozs5HIi+VdgKLD-9ZBvQ5ozT#QX@+bqd4TP zU8fVvL#-N*mAJ2$R>)QDlnSS^PwjjP*O(*~UN5FoMg9?m*pnwL3Q94;Y9>!x>Wl{J z^(-l>2l#_TpX5?4>)3j5DrB%_pITt=SBG?y8RPUtE0xt*nUEg8c22_5>p_B|<`#2U zziM#MqTQKisZ-GF(k7Bx*xm$|mkx_`6>cXIjfC1Dq2R7U{i+Hct6dJBq zA|Sk}AcX;N1Y}+f7TJ`K*hz8P#2K|2;G|QFWT+Oe3Nk#J9;b3Z8Y-Wnm^6X<)R1T# zoq!J#&TQJ8IO_ z#BpHe2D}rv*-c;R^yiCB7+FLi>Eh|Wp-fXZgP#WZHJ>JUdFPuY*HS}USviqkV@;{) zvqp=1xmN4nPWBEPP?qPDAO12#6~7ymR9B*?E~*vvVwBCTLxhb#3X5sxP&CtRV*1 zQeL!p=<^$N8&DM@43u(JiM)nkJWaNo!H6w^8~XgOvhsL&7@>r3f?uU(dDCCR0C9$c zRS5x%r)hGTP|Sr0T6HJ8A=^_5dnHg-?Z*}`lh$FnToD!)_lu{;v3=yDA(fj#+mu<8 z0}C#X38Slq{dp=zEtiy9%NTWqA7i`t*{GMI{!-xJCD$QISvm}BC=+zlC|RXha9O=Q zEnFymFvm6h&266?Go^JKJcF+PYqKl7tK_1@P-m+diWm=$y~;$C=rh#kobIL37t7YR z=ote4D7lEsIwk9tn@%;DU{Zx?EY{!~+7I^i6DU2xQ%1^@$yypCIUo861!sgme?1Yl zCYJEBGpRd3=-i}zC1<9?9%pPOJ!A?nsyLVQ4R=w%Fffiz6R2Su2K#XTb29Y0h}tHOPkTzW zt`L35Q;}w`v58(w6jNPZN}1s9f8NbI1ADruaA!2WT)^O(QE@5NubVFouA_|c2OJs$ zuS?x97u6(iI8`1JWUWFC&KIU}dnFsf0(?!HnMRGpn0ay<*l>6WK+iUT?)g1y=EPuC5&)OKbnQ+ks@fPLySK zhg#0g&l}5m-pjd=CoP^bQ`H+VcsIIOb)lm~1wNoF#tOO!*lMOXVyFXraKknB!h>(+ zA;_rwB6()gA-9Ymy&Hi09?sbRh80j?X$=ePZ{ZiwAYHLtD@~+wz33QJmZ(#pBfN8& zJ?#vb592~IdIu##Jugij(JHsDV%4WnCHWE!ZUjT2gmf=8GbHiyV$RE#>*-A}Ee!2J zX|3y%0`m;GXd5|YXj52U;An&%?@6LpKFJ?TfgBy;G>^O#m>1YBHEPF97{MnBbXa&c zF6C;C5VmS&#|q*=$1+7U^NS3UcVsZ{n%bo^N510MUK+UUxHeg4v~3bqgFWG0omJNl z4Va(lo-DO(Ud#c9BGrfU>NVvXEk|4KkmU=5#Rm;BL$&jEo&ugH7Jeo>))@}Hdd{`~ zw3OY?pQd%QIad3fb2aA72XQpK{EQaCtQD(np&=?F>lq?lRGj5ZuY&sePg#k^1x&Be z2--Yk`Vp_3L)y(%12oloiaZ2UE#TRxXdz(?O6R=@J_6|l z-beH?%SCBdksv&nCz4_!Z*)QJ^bz=?4>Yt^U>g=d88<@v$(qowCC*o+FURpCdDtsq zH!znDfs)+FWj&eJHoTacnwX>sZQJ5|`c{%g6=4{?NqScN@n@fK`eysu8=~sn5njuM zfe-o0d+ZL=u2NdEvPOLT-$+$+iUUGcg>qHBOKY)ECnSDIjwQ0{(lwJn`&`LwOOs${ zU&q15#^UtTv1xT*goXCf{kW#vVUn8!_s;j`8yLuGjTObOxUH@6Z(9@SvF`Spe-d9f zamVpN2A}=Ppe4}vybnqErT&>Qh_{@99(51JlBT-$94HJ&t5X}DOEGfLe~${xts0dM zwl^~XQlIn@RtSCni?vu%g1##10%W|cA$sh>d&354(0-?v%oFkTAN2++ncT6#JCSp6CSy)^L zMEDoGMY%^AlvcW@ob{E}GTuIcuW<8Mj;KziYsr_|$@bDz1-(KX^^5h`+aeA}L^h@} z2BWHz3f#g%@3z6{t8j6t0hj5t>qw-RTK;J#M6S7=CnCJIIdp2gS#oIyqoS+tnTEgP zch}olZ3pNzTGP9--%EX8(FAPuPOHq1eYf21s#RSIX%cK`RbBlVu;NOi5*$y%MFFxc zxMf5b2LtJK=l^P){uz$$r2DM6)jJ7^!Kt4CCfI z``ART^AgNSDpwy6+OGMLH5EpOf<~sY8>|U?GTu(^Hop*cXe1V9DwwJ}ywcyDkVVyd zXR1hHe(7o}pXAM_j{e|hi?PbNsg3GXl5O(K#3Yu;=cMI6_aqM9Fut!*Ck=eU#rL`H z9gM|lGTwUu*Ozb#q^xNL|n@( zAb->s`D*J!zCDuBKh#G+E2}Zco_WGl=l2M|`1!Y7+fw1r2y^YoqzygbZnhSznZn=Ab^oG54$>EL9$+FX`Vc=!A}~UOjJy4OxRf=ky(Y^Wr~*8((f*}#d28M- z&z!eU>8bA#FWCccQJWNvul&5JdwYl8*tsNk4p0~+a@jMDEfuJCAo>h+&AUWc6;Ce( z<$spQX*tdvYcxtNKn>TPPhNO|}3SD?Y;-LRGnNYUpgY0Ku#xRjLx%#dAMpU<;G#C8Tx>-4)rK&TR0uGw+t#;?z^5eKzf@TJi z4#?;sZ$$KH5BDkl+4zjowG*|1_uIQxmYVy&q~DxL(F388?lpqnu8I^&0k%T!&QJMH zyjK?w=<|(GZq9We5}WrWAny^(U&|xW=0(YSxfWbQu{1-+!g;NNNzraMFRTso>e%F z1U-K?LTw>SNWWta7G%ZX9J~Gs#j-_IHUCKVZL9TnJqJ(#u@`giYB%F9U7xB@W6{hx z+F^&~Zy@sTr~c@3ToSnt=YM>=kV``^4*iP)bGjui&Z}VMLUVC|Az~_K73ss1E*eoM zN>U$(28{A&Zr&LvPiCH2T+K!Vxpwqh1;SF8TGEO4efdvqFXv2e$)O=lqx~(Rmz|QX zt~^ikCMajd&!3E$2xu{?k9e^}ZfCCyXN#Qo4t?k8SBg-QH zF$ANp=TM>5ijD^FVl%|R}!f&=aEN!uhH3(@q%b6 z-RTq!oS7@rZPcbq4lm|EB0R+RzLufEt;JpV^tRH2s3}94Ey{xmuK2-H%bH+b$c`1e zLH{RpT9(Mip0lvGtK${UZaLHZ=+(XTI+7(#v09+N8=qrERdJ z*(si*oeb%fnEAUPf?QCQ)s&~tJu9rC(<6WwZ^Sel9d(ect(G3Tns@4SwwralbiGX! zUpEN3VAK9>YUR!7)re9MKZ>R!kIs%lN%5XqWS@W7>l)4&=8Cn2FOcBM2X+gxWZmAI z(WaiIbocrhj?)rZR5$%^H})DexEWL4`-m(J)WZ{_{1sd=Z<@Qo;O#2>=LS$0ZX8Y~x=QC`;neFhrX$aAS zFP*EUxLk5Zl}5Qv=e56VFbzc2Gd4;1ap~|U4~%jFgs%6a%gmz{ozzv>4{_1npS<~| zEZb|w@)}2rdF@oDP9j6eOZvjP;2WtwH?6NhxbyXfUEa=&1KF=7?>y`oQ)%Ou)f4U> zi}f+dqXewf?3xs8;Te5NQ>iR)nH#D$JH;M=l$h7~QIru2E}trX5q9rP!HgyQ8za?3 zp1ib>tK5VaOL#t(RvuyJABHlA@reNpybz6PidAadXIpLEq^pdPWT6ykat}-J%oR3lSFGj&WrL}QEb4%!%A){q+?IgO!!Xo?DUu2qeh6YMNLx8Dd&t+9FR=D zb|)ZTk=ks7`wCMJZ0KE&!2G$<8jTNz#zc;KE-sUx!b>-wdr9ei(4W z6EfY+@JRVWG&B0*Q0gAIdF0VK>9ROWdep8J&;B5>7H`5iB`VJ6vB7`tnb|V6k}v{} z%VQ$)4ol@fwx5bQOmFbMe89rozpVz-`RPU3%-AHpALc$Gp|bKh)LJKo z5~hBzU7t6Ly{gv4Fd>UBK%3lZ7-PT6zi5Qs!9R*iL9~_wqv2u3MwCS5)18|x*UszQ zyznOU!<}GrI)wYycZnzIjah#8*7lx`w%K~`tHUt?tI*>*HpfF-b+^To=wyU?RH)^r z2wP=5Hr^&Ej;Y{XsDZLW{N0In`$aWuMyAUiXAK!_s#PDXKhop}taS4V>3>}G^zFYv z8KdP~>5Et@Hh&B)sM`6`@xW&Tac$lfQN6%Bny8(-xHZpGP@}UEcWLy0fRphOimXag7S*sH6VCpeDOW8P z22HImbLnIDlKCy)QWK=@_G>zK8EY}Lrmwug>eOndPDxDMrvjaBe)DszywqQE{OM@i zV`!vhxcp@``R{ka*BO-Z?ApuFS2R&>Z}4R!CYR0Bp_?_C(*`zS_sk}sZE>K;T*vUn zb^9qA{8{x#6|*21$n!$--R@JwOf(UbiKV%N9$Rvo(dKt z3e4|Kl_DXf661GD@i|$ZA7LX+Vp?s(o`o&C4r3_)+X^eOIV%M zteur_DqEXs2UD)$RFg(Bg$4ZySD`4nf{7S8g0PAqS1ertK3kYtFg6iNiF*-n1Vj(; z%#rk>j~GOk#aHq*Gg9D;9hH{ygM-)O9amx<4jUKZ*G(ieMcOHH)s6?u=N8iW<5D$g zV+0vIN~itXW{99#E&y@)thMFobn=7icc|l7loqu+*ev*Ecn}_E;(}ej7e9T#5p?(r zPB(ri=l;FNBiwV!@b9d^L}CjF8Wixi*1R}HiLx@y#&Xo+M`#fsx@!@uNBHxPjXw7z z0+Lb#CSq7Z$U;l6Wg;IIa{cDjny3$LGP>Pha<8x3&YGCe6}<7pe7TJaGABGU#hsrf zS9o;aj4#@@=ZCg(=4ufB+z|_s-*^-%UwI*FI_nqS;a1Vc_G<0_8P%R_hrWqqJMGYS(k zDG#wyW7PYnr%($c*yPzesajcADl!swoyO9H%VFdPL!0vh$G(_&Q%kiT-C}GvY984{ zbg^gz0gz+;-0jZE7yJ;Hvq_Vgy!NS$!P6-hn%;WmuhMUmOKm6!1O4bWzX@QcqkWk& zbjc5Z(f8YPK05~=C)2FQ(_R`Ul{N<$S7yjgtwpK=d_u9K@?k$UB-LtW`?gO$+?9|+ z7p5j=6`tHQWx+Bj!DHE3!ZJ7{739+Oq&qZNep)9gD1APN=Y!$j27>Z$>?fW(ztlI( zcC(MUIvU+Q#5zCOkJ26-)fWuD4OlVjG95m=5{3mK5T5N?^% zq=xVLSkh7sq#}Rs#w^L#AC9(_9UqRRr-FdWpJ4)83N-L{1JCl9%GcO3e_%tv&`D&u z(}^&;&{vzgjb2tjtyV@i01F=l;SS*Ck?{V8&NL^an;${?tyQcd2#Xt@ z5W6YS7VtBXY{&L*9#REJhIbgMR{y4|onosXcy-_|xZ!(! zjarYT&6YEIq7dHZ*kR82lN?h@fB}oP%3FMkjit)s6RlNb4g1wrne$@3gJ>S?udJ@$ z5LvHcSkqMCx5Wtu^k~s54_t}pD#o#}oQxQ19l^dM$JjbXJ)>&#|K99+qrC@+ZE@J& z`}nH-19(t3Z-Etg3S;!uV~8UbH|MB^>&hm5UC!QloM-LpuB;g~(-f1bN5!_t2vc7uNU18)sI9b-F;e8hDPcyZUNpTko{MV$x39En^L&e{Xh zH;A?PSEXMJ3Q|ajf~ab$1fmDaEyF%tuni=m2{Z!foN0z%c|bw_yc;N)NDf@a)^Y{% z%`&l=DH?Ghx_yYU3;&=00@xYr{j&iF$wc%QE1Z$fGok$Hc%vBbvyQ)?q}$W1$clF! z@KyGgvTJfgI6ttU)LuSHqhZyY%5-*1MWrKJ)!(h8UuTGYa=lJZsil}9a>ibLILx`c zgkLF2D@KX|?I0aytT#)8lRR+r$I(V!U72?ljtWAlYkA~9zQa;5aWg1MV{*la>1<}x z_HGt=Q23?~baC{1#H-++-vyLJ7{0UvWy0LF#*8eydwY;ieX8LcG*(e!EJB zq_X3z&h=Mlvh^C>LZ#2|lw+oj-|s801|=(9O6z@bj!Z`C33;XQ)3#+=d-Q&44>W)9 zXr$F%*wJ_0TDs(PKVN*tdeSrU2KGjx<=D2sO#6v}Sk*tugbSdDL61$7_zjCx^SKh> zc}2PQH5TG+**Cdmx~=8GI=#;>r>S$;-k9GB|h0; zIg8=nRh4SL)6{D|w6^(jN!L=_TCiAIU5L&}g!!q0+9_r1_*bT&7?qg6Xw6t5vnOq$ ziq2}Ds3S!74?vb*^*ZyAs+N{wAf9r~VjM2Bu`Jksk)Le# zpu9ym(Ob=!QLnr(YI)6-?zpeSN)`ca?-0hhgi~D2M}}?XQ4Dr@W9;dDrCd47#Q{)$ z$a`ho^exY|MdQx(ygU+K2{{5+wfn*H>ONkn$%5iOSddz7R#g7%b~7jV!qL2#d(!IpVPy9=-#@VFdw<* zfOsE`VwfGDO31pOihxI0PkN)6D(f;E5}R1&<)KbW_j)K_P7+u8A^j%B{~u9r;t%!v z#r@kS$tWt@Fl3qf$dYx&Hbf=~m9nqdvm5)q6cxr+NyZjMCHs<{Fvy-PGj=l=48}Ir z;hyi~{@st?zi_>;bDi@(uh)5=^cwulZ$>5htP0b%cfazT5KDiLLtp71{!VC-`Gb=+ zc#}j}t-~}~dX^$v*O2Fn8xgX`@DenDtzjubBuntPiluiH7~0fRHT#~tqP3r;*P!g> z{{z0+xxaD?K{Hkqg21J%KMi}OaU0R{ z1n}D1GUHpvhnWo6ybipl6E4wIWnKQrbFjnc<@gdvbnqLQf+_m^KB(b65)S|iVD-< zD>t)BzAS(FnH$fyyv}#M57#v9EQmAc`D>g}O>j43dzo(9__b9*H{NH&d1G?Pp=jSW zTQ^`{3*N=|xvD#S&pEvUH#XTH;r7V)HPDcHi{P9%ucw$;5-VyQaBEwUJMHf78^ti< z6~Topb%ZS0&p|09?zRauq<*!m@i@_w+_&gT0?mqWeKdkYI_L8vsaN zmLMX4ncpzOU7up(Pf#8>%Va%5CB53oc9n6BSpr#pXp>8Q*m3~@pRA7*JK0kdR~;!% z;|+UuPgO19&8C}7&CG4)PNo;dbyK{kly zjthInY$z`?(ye$6E}MRd7i_*aSkpcPeImB|ZH{v-^yHz6)Q%jY1cenXAH(4I?YhT(cc{RWbS&sZuQJB20iBmJKy*eH7bY?tOQ(8ecCeKrC;YwCfx`L-x(eK_Mw^%FO^KR1|0>n+Q z`T3>#apSa$AKNa1!}t36Hez)m?{*l=9!YJL9_fpnIqL87A8t#JS0DWaBKHWz_Tk%4 zd#XP)XE=FeNe899N{*oLY=Jl?B{*NK7xc~)DgDcZA^v}mL@n|EaU`M=Xy}7H#mxVU zk&sx;WdFuI*`0a+=2cBS+d+0-q_0uq;DD6tHtXa7iL1p2bOA!tF|gI?X64POSm@C7 z2|+Uytt;=L`5^N~DMEkVcn6{5W*Gg5t81_83?KOIFG@EO3tfyxST|#UYDPU1(sVa{ z$b_!#2O&&;Tjk}_wLC@hKZoPpQA0s0cij`KF#JFHn)gOD8EnHIoFub0Qe}j4xt?KB z*PTE&X*JmW|7JdC6bKb08IT}`pYH;I0)y`HKWpcsQL3u_RWJq5CY|9?`kx06nJ&#) z??%2({c@3qcr~P0U3YGbGl3C@gB7b`u#+wNyS7!~L)@(nP%>dOjygx_>o`NS1ewk* ze-j>ik<~3}uhibOJ@pTmMO3LSth4hzpdJ82b816^FgX?xHFb6kCEIg;GyWYN8(#>3 z^Rum!U*R_U-|bq#=i7F5Mh3he+(#rDVCHUq*UP8*mzfloIpYG(!$e-|AD1AK?PFkq z$CH^cMM7`O_U#O8fpz`$+c1RJOo!1krbXT3o}N29PReP~#{Q*?u1QWaX$3D=CO-^Y zAU7H9`{9kX&n92yB#mFLzjbJEeP=t)D~%Pgo?@_hAZj(zr!Xh%x|AGKbcS9ztDroi z96$Pk69cPp_H~jk_us_W({Dmp5MRW8e`$_XDVWGb4tgfTVP&L>T z$Iynl9_q+(+hYrJiG2!ztJ}g|sr|hZ>j5CnrAfgv>(Z~qp}96!tE|M5WP zr@;FC=rTI@v%Mmo3=IQdgU2=B{wwN45v34wZd{4SI!t*0GZE4?`svQlh))Se%b}Ud z=9}SuOkm}Zd5l71?{6D1xiSCo@_MA@ehcoQrjc)3*%|U=Qu=>C8(1k|2a>zaIjiLc(<^gmy*_pU}XOti#vitnRk}WG`UI6u_>g?w4B6_ zbg4|E?p{tcUwJ1GK`uuv?Z$c<^#R5`xy7Z#YyE_izmG7>!eC9q*^uN?vi?{Zv6;V5g z{Hph&5-(?DqmP*$>M;6Kp{9s^XDI0DuJkp_FO&_?fvtC)A%Iw%@fA^uQy3iH#n&F6 zi$vWO@*fZ$*7VNH$WHzr+(zxt_iD`R?wf+a?A^kSGUV^TUhNsF*8&n-_TX zP}!n$X~8rA%5?Wgw%j}Sl=drMdGO|rO7S;&9#|6%xD^K3n*$UYlz;2_-n?+1?|G~n z5ZWl4FXu!`#QsPQCv}SKURN6~4uZ7#lHFr{$YVo-hYP8Oi@Gm*^cl1{WRrYezOP8r zX1(k1SNPskhve$fu~%#0FZ>o4NYho)ELN%_|e%fI3JQ`o8>_5vNh8)~gLRDX-qvIHV{ z3hp3EM;4YWU-6#KCl$r$)Y!jB;$IIJWK|B^M&Dk(luJgFayLIEimDwK@T=cCOhdpo zQZ)zt9H%`k{lvP>DplW6yu+ z2!1p?+OAfR*EJOOn^_uhAP`2x80->r6hD*_;=SBgLW%Soeup=YuXzkknP&+S4W2~? zT#a5$%c5aL8rX1QWiOD&AFc8}B?{aN%SrN53O&%)Rh_0qfKncR#Mj?G$bOU$UB1F# zQ1UZXa9~wv%VwNTZo~=$q)2#S(FG$5yJ|rNQ;CRBp*Fxz1dJA}76RO6-(N=K^OL>I z2w5hny^8dgz`p-z-^gC=&Cbm?$N~{wPTmy;AmD4t9W%0R?98Pg9rdb-D4 zCra-<;u&G<(wrZi8C;{-&m0mxI&B`gOmF3#{IGgiqVli$R4VZBRfgNjVPUxv@{cvG zeL~W26j8_g74UM4^O!P9G+9`Th>4xK)}op3>@d(NjUO>j-b)=mq;s4l|3%bYop5M~ z++fj*I1~u!Fl}9%ju$>)P$dsLsV}?)gx0JFg1f-F0n?$x7}v0Sq;XHrPdReZB{!|Q z1@6xO6+uCR@LC*HqpXPmT?2gWe%NA#i9QCXsdmnygqW4fnlAPX1L{SA0KY1hk71%z z&tqLgr{>KdU*(urc>~c%zKf8~VDEAtZH)fTqUzb^)>)~_)#U3v`;kkBs{wn*z4IDS ziFS0=->8`T@sFWATWECT*%7raxar-1AWAR45gGkjL~lo{b#j7r3hZ%+q^M{5 zT4F|Tg?o3ga#N1_`~yiyK>UU?U?&MVCA*OJMc+;rfmNnFP{b%;Ga zd%vn9ZKhWG(7z*KB$s`lV5V+pv!f%_qjn{=R($KM2NBnJP8wpd3A8!uYgH}r@OZ26 zT&g4z!BcLvQzA>1!g4DnSUO&NjE`U#mj!O?tJzd zzo34LRC&FpTCgO!WOO(&xuQGdo>xNI@ZYCIg5)OSRf>|O8K zf1N!${!^4iXVWL5lX|6}tbkE)Km2}rc9ps-JbX;iigAgt+eG>j1=Zbc>-^Jdp0^B3 zpzwUzNv`LB=Uv!d-5J&iya1Rqr5w}`G}KnF=Z4ERplEJQD4i0tyX~=eNGX>sOddE{ zqirh=c^9B0>nwzo87!cW&fS$!w-_Y8OJuu`gFV1Ed-EuxUUzs4K%OURwAYWMqZYbp zMZ8Y~E@|J!fCfuAe--6b7Mz%$yZdH$xeX@Y(4pmMg61I++e?RiLl^)nuX6(wk;`wl zF^8fzk3P#ml4wRJ1sym^G=U^%9rco)Pr2C7ITzMvnHJ3=X+@0As8us88WgC3ACIpN0aRqb$zLiV zr=)WIvP<)-X($zMXcHF0>inK?Stlk|-4{=UBAUpF_DGZ`EsJdf>CXbnNTv62lQ-xN zIS{=yY*(ExvH{;q5!j#v)~m<1iLGmBL6@F5*81g?hrdLlf2WVWstcG*6P7u2c*yb5 zhlJ~gs9sY{t2l4o0EkAO1jdz}kDT)XLr1gXxtmWW0*>|mRQ=3`{DmB0-K?hQU3VKJ zk}0J+Syfxj1hEc!_E`4F9Y_bGWYp}848=d#tq1*^+xS0p-Lrl!%V*HvL!a)L1hq{y znYO-#LHeM(3`pjUUjxC%%I&u!hxy8&CfC-LC&n*OiC>beN4S3;jpu|u$gQ!r(l;UI zh_)+Vvc_RwOpKVcY%S&Gev|Ii)4K0Wq3H@C0t;jH6WnCWcK{*;M^Gg4X(l-P1>nug@0d&4F3^W zTW?e2fX9GOk^$hb+4K67O~8#X%7ts+m8C8+O&6nIW?AVz@=NzL)a<_HJqr>kx*2rw zv-+JfSIyk?I;}R|JoYW98Y1^2lYhvtCiw#>$k2sUlGJUplTsM}5DyiTU7T)eN<_t` zIn1x8W*Ww~+z~(OJ%7_-9Kw@;!XvZ!vI|<*Cp$KwAq_koo+?oCegek+q_B?mvH%64T0$zf)eWqP3;UqF1S9csdf7;|Zz^{>x%-aP*GSsF)^C z^V;d|T^E{wljpsA!q?;ZBX_MjsBSIFAnGg!5C96jJ7FhGt@wUmH4$()UYA|wTo;&v zt7C2*EuWnnpA~F9Kj}c?aHhT#Y5FQ-c4m;Jb1O= z5fGt~nbiGyCb1~!_bBywbE8wTf_jS=mQkzLbgP}$9{FYX+UBNakm<>?cIBr~9eC)~ z7A6~f|Bvsv8=3I%)F0U!x&gyHMvf~tQeGDQtobP|nLZiOx)L)xj!JS`pTO2h<2Ed_ z6HY)khCKE#O}>mP^$E!VQc|o?rr!?OX+DGMZwXx zdj~2wVeO&~NeTA()IqpZa9%hgXN7!DlhG&gR#!e5QP_nhF}-OUkreSP`&VoY6(WWF z)|mUMnD!VgecOBv^Iy=l4v9uJhY=A)pYg%oLN0St-4cu4A4uW08Pb_O*le3Jz9gka3&wAjrohLq8eoN;`CD4t21XW8k)X zZNP}+`lOns(m_Mbk4|--`dqRT|0i49CVzMRUAHvw$I*LtbnXo~9mLOMlGdC`1(NIm zIqA>ff|yhU$IfpXcxKvn$|xu~u*WLvxT=807A5~P84tWV#;n*PCcys*IeEMt6FaXu zU5z&P{8PcWAY_>gUrG`%Ml%Y!Kx2A2BM#G@-AuC#_0U3UP@XVm7K3)ft|^5@%f!(A zA2u`Da^}~5ivE#8ovD7hlT$E?*+vJ>28U<#;6)~Iy~e(UD$V2W*!SBtfm;LfU%Y&$ zC)H1{9(!Xzr8ZCF614ASX|QC?&%Z7!>{gK+0z|b}n#F}Zh)Xcc2ZbNkbO9UY zU*!lAi8S>ZQ_jrJ8}kvV971qXpG@Uc+b?TgYq0AI=U|^W!!^b&osd*O`*X8Oq}ubv z5Xb@`R7#*_EFgkZF6p-JlgttBV)_>)aekN_kHF!1kaTR(mlVrmiU?e&2Qo+x&w0{p zOtT^t^HOn_XvKLD{MFm)GIf8oE+AT4*a@dI9@0snD3(x@fa@-ToG+d6Fjin zdtPvo1H`S~S2_?0^-W$>u@VLttKWwn*maoqD`O`1{aT(YN4VP8om?GS#UOHfoMY>I zmE*x!RHj~dSo|IQc6*|G&@UyiJGha*RRTc3WR$1NQ{`JYF@WIeQ%%XZKU4A&q2hZxnI}dE%M`S>gGZGeqAYVX~cna4VX{W!F1ndZt84zXrM`} z(G)coM$TC%r7(4)!&}uB56{ZZkIOBx3b<*2I$&R&DoadRu=vWrQE!29 z?SCIX1BqREn=AP!vArWCpzIcJ*6wx?@_U>(4W02tBxmp$(lhWMm2}qE)OcPZ;Kq?$ zQqd!8+<0=G=HTPX2K7eAMxiij62uOTb}eDVs|;m}HL=>p_F8&74)){!zGfOdP-0a+*+_tTs!kC%Y^@O@*K%iKn*=_46WLaY}}w5 zno~NrKV(p~ZhGEzGNqa`Q=Ut)Fu&uvxK3J;!p=r>@s6Gh(I^^dnVCC$VNlP-)$REPM+SygivwhKJn5Aif=f36%rJcq zMD6**ox*g3wQ$h;ZFGJi6MN1`60)LiR9$x;q30*7qggQjjetwV|4y&k8{m+IY6bjd zWd|rqmD+uZf_M|w_X0jvC$rUi@2RIE6@N#iUlnD#s(inRcxeS{Iw7b9`}<62n48M! zV1?I@Y=9d2x`IxsQ(c|gp<4<*n4qfBR=W*>4w1QfP{9ly0GHADjLmKxIg7mZL zn=3oB4xLzQ2gI#Z9|avMMRY9kS`WA2T1p1kr{9(EugFxLy%SX<-u)p!{WSnPtvQ*w zh5UN4WceR8<-qVp0tjyzZ*$zkvm5AP^+IlL{ENbpdhOTV>TwbU`oN{9j)g2Lp?chV zJz^YVbzJOZUm5T$f-WJxIbdrVQ-V(f<=wwQa8myQt@ZL zmz+??*AD1fFC_FQZ2S2?)Z~!Z^|-`Oe8WWQCpbU(Y_?LRhO^n?J!YO@!`ex3XdN= z{JbE#?i3BRfQx8;h*w5T!5t}Sem163wYZ8<+2uD%*@=(0w?*PX34zg2@j>yyuoADm zyCO34ecQTEC)cz2d$|!^?T~HF`18s7)b-J-#V#6{c}spc-%!{n4L{l(prRJ@vZ7oV zF>lM($SmPYv@JRR9;N0d$lb6+oBSXNcSBE!?PgU8_7k4!VgD%%7Fn&3Azp`0RZFGd zOB2)#WSoEC>>bca^m#K;&^2kBKX%$fkE_+z!_e}HIh({wymdxO>hJP#5LDV4AUB3}S!{hWT>e;>Wk$ZTq+53q@rnP#a zw3*%6+8|WdJUb$Qzt3c3;@gPL4{e_TnR=Ogsq?;S}KFt`C(Xl0`7^@y%k`U z1|emSuc1T@ws&*S!_KV)b8TTlb1VtzHC>WHH*tyh@`4QYjr^jGF=Gxd8N-Im$jzgY#4 zVNZsOWo{@{qhTPf(_y_A#O?sMX#K$fk*vwUp0AWv*SXz!?PA21B`Y;W{^b^1n& zEMPO=*5MML;;J*3%5Vkv>v8X+YRGoss3}MF_kbYnz=K@aqK%qPAsg>PQqkg>8>P8Y z4b$|CCg24}w~fD7u<{*M=%+dUx2N5$O^qJ>a?Oy3>@Tr&`FPqI3=Bx$L;~v<1fD6$ z0~U0agrFSwy;~^Hps+pGPrTfaS3vfa_I7-1x5LPFK(9|qpRmekWV^wU zVH}*Lt1p@i`vZbSub`6*tB$*YnT($$;p4cogo=)P%T$oNfk`>`j-FP9>J3#h)Zv8oU zHsOuaFb~}2Gq}t!oUcH-x3Bx-{Y7?+C07yj-em_C`p1v7EmF)}@(>}5KfSVd*GtuN zJoy?7N71F!qkpa6Ddpod1ibmWNn^kme9*?K;cl|$@N_Gqm?=^mEu*GYCf-8c75DSstc7^;1;W2o-`T1E2=08#YH*V zOYz3L6DvZiuCDQ`1pH@n5q?7GHAzn5M^?`F36kj&qi;?yPmyc$*WY-MS>4mK-4dKg zxE!^Lz-sKJlX1gfoP`L6a>Ks#TZ=ojl2Aa8#`g+ZX}SJ@Qc&s-%G~$$L*fgSiVoIoyH+iQa{9gb zW4X4Xoj-JnO(!`f?E%Fv8LI$4qeE&>WBBUI3WXt<5$`b2d$zx?271Mb4Uh0Q-}&TG zMTcHVy>93kNv%f2@6MtbpBRR|xgF#q=;8y?NWg;Ue%(RhAE&gHvRm);pMD&f?>TQd zGq!bbC=8v_6Y#q}aXU%H6^K3e^C>1Qy<_?eZ8Uhz8gV1Hxn&(}?kB76+%Niq*`Xwd6;k(d&%UXh^~+5q zUZnUrg^}L#%h?k?Mu;^ydto%2E#WAu(7EDsdd8p<=2SWhMVTBRzXlw?GWpTTa$8&F zWb*BLiifs39i&Pv;kx8nX==%qk7R}1&i7b|3WiHSap@Doaqp1l^D=krC*h4fW}XMU zyxZVhLRVnQgu*u|^F54LUPXOj^RbHy7vIRXw(7`V9f6P$$bMqq?+>AIF}ufF*UuM9|Ep&WX<({Zg7TEh3u1pNNS_q+>l z-6AMSa2C0=)<>{9IfeB9@s7(3)|B?R%O4re(%&!2`X5N`ctoPez{$YOmIsN>R{w0$ zP{I0SZIrEC>~boCUpG%S;lnLz;uy)i_>}0V&?1I^A4!f|RBIME%i##ox#CYs3!U4p zx6j3Tj5Qp`Z~R>_v_21cT$L0?vggMXMe29v+?1Fw!u0*RkI~5b-+G4ZaMKeg8pka4<#xOo4HKdtW@Yyn_6{5k zG!9=GDT08dftejk+5{gm1zjMUrE%jj-Nl`3eD@KErW$6)in1X*?8F)fi^ELxa{oDn zLG}EhVoTw$-Z++7MP=cHGxuArBB_w%+vEd^4nb4;R`+pKJOap&(&$so`S70!KHUowbQ< zp$-zHcS)5Nc+yAqi`BA#A z`x|i6cP3@PT|6?sRF<;*Sgs|?P~)X9H86Z?{Raj2@ZDa*KtU1Qud{U32u_T%q^>*M zP`1bd9MZY_MJM}Qu+@y6?e$3LCp^h0IWGDcLalhJXnc-jb~pSPlGXC6yoLd|FaC^? zUkzEYI<)gIDjcxF*SWAgUI=U_Tq&zy44bt98La%WESB=lN=UCt+lpDapp=1;BCto@ zkY6l`;xtG?;xCIo*x~vGdm^U{5}-+D`M}_kU}665p;TWfbgnPzQwit^gZk0P!bi~5 z%4RD5pI6{#db9AO;m#}(!e00@vzeaJvMl=@Vx!vp-LG_Fno$ur+!@D9Rypk1%x(t`8%sED<2|r-Q@lwit{Fj9xE8^NjUZ= zzSjt+)m{PM3rsw!7b}NMD>){jZMQz9aD3#UL2W%SX1s!ms*_Rryi?C?GLPLw^Zp!j z9l3Y1$GWvYOy8Fz7RML4>D%=DR>Y>+t47IeJWs$aH~3qbCl}?khna3b9e@|hnBq$BQuDeNGJm#nu=~$2|ClrUGtw{UmAph+TF-d-LE}bS z3L>w7YvW(nXho7WGiMvCDQ6g`Xo=Td#9zq%NC%roJH49ndpd>veO?a#jj_71*blYj zoUxWaqY>m(j%u+C8yxEBM{=EOm166M&mv6;)F?SU@H6mKmKpvj<&Cil^oU4FKXwN|CF#=IIjQkaL|lbU8~fUk7g7yzi8vQN0-ypQjG(h{z~#?~H#8!|-Pm1+8KR^{yUCvKK+Rq{r`S zO)NSX@P$WrW=mnWg|rxwXmqR9Kw=6rJY0iozG4uln%y(48 zN}o`?guOm(1&*|^2yCpI8<#p8uz3+rU2Vk0;B#h1_#+g@+Hf;YHD6AW2g9U(wmvx8 z=AWAc9{r{{bv`Ym$O@v8kl;P2($};QK@S>wt%HYGIyq+YqfcE%;^R_o3L{g6Wc72s z!7)UJ2e#_*5ecrzBOqJhRrUJt!as)4H3l0J3p)j)SfgbUUQK8Ok~zF~Ql1IyGgj|M zvok7}Lb^}iD3`MB^phA>DWLjTwr~x;lI}AwV9_&E8~<*hAayI2zmSs^j`>%yi6FHZ1?E6&3EOm#!SA{ zZD+tG^K-xZuL*8vv9t&c8{Z&{7@^wRSWBH*ky z&_CMVF&DKxLDw{_P14=%wP$IkA8JWoF2fur<>)3wubtvtcifvT`s}7 z+-#3x>OJ<+BWC665ewWn^s&=Zr_f`2#1=+5co3VEv>9%A5z3N?We(j|yq3klB#IXd zrFV&D@~=Ta>NB&=v-hsBeLhnF*D_!D0xJ3(8KCZ+7w`*uN3UgzBPbs0@=Fimn%9EjgB2yz}ApxB1@K_c>L3a23 zxkrh#rsOgITX;~Aw%{)y-=sQUarcAWzLC~1#nb~|!pvPJpw)w@g=uE4Bg#%X=OhzL zR@9dWYu0gbe-PX4LA996w?&U!a8pbQ?U??nA)8O%JL}nUcp9JkM&Z$J?nX{X|5EV0n$4MSVg$9Ju)G@i@LktaI&)jbC^{zw?_8O`@`+k)--^kWv_@_B%utA6LXz21l0=Si>F z;0hiy4WBw2&-+^~8un`6tg#2vPL!3exXv7P8xUat+spXSB!{18l-i5N8$R+L#P%5> z)Gye-aCL|iU-vZ^jf-0$IXQ2p=n&F%L^%_^JK=|#8c zhXmpD?6#alLAX~$R-=>r{W)@Pq>I~!>9L&CNUGq|iC%@%hb7TFKI~*OrN%w$GPgMz z-=og|+UY^bd2pYpqnCR+0anM$fhTb>V{R+jPB)D$P5Z1(IlI#X>{(BX@sp-1Cf(N7 zn5mD9q7+&Y^@2_=GDL_@f_gY(cDAyq1eU>Ny)-QgcZ9$Oc3m||kVe`5T# z`IzFr$)}S4Ct_)zR&rHAXIZv!6n&S$FDviAW~a zz>+5#Z|Q`vD}z%r-!%$a>ll;6 zufR`h%RqCPzL_eG8{W49-H|0Ip_k$^wR73~V3+mp@UaZG$ffr)Q|8IArT5vOj-d|< z<)s=!&B=n))~~ta@bbCsiO8AjWBBi`Uk(asc89dYSfA=!a)UHs;heUevds=n?-202 zHc{BNk21-bMIV4=HYa`;d4(XwNLK3lO%_xDpwDv`$^$1o2_FhQ9j;zj!rzki{Voz@W+=?6?l9%C z^r^P{`^c+GJ*np68OnRBCo%ev<$sZW8^&&R zX|l`sp`W16MK<0 zD9g*+IV`o+%$}l-Bqn%3QH!MNJ9;I8gPVh>l8nf=zUxb_BJ|#E-0Eu!!t~}l*r1sU z{r_d7cN!*|H%Gx|4l4FQR$6N z^8mG`?Y5!dpbII%u}O(BDQ!3Nh3A=|sdk&;1%bk9|P#R?NGU$_-aS? zerw}%`x->_iL2dq5P1?*p|1c5MP#tu}Ut@3jPB=ss!ZKq)y%#2Bk0+Rw%rA z=-^|OB3AW)h-jIV%~DD)@F?YwAFy7H#e(IBOKREvSf96;sutPHpI+jd)a2uNwV?7E z+vY^OHs7K@`2^pCO%Hkh1tDWDKvb7sH-;^tRRPkkFNm)(N}!?9^dIRCx0$pgHL*IOX!JMfqs_0q1XRSv+h-ngxv1HZvNDap zcIoWD*QxDOxE6?NU{mqbM%ciN9&RjB`8c9u^o(@wSw-QcV)`hyGMUxj`z?(e#gWBj zTa;;wvfT5+>+)dp^8$rtjj!+Myuy4vE!{D%vw67feQE6CnxV0HVAA_70$tY{uUjyw z?XY)3Di3GxNXa136vc5VUpf(pvZWb*Mig@_w9GHzt+Gtwy8 zoZSP$Q;SU-T&BlIIH;jnYXk%5r(1y~s5X(d3zs*2N4&c?PHxQ|4F)!t#505G0UTL* z!qTdQ{47zpBZU>Znyf0P(>PQ0d82X4#kO%}O=5~#Q|f$nr#rsCkQ90PCb{Md{4fGB zwVi%`l2yx}+gnK0xVu^F>u&+`I`E8JIlD;ilWr^-2p#;&b(MNYbEw{j*)QsX=J{(8 z6mdK#d`ZG)wWV8>Lxb*N6bs$tVOBfaWzG1RNzuGGwN7i&*n0vazf(#-IWaNO&x`XD zvjaHuv0q8fF^;8e{#9$Y-Ht#OmZ|gZoO;#0we)TbaKvHm&p!R|Zpdj6|K{r?>eH#w zb#FSg@GqCT)4xa4?>+n;0$5Z@V0kgn5#vjN_!7`9osXxWK`5)skCr!$n>{gwpavw$ za)ya+1BOJk=Aw~j3?FIrQ1w_KDO6pM*L0ncPT(o{ZJ!9UtOMHysRL2(U>G3Bh}zz( zV`LyaMjjaM2OPZ5s0&U2X7AOg23q+pu;ftj?3q*E!$lMDS(Y@589&O7IlRh5a#}J) zc7@zAwUZz{OBaGkX8*h=J%@j2GsO}x`O-{1VE;3BbMeXfFNw0I%FvqW5D=wHG0NVH z{=4(T5xYPxv7-u;DqSa6I)1l?+vc^e)#)$~+0E(RDh`fyF$(PT26{4fGE`6gR95Wf zlo{Hb1S=EjD%><~DdwmiV<^nF8etb9QcS?Lq zk+w({yFB*mAIiU^46Ex27fU*jM4<)0yc zFsk>Zc&M|=T6Y{aJK)cLGDy+C`h8KhGD+qr_!0>xTt~^$>#}#M2{(Qp8G-bVJJn>I zGGb&TVsmgHmyK$^;>>r9%>02A+(lQ%DSei*UUdg2~8Q!IVUDEU+)CCv8^ha}Y@eXWO$?)QYn^GqM6 z7D5GbUoqotZEGj*-@Isn&$Lh?T~^?DDCo)lZr7^kaVSzp$VaKq6%yr50?9!HF6Xz< z(V0TWrSin*5#dJ+uiIjQkX)f{4XS}g&+TN}6@$6BBJI*)q&rt4XZ2pY&)R@hozDm7 zBX;->i1u9k5#|v6qnIHbw7k8sES)GdhO1#4^~uG@~YuvbBt2jT97xSkicCx zgHrxJ3JaxIWOx!4Ll+mdka)$yr{5q7>M$-9I{TKR6;{}qyvaU0bP0thU-~voK9U5Apt}s%M32_d zLv}y_A$nnZVE7Nes0>AQ2Omd{-@bB`D`G=w>hG4#Xob)JJ%RsV>tp`*evw$rZ8yi#mWH1tN@M~rB*5WeVl?}O za$b(CGQZG5$J4|trq>RaYB+dgmx;8I7mn`=HW)Z7nBttQ*x1R&Ub_Gds|F~O?L_{N zqOW*}3F*R=9XQH*46iSh3ha%tSXIAsD^iPC!&(~QDjfYiT+&r^sf$$=$Xh&=SQc?z z#@Gt|wC8|Im0T963!P@$d?C!Sdo$$cw<|Bxx}%ZPph(pI{XBAUl$;41Wj+5$X&%uc zB<{20;yCAsI(eL-K==_A{0hgO3(kv#(xH*9~=J$}z4*_AT<`KG8glYe|K<5hqg~p3N6MA2Njt{hnl7 zb8=05Wp+go5OpYB<7L9X?#X$W1XiJEndBJ$oK)e!VHo_!sG*j`Uj7`Z8y@NdN{Llc z9>hYhg;89p?<=0|y_2v<=HIeu?T`Du{!!8`K|OEL5?nI@kSD-^LRv;S$Q^vI^-_Vb z&D?LLaQ^?D$Nyf5%!eDm>Oekr?_X$nNPjAF-e*wbRa-}IVa>6^Fu ztV)0vInm7xF|>c<20CHep2)n;Z8u0h;5F8Q^xu7G?Ux*UkGS-lj_X0`&5Qi->Xu54 zA;ILCJ9$N15f{(Q;0_bc{1=yAa^(Y4sG zd0!SuDHWh3m#L?T)k?zWC1q3lDqfbY-&5Ji`=ZWEC)X4w?_!H^osVwbM$v4IaxS)< z3T@bn7to5%xkBwQm6eao+&~*E;1pi;2wkJRhX-FkYm)hG&N|ozC+%5_uA-^RXzzgYkdGT0nWx>fRUW-BL^6x+yh7`kI6b8aHB9 z10G%vXS@HrY-g;?VuswwZB^-3W;00NC~3uoV|N{Hql~vLH#WWZTD|TcqqXThKnxPix3r!ACq}OwZlKalp$#~6GBcbK=w;e2%sX_bv@@z{YJ_Uu z-Z{$SH^)f?-oDR@Vn~mQ^=$dY21>P<;T5xh|Fn2~UaaQ4C;2(z%=>g+LFOmMzw0VQqX7NEcP2b+J#*|6fC z?eD{^fp(MMTE3;11QgfVrG&JfE77T`JpM{s3^6|Y_0s&?4On0z1m=Nr$!bxqyPP{? z-q?wXuCrt#Sy(mgw;#lk|0=Oj=o^LE`6eIp@n{U8{_eG{*p1P3Zw*KS(MH_bz8Ssr z$mu_ehg|&xwx?^9SWZOg!EoFwS=kGrfPADw3kA6aB zlG5x%kcM0Sx4Xtnn3`38YuR=EKe|C4L9k!L^mwk>IvY=aiI0@GZ{+H=U$%PgeL-{` znXq0r0v%nJjDap@in|`$tyyy)ThJSo@XyDyX#iJ#MJ~!ccBb-Ls0KH=f?h}CCDU-Z zitmC)I1iaoUS$HMimK7%kgmP1F}R!tv1#4ABE8X*p~gFaqYDqRNYECPQzjHL?oE0+ zMOFhycjc&p|BG{r|HZjGJI7w&MfSsp*O^yP=vdS!Hyt!2OEkA*nFw=?7H=Q0mPv)Q ziD`Jx)2k6zRF`(%LC8x6Rx;@vC3H`NE;3MDCPn?5#>8F|22H`}g47A}Pwvdt`8MO+ z%5~h4nT6&HjNpEc+x@AMXn#lk1h19~eTh7|M#ba`rr>tC(6bb!U8fT4b(K5zE@)AE zHc&Z2LtAa0Ng%+kL~gS}q?X=~4uqM8;anEtTV=al=HGxnIXxrW#k%+PbLDaI+YdPm z!Ge3D5G0VKs;ED9;~?DFbrB`}he%O!Lc#Kd_*^nWI0D zSbc`-;mZ#>FB1^z@_zx*hC(z>ufqS~>MaABe#5r^jgcZDC^bq!5b2N}Axela6$O#h zk<#5Tz#ka&kWNt$=^71Fl+huvu}L|)YXe3-^Zws2{?GI7`(oRRYuCx9nwhMU0w_^`H0H#U)C~pERnuOlGoMHsluz>ZMK%R2{;v17htWP?EJ*7xvqEwR+;Nw)~s$I?(3U zp|R>JQ(OhZ=h%jL$R|$9YtV|mKz<@IS+dBnim6~r*(P(c5x~3nWOlMOZr*VOCG27g zfFR1}B2OuH4DBB+Am%(B&J5+32{_y(?T5B=BQ$V8mhu_3iZ*(>1t82?TE$Fu!uu>S z2=|R3@&yr%wQO2W1}f);FQti~Xe~kwM+cR_P7w>4Ce@7+@s)+-Zk|AN`J#VpyB?$eaA8cfITrw;$rt}c4X@`*030yfWu zo~6Z82e`75___T&o^qi=UOG+6lpvf;-r)+TW!eBRT`q8>yA@=+yW;|{uzPVfIg|`>3l}g1wdit#u{-XwL1NI zw~~vK`g>|tIC~I4+7nUbo?QX^(EV|`z@GBWzoP3~EMrHxrwxN&ZX6c85O~w+oxN~k z9QcW$$ufBL3!h*YRE;sg3cT?lw+>ob_oX&S-3_-UO?FM#1nI0ar;Y5|c-xt3GFWB1 zbZ;Ou#%OYTKUkHVR|_b}O#Q25b}oShohD9j0w=wC6l9K9f%pelTqLpCIC#aJ`$JIlJj9>P{SYZZ#t2jvG zUJMJ(!bIf*H$?o|;0cK}dMqO}9N4QC4M29+$M3^_>gHI$eEq_-$+4QJFX>*?-`{Q` zH{Ad@q>Wv4{OWksRk&!W+4Fs7huTu2QmO271c$?N7OFKrM~{E*vaLM3t{z!Ed76Z0 zK}XXfODI>qI!J@c2+sM=qX48~Bp)+nsIvg%(2g^Kded$2DjTRQSLMR5(&fVZrh6In zb2QP?%KB@cq%(v|J+d^v8=6PmTbmdCQsHeM=pdj%{YZg^MXtOvgea2O)K!83SX#WAiyLVi0DsinVa$seVmw!Kr6qAXVVr? zhcG}KTma4^^Qv{Ns8^%MR=zbld^UkfW@Bmx(?M!*kD=+gUPl@Znb}v}(H6G@VzEzg zwJ@}S9UT&I%OfQy&R&-E(ad`4tesZSXRCSHU;gnwZ>y3sm}lizcyIht&~mP0+S<%G z_y1ef$Lmi9dCvnV0(z#od(}zQk;+HZiPX0P8loZvQ-$F`FbSwh^$|`TNFSOY&;F*P zh3$nCBP+!|;>jtqKoDP)QvXgDw)utnE;<=gvIuTPJ z&r|SB3pSM)@n=hR>VO$wn2?ex;sZ;x-Xum2`$Z)N@N``+w`uoAT73%3i z)r%E;x|p#U)c3$4^?WV_SHk3h=S2NzpY~6N*>!2DE;5JtXsA_jt|NE^hMlHSL6>>fh0Kpfu z6|M^DfgJP0NQaQWR2r_pYYc2#UujbR%zF>?QHwBlX5TMu7qsQjhxoy73~2%%pnRm1 zFWISZ9#Jz9jdqI;7o{Vb-~)l%&M%$+|O4f-R=Y5E#6tLt?$NU!%=U!ug(3+N|RW+GYO|j z@*I#w`{q)GW3K`#r7UEM9lQpp%!OdQgKxNCq%Q0Dn$S84#Z3T*!3kC?U6v#?3=^Kq zb_K)_4tyr+@#oh(4tY1dMhB)V#^#RCy2MHc5kV{mt=8s=RPz zmoF(`aXywMpPsKOL+|LgpTf57@4r4Pp{36&6)V|GCZ7hF4mKnEO!PX& zd(}A}wZxG#O$r=;87VFKVks3VZQ{9A;QW7X{*gH+mwEtX{6A0@@2DF;+RvA>wYwx|YDy)K+cM|+Wz!hm10jn#L5dc;>X zmgNS>tB)1unqE443d~=|J!o|N_`?{ic~V!l>JV}mkWK@E*q!~k<#*XUq9}<-NKx7- ziJ|T5O=D@k$S1f)@(x+cd^_EedItAwd_5Uk1hwK}RRA!f=vL@u9#lV83L4E26ufoG z?%WE`FMYh$a)KP98g{8_N1mZ0PW29tn2g$}itn27YcM=fNyu@k+W$3Yz2{g|w%ycT zQ3X)^>^6qtJCt(X7sn1)%37Hs?`}tRddX$W%gOx?JImPoSs7Quo&T>H1ybM8lS>1{ z`k)-WD%OGSaF@gmxD8`iB!eG-)Awd*gVKz}gAK0gmzdepB_%Z#GBS!boBGL4HC^1s zKG&eN0D#ZWwUSR!?3ZaId4E{56BOo@5+FWiese5Mt@h&^{?&sBYE;Q8-5esq;}0!IqW;o_DZ$zvVj&0 zPs&{Pu1i${O#2av&G)oOk)JB=O|0IHt@De0&5Jt5n7*BR>nVm(&iNOo%dLJKRF53| zMwnTEboo0z5>r)G;l!~&Xn}i2NZ-Jp6K4H7svYg!uFoPl$(APaHxGuH2f{PHBLGzI zm;kassec*lt!C&&ptLKaj7*2aTkO=~8uVej3LfxwW8x(O{-Fhj8+plcRsmBd2?^M~ z6#pk%bqv_(yj#b|bza+_Wau@T_iTLDs}?X4BzI znT~mnhfczBluNdZRZVtz+l>17DgZAXV!AjlQ@h%x{IHsJq|FN?!Egh`O+EtXk``99jEP-dD>LTnX&jrfpC0A&061atxIsxpMhQQf zn@_N|Mbz)d#4AOQ)QWv@J)&}%%7(DLA>rH-jkH)^obY-*jZEmrv4<0ta*Tst_#CDn z7xoMlUCYw{G~CZy|8{{i-87x?9ux6%+?MYg)ZEKZy!h&f411m3ZaZt#7qHOlrzGJH zEz^6fbX7~%M5qa*#&O7#9v8by!;ekydb)&t4)*yFTXB*c*XqwPmAwn(tyQFz*>kvd zVhdiuVsG$^&dv=VL!TRrDpPq9sH1A?o;pEyYHB| zD2$0g+gLvn;O1V%k=6q~cAs6HhE9^sy{1|>N*g{mI5mURgFb%*@7-2&evYX-dk8x~ z`k#!3+T>4>vn7g>t-uGj_hH8KRl|jos40WM zwb%sPWgVZW7c1Bj{%RrH79EZ`YN2dYY|I=%iYFtPx5z-Tsww2MuI}8!O!%!Mpe0oz z2|ZBSdHq&%N}6Tx3(v`xnN#eB;$-VB+2erUcq#5jx}36R_9xB$|;NN^6} zZ~N-tz2-8W82m8XToAR9V4g?(Vp6B!GI;fmzoCWS`k>nsOUG>EdUsfYIjJiKaGL~kWA=PmK3evx6YOC6s_~#u0@Yq6pa`-(u1pwKthNbq z3XZ;X^fET^m%EO1$l1Y_yPLh6FGd_aiuuBi=E0Q6vfh-dS)_?2#K(*5XBrX;V3U!Z zK1w%b?+{$!y%)6kl(##HsMKfZ3Q*yY!`u&=95An=wQDztpIo3f3Havm4dAC6f1F2^ zEGWc!kn5K(A6xOTAc>kmP)EiKDd@BElBC@|NkeEN`sB~@Z-cv56s1cO?_(>PZ)zE zxqQm)e3Zx&J~%L-xtB@Ao$5qL+dgX#LM~XO`vF!e`=!=!0>cL`xNw#f3u%EEs zKnGXrz-cx6`kOd4_<&-hP2vESkM*ahhUbUcg}g>#*zPINi>WRP5YM&ri-G`W^5Ucgsipu9Un(_}}Y04p#paD9CwehmboGXFh{59m{-twt;K- z*#?7i#oVVC5eK`S&YH)V2=~MEfjSbdmG9`iS)*R(#x*xnNE&X&d9(en-lAh?d$Ty= zBpunp|KI5M$SU;;z5{TT%h&@wM5cSoMTdV^etUbxH;wJ%v_{vF_DsbB&@EYG%Eo1) z2Z`-PzD=zIlm71B-1)IIu9QIj{0CcBb!MM#H3>`!t!G`a73FuUVX2Gl7Vz9%x{62S zD*%sHj;ilh&4El6QM7QO*l_)XUU!kc^`l9;%uwV z*gNW}@}FakK&=uhL0E-z8ToS*okn%R7B=r0^E%2qn6W>3a3OSEO)+ zB`0HOIydjXM&33eEtQ1hdPek;%;xl}ch8zv7O(P)Jr!6G$>}d56Y)X1qseD)68uKm zyxeh@Bh^lqnlD5Fba~|OX+uBZThE*Pxnz&8PNL8ovHLh^8OrDBU@@=d6vu-NZpX@* zd+$s*3)K7GwxQn72_F@R|2i&PXSR{4&%2ANoB3g3N%hXC+N-1@;-A~$tEfyNcW^R` zM_36X%6WcfA1}Y;+CsALy~&B(ZriWeJc=Mc%vSL@h{zII8=G7*Z`_~OycqKz4ygQA zr8T^Q8e$JW?(}0UX-w7<9@WDS)Z-3UyAJL_Nvlhaet*Z);?3pQ@npT(FJMV4F>kRm=KdA1B&G~}LYBddbgGwln5c^O>aHX*i$isR4-3xs) z$XEI%U2g0+3fMy05|n)VG(p30Qz&dr!} zp3_DOq@smCO$qK(2>=S&3Z0oCn^SAYe+~21gwtY^<%t`s3`aL&r})(;?&}E9sLx#q zyzxPn?P%rMC%rR0!+dJXQ{IrDcN>*vgY-iB%7fh8Ar23+A`WWYmh$#-n~o6`kfl^J zk(2A2z&!Dbod^XF*k;G<5C4_?wxU4`1#--&&W$5M@Udo1wDk!YdGT)8Wj5>(k8j6> zmRKF@l{5;?oNyv)M}ihxUU(1(eem@>tF9)G58wmw?oh4>5oa)_&vTUM+e9B2xUS82We!36+Ol0&8WEaEr@As>VvMR-nq zyZ_j6f|N&P)wN+L_Da7shE{i3CLq3JE2aJP2(5YkIm_$VRb^FE!qfYaO6RJ>%RiC9 zmxc5}*LY>n;N{-OEzwsqfNaM(lFMRTW%+P%8&;)tA!*=qK3`?0AN%)#@f}X#&@ton4Jf@4#tHyIb%((NY@|h&HhBSazq=AnwR%FPctsQ-w z&vUn}@uyQo9#MXBSI_Pc16N2@Va`_%#*cN3%r1Jf@wMxnv!u|!2)w+;&L|Qc$r3g= zr1Mc*Oc=mbjElKj%gDv_+k9VsNh*{;u~A_G>q{?kGgu1CYQG(3{&Zw>7}&AVqR_uS>>wrq6U&I8B^ zHKPLcC7^yjkg4G2*?4&9B>^|Svi0W{=!Je*Oud=ZtySqqj_T^&04X5%-mSV4O_V4g zcIy*k?OY@WF3?#A9ksT3Sm6rIXCPxR*p^nM+LvQYSq9NH6{?7uBj#g*|+4*gj`Nd)+=jLSg+c~SAW^V$o zDjO$E=0#gt)`>=W0zgF11l}*IUOQV_k*uiuYTq;Jo+adY8>9&hPdFxwE?AM&=+5%JU*Z60tnG*8rIMsLP*iw^qcHTW)qZfAif8(O!@JXFp)1>F1Bvzl8c(`wAYt#Tnf z-PkWRuL3q=2dixgCYr<@eQK`V<*^AOx!&gsfi1@f`43mVLUb!*9&W@lDGw2W!poN2 zC)#GOENX7b)qb=N4&Dv_tws@6K@az0y0k$i#{ULK-a0R416xnFju*#=#7Y_k9B*-X z$DsK`T$kfOx0LUL^;}}Qif)83s|&^iEBg;w*dk*DjmNFN>d9|8sCWdF^+_xY#(_%u zzjqrH(H->E<>{f6@W8jnld*IB!8vmLc69u3S@oV03FGYYBZ9%?cyd+5S}z6~@aI6A z-yrX5Fe+6e$s$uQI>ASM;lC;fIf{?m2TG;B)dH2dI-bMm;G8JkGSmyxE|U!Ym{<&% zw%~H-?J3bnDv=IM*7;#`kvm+mMLt7OxTlA-Cl|4)uQPXTaly6Q+ek;ZghknBGj0p6tQ- zIipraEUy@j4ZT+R%))mZ(PIxu-cIyf=uk7ARm~!JqvR_+Ty`KBgW+1ap=y><*JG+lm$%Q45}0e zN&vc%>`xb5hW+IkogYrv*TE$p{cym(+x3eFY81MJMoMBTm8y204HJu)nk@;H5I?s- z9JYPkEOVVI7cRSbJNI#@#^ZySUxW3spT$ZdUlu#QXL5#|n?Fz3L@esB7qu15Mb<4m zQh}h^V(B(4BhHAqI;KO{eYz7={Du_k6nRB=e#&0@xl%AZ?>&-J8V^FpA{v<^LBkvG z<=li5(xW+YD$H5O36w53bGeUfmyT)el(3xlcyc^J1k`*pk&hR@b8mKievRSs9xnkT z+|*g8_qioB2<6WSt3V-pBYJCUdeYx@!^l?;sD;R5qU7JYcg4Zs$*G+QdzKEP z@l-aE7mq6UcB760Twc3ckc-xK6nKW-r4q`K%N!RKqI12inP6$*EnZj1w2={ZBfHgp zkKwkH=hiABlP_D{eh>7BTBL2jQ;bFMMiHDf(`Vz4>^WOU!fLej9~H`Ye$L(lsmu0r z9CG-_2j76P#?#a#Qz^i?Mnszmp&Grixk;=4WSUyw9nqutl85k5df6KGG4xD}$pOCx z$S!XhJ~oCEd=t$uEaZw=H%!=3nA?0r(Bs2KRzAYKBc-`K6Kv8-WEb#gJ=@wwW*cIl z6%jX-5;inQ`iGT3|K4+KZ7qNqRw&_L#4OWq947!a%Rd8XxIVA~<2Ign#}yl8);z;B zEME}?{{RO%QcOiG5n68{e}B;eUy&kougUbLH2@S@JZj%DM^Qk@+_& zrl&cEyfS1Q-_!L7K!);eyBq+ecMvh8HM(O$%&fLpZTzG0@T45u7D0h@(-m*JdaL&6Em0oYrdC*Kc zV`(e-=dN8V*tkBb6~7rUd$DEuwSG|fc)>qnFlLFzv&&W;+u+4t(a`}0!}5O}REqXK zr#x-qe9ovxTs+#QV!ajN9lQ37lt#RdL&QiOHvwDerO5b!ww3gb0S(l&YpgMAyKB~p z1*;`XSN)E>8NGdExn!pwO2Ey|*?IjTq4l0|M%O~%p#E17H9!Wy#w~oTC|3}#l=N2$ z!})%c+o#1!BQJ{j1u7x)`X<{W)lWp7v_CaeP!tdsSSme*Fndz0arP$P8_n*vHdSwy zkRKLh%I`?80C*U6$r`8ym`1!tYRPI3t8_ZP{NAPTFieyDGW&3zr7;uc z?{=k~TUQHI4Y)gX#lo|FG)%L`g5Y{O)g)WT?co=n>!Z~xkt=I5tX$M@D(vEK**Mc? zhoPW}#RzdMEkjn(uJqO3k}cZ$VZLk|FvlDq4-IyNC6`n)(Oqwg&_ifqK zsdz^GT@xerHov4E?hBQLttDSaPl=bXC;$Y`KK_OlNEZyqn)epRxA=asZZkg1%hZlR)!HHr*rd#$-41)Z&|NigVrtZ2U$Ttk#d99zwgCWA*~5NM1#X35#CSGB5&pg#t-sbJU zo#d>^|?xzxY7H#mw8@*%CI zpv7_g4PL+3pu&W}%v_hm5{*yUM%<2D>LKV}Xr51kz#Ks2iJk_xx3PQM-NZOtuL*4` zcBEIthR=_9hd3AmR9O{09@DM9D-V3uN-bkMDI`zOuj&S}gqcaBl4nNq8bginu&EWB z=oM&6gnlO>r>a3n^9aMlihDK|);Num3L$tjt`ZMLLr#abeP7EgZJ-VdEulg}Lb+}{ zLL~RgQ_SskVVE~e)%qPZCd}njRjw8)Co;xQx@Wk8)z@37>vv!N?z7ECVS_kP@=aAVuu%N8AcB|0(>th82Dvzc$p5<48zj zPgPdRp6k7SVx4IAiz1HC8B6^u(~77O*j(_LTx0Csx~pQy{4v_Ml0Wu62lvA%#C3VD zJ{21mj*;Jr#xKADtqbg@ER-okCFIIZP{BQo?>d5aE#AWt_XDI!!qv6b}I^YNqh|+ zno5!dJsilYWw()F*&%*qv^pL5Q`p;4j}TD18{M`?)e_&z$s#PTHhI$2?Yj2brA9Mo z#G#6fUx!CQ=GvWVuS(zBBzMk@v0e%;O_wTn%f~gRs9mWx&*DNLwLw;FIp^k(oEVT@ zc^>sIj)r0`qqftEi5~QADnJEg=`O3&gczIT{cL8O;do4SZ^%Sf>Qu$i7G3_|+!dKC zK_WPWS^N34M2lUWSYpmXH%QZ>ua+|ua5qxddhOu&lD}`Hh{hUr?4x2rFG8+fPF$#9 zRaB#IRVzdtbi^x;sDEZ-zIG|tS9(=m<-kxy<4Hs)WnO$Gb#*$wX~x`!OT(7*N3ngu zC`+-uLooD*!Dd2T=L;K`^Ve!~LDEiR!8YE;?WHX?ov-a%*D5+^O`%mZ+uMP9hruno zpF=|ArRgSIOKN1JFUbU6W_Xy9T;m_Z#6^e7-lV|ZqIPAB`=$Wkr=m)iniW6RF%{4? zHFO)dmxGEz+AP5eN1U z4Hra;eg0?kpzPa^|EL9yo^ZvX5Br4B{QaC|--4(j#FJQ^rF#Uf%|(N#HZ@)5l(9qY z?;oUEzP9$$_&&rGb$usvUGe$EE4eK0^@racs1Bq~W|Gd3aAfgEef#R!c#LBv(76~<}=d5;f|Cm;Ma&ht->Hs)PUj@bib z$3@!SU$zsjBl*(GWF@m7{4ng$au$l-O&U1u8H?GPP&$EKR&8FtMk3a&scDVrIp((L z66M>wJv$xqvF`soNdK&Dn-@9DTWl!0xn^=)WBoP5t=FxcCsuCHRvEE{;&m!0I`AhG zI0|Ngba&m;Yv-50T@s?$nmR0NKD^oES_FEyQPzd6|wHfNr$ zj!MlWwy3S&P+b1CYtGr_wVY7R7`pf;CC4W%^ZVS9vbpUKSyUIpH&|MajUN412S15$ zb&B?+RS;Ebafo{9^OZ`9XX`DEQ+;irbfU>&hNQ`6vh$~Q@`4tqs%JwTcs=CwB*9YP z!~h%=*8C)|#VHwKElg(L_@M(S_%*|k)ra8Z>3fn;VtZ4$Pz-yIc{47%Ky2=Ikxq;M zWxL;DfagvrP{A={h&J&Dqx8ns!77JQ+c;E!!#I1I8AouukQi@CE1TI18Eg#ABROv8 z{!VOB|J_1{UQ}1S@Q32hthQgvzfPkG^qsbg`Gpm>w;a~II7K#fuvO-fyQc!F^typ4 zx`hcx9kyx1H69k&+Lh`sEksSy7VBY4FreL?-5WRTrPun%NO_p^(RvafxBN))cG z2k#w_otZnZ;Q6C?cY5A?GDlVL;0I?%b*tK<)^yNqf)qMUSJpzm>BXOE;6#=o|*O3w?s&2v^z0=ujHDHE^0`Wc{<)ghdtO2NuN7 zsRU7Ib9|iLMC_kZgc(1oSfg48ny{*5zmjCgubSNDns?Q@+l2LtJn^RAt_dOiyxT0ncx5lu=nM6q|LN-b$*-LlZifeCTT__& z0&(^Bm+KO(S4Zx6?f&8naPfg(4&v|6d&*d#N3m~AsTbTL zf2QkQE&zCsew1VXgg1b+cVe6@&%qnX{awTOF&|j(Y=eApJ)jAGiLTcMr_ZTX+#9KFG>C%ocC@`)Nf_=Aa#H3@ za!uCsrbB%dN9i93Yeo(GA z4i$0w?p|>n-S1BG+5CZEo*0fpO;5~N4d+aN9p%S!8-`E)k3ud!g&pQb{O%xj&pLlN zU)eNS^CPvcHypTJEUV!+MC0nYJt8c!OK7qvZn1#D|fbxC9bE#NS_e?NV4HOV<*&|4$tD(PF*W`pJfbxAS_| zjl+M6I^8uM`ED_&+F4C$V%~bHYYl^{jLT1!>Sl^6JmdX8|6wuq>m;ExC zS^YT2n!nI9ILa^Q_VO);ZYS?z-p^WYkIO?Nu>^VJ@hD^YyMc^7Nac`3mp@DvSKsR` z;B&}A($)_Cl#ig_yC06yF6b*qFRjE6Ls=Q}dmdEwa6))?vg6J}$X3nEH4l$33}y&|7jPV}}Ft$W`nLtH1|4 zd6%orW<%dPC!a?|WVefLtxg_Es2?A@5v>A{>zXfYT&>L#h0dUu=MgsuLa%LLBmy+F z-0HCK5P2*e?wozTq|`wC!l^3Pnqqb`RuY%%1DhZHM04gYfoKf3?cMy2ilbu{}(UH6U`*77aRWa+1T$d6L*7=|SkPT}cH zZybt-OmSje#CwDmoji~%;SQ(3i>Wibo@gdcEf|z?W|B{O=r)o@Rp{bbS`>#Y=NiRC z)59*fjE>pAjNcd6W?%_skVI4c=R>SdP8YlmZB`fCo5I(dR1Q-@>usPa)vrvoP@|68 zk1V`LJKHl(hVMQV93!*TQTx7%Pi^UDlMM65c<_!4#Kg>rOskZTt833=?1;rJ zS&BZUA-~tp_3$McmPhRu%aF~yvMN_fR5T~G9-@xq!=21RDx`7~{ku}Co4vaY`LJvKvoY0wnuTjb= zsd&+7g6?6GklelEbL-54gc2heHrskATtr+;gylKPBBjbL0dzS>O;?<(Z*_VjV6WC} zDbUAlv)*decg4(@G(z(8L(xr3Kskw{z^{5*7oyyn(|ko-M`|Jy=s3MV~b!gD%N>Uwffm7 zzbF0Bv^%4k{uGIZg#8iEiLhTR#V};{8)ds|;#%Lo>(=DwgAI7S^%T;_c6HGwoP?xy zO~~&0SRXLtRh(x#km=o>scJ@PAm*W_?ayg)5sRosk@=f4j8{op_oihLhM5JC(j|Py z%Km5bCAAgJb@rv5CQ*#k-*sOidC*RY=O;NEa>Y1s$NESjD3RtfhEPT>jc1nQu zg5cwIdi?_lyZz|yfsBA+fY~zu<{LWJ@+`Tt+41QhZLSa3!naAGm>Tu4%>K;pZOT( zIQe~4&v#<`o~vM0YCZ)wJq>%Y{s;Rx)k~2;d)2@d*p4mpq2Q4)SG0aNLNU%WtZMUb z*jv2e&OWt%1Kr_qht11*cw)R8w@v-@Er6N(4XWCcO)?r*+g22qH##hkKymESFrAh+ z=3Z*Y{de++xqrVXzR}z7#@EbJFS@=$uMUO|uRavDBfV$FeJ-C+Ft4PZgpJw(TcA(+ zw?C=^L*{c68VZU^yk38CDNq2XIyG=;O4gj_Ox<{-7@96YWps4&$SIvf>RbHcwt}VO z`r@(Yit9Bz;Vp6N^Knl!U7P=?elo%drxs#hvPXWVgjZ5H9}KzlXcbB2%fg!RYmDy3jw=&s|`bS-#Qq@=H@`ax3{x6gd(ebUHb1D+>w(?q$~0@S|HK zFwxm`i6wC*;J5Fd*qOv|&S;2V7x*fKICoC0QXR5_Zj{Y-Ksy(LEOx`q{y%s9HHB*S zz|zu!?%WcV#h>n#o1;14Sad0#x=|UzY})K&tW+n;YfDv%Bp?)dH9cAVM#dFj1 zsB~TVj3>V;K`!(OU4%D- z``+Eoh`4;k1KX;2!k?E#;?i!^ddtaTd%Ah!dg5{nb~u%ix_;@-HkV?iV~MW~a1JZN zt59{-UERdJo{Gmu)v*A6-!8M<1P@8eJR6r#lDP%^;S%*~2i~JhPwxWjezwPn+hP=$ z;NRb`a_7(-&QT&u&#^0H)|WRJZo4(+pEdaJcSf{Sw46D&g;5s#BGRU z06^GZS5u1yF9&ld=LM~&oEwIKU^rX1fC5#5HS{%iMB?w(MU^lKo}_*H#(8G|NE2PLx$*!FAwC|Ym1F2D2Tva-LG!N$FO zAO6;A44MkpXj8*KY?N>FatbKMh~2#f)8*P?1SUlwaRgv9c}MendD@ z|M9mxHxat0>|3Y7E4D0_s6%vOT$%sbn5yii4&Yblned1%E*NxX)PG`=rW_WuaPYnY zcC3hIpMa_a7be3(>#wtzQ*rd9p{~w-%Ja5Q2}Q3O_KiLk8Ld+dYi^0E5!k!M@|-GX z1d*5!3VX2KQ5n}MM`ZpsMGKRJGk9|RS|Y^2{spo=`t3hgTGHT;`r*IbM!woLFnY&jG?#^LjkCc0;lZ7Pe06??2csqd zzfa!rLmUp8X<czc^QSB2-gx3a%Uq- zn8HT%jzV$YM#DR!)v9$HZ`W?U-`z~q;BV%6>=Y;U(DtQ&%n}oQ5V-2I*m$W`v-q{2 z8jl3$4waz>f=1Pg0N|yB1el*jGH9^}qRVYFi77);iR}C+y(Gw6EG;LVeXEe}O=i$q zv@Ef1QK~6|)A)RsK{E2;h^MQ`aCqng>wM!i!a6o@jFW3))j%-0Dz|N86sm@%@Rr5^ zF$+aP5k9r0v1vxX^UlBYTq8Rb5@bKAf4_wFDv#H@j=B-Pa7QZPGQ`pqd@AVGMR>$5RPRhlws=e4KTef{Oke8VO4fmu#}GaqoK(YXb2=;*54t1ILOzUYPSf41Hjr09K`&xnsY{X-g+M#iD)n)N z+(Owk6qDyKT{)4yh0NwX`f9OWU-D)R!k3iQmlrRw9D?ZZJff@teLEQP4 z)WQ1CgRa*^E0SQGxMRH8_ZO1|NAv~db(P-q9F3i$)5r4n6qZTrd(L^~?J1@!duGpF zmodcsGQj(6F0ayl!ZoRX((Se+uF7n(ySkuN`m`msG7zvjCm z#D1o1ie7rYmapd@c^gFq@f*<*61;i3Ibm>-d$>Bso|$UkRH{Q~chJbsA4zYy*C1Pm z=rnqy2lm#duyp7P^Y=O=C=s)_OJ}zOFeR%*W#0)L651}ajMuPu0C;cqM-SMy_)i{M zCGSQ$XT;}#1Tdt)4ML0FEBUpu5&72xjvJ3ZOp4oPx0^S5-**$ox4eiZJf7&w|ARIC z;|sG0LvPX+ZXPd0tFhAPZ=Tf)sR}lDPviUv-@a^4o~#LM24qiDHm&Mel7*&xnLg)} zZaK2@SBGb*rOR&ObJGJxn)gcx`+saRh_aLe-gk`#x_eeW-It{05e{|$3040h!C|sE zV-Z9S(c@;=C(F(2xY6rU*UdP0o$*n*^xA<^5Ds6*E?4^)gMg^_MH{Vq!(FBCdNE3yA zzJOcVFlHs~)vR8Cp>U3O4NApXE zcYSJEeoLR*%D{_o8+^!gJra+Lk8FmzobI-ygisI+JTx+z&4nbT`hO;DI#)nW!?hz! zRY#RkzSW1eerq+s1rIqAz7z19fX~4JzBDIFdp;4ou{z^dhL@!Ab+`8NNWU*&NBc_7 z;%+$7H3txVU;#1MX?&hxO&o6Y8X0AFO0J6EuOBtGGRR}%y3_wE{@P3+6?tW>R5r;Y zxq}nXL#RpH-5)g;QxH{U_VBI-`A%i|C^4=`S2pM#sfzdf6fdx~EyJ6ZD z9^@ARaujAz#;bpBzN_Fz(_$rOQQog@=IJHJ7gi{F<98B#Tw2^rX`d&HyheNVEk8o5 zxdd(5s?1S83MwRIgaTo>!$rpS@$G(b>BX~CIvc2N$>2+lo5n3&~|0CTx*!d893lk-GR==9fv@@c~xEHBk@I&H~jBRcLax|JmY4v+` zPZWt*z>SZ-7PZ$r#wb%gOF)zuKgma``&yb*8jzf64XEG;+^E{gw@jZ(JHixOJj>g; zA&NzO8n~1Sz*O^{e!nMrf&cE8G$%OBx{}{6Dz6Acth=m*P3F>i0D~zj%amK{21q)TkkMV$c>sk#G6q=D;tF!41H>@ zNeqfn3TFr=A;bHg7p4OoNPwQCbhsov2)2^)F)z1Ysb4SD(!ugBJ1!;IsW=gScLS8$QR$rnIja_^FNz5MtP>)3Jl)%rKcbhI3q+-Wxi?{k z@XVTnTnSj|MKf~2#RJL#=*<9H?vYOohb+oYzmi?zExO;y8jS~<{g_Fx6U_^UO_dVskg`tb*Y|;hbdM2$z+{c3&)^*}Twn&3 z{_D@08Xdpp*($U#y=~!Ut_Jo7r0}DcU?U6$Goe+uIcrTd_c8NXpM+sw>S1DTYzIL& z$Rhg&0?Y!V-x#S2&DK~;yrWL)PHs;b6RHO&$boeG25o6%;t>J&SJ!{hWKhB|k7I%< zyuo)o*p(^%cfVQWJRXAn z)Q)@fr$gxeua}>Lyr~}mMMENm703abt2TkcG7JyS#Yo#Ub zPO%BQBmC0`*Q}P!*+GH;r9OGuzNt4Y2bXrbEAPe*JQ-?5kf$ue`%XgKfQq#MZ>@(m z%;>%W_jzbyiiX-@;2`$-u%jzBiqMmE#x?BnZQ6Nab~`SYrIm6*GX)h{yY~5eV78vC z=qCS-U!2o0J2{;6Ux=P~tM$qu60~$n<%I45fp?8oum7>8rGchvR9b-+Mcp7rwD;#V z=W`M5^~q@&Iw9+4kF!4oCVk%ZWiM!xw3DRu6l1WZ`q)0l8(ye$4!?yydsEEhxtG!1 z#4tpeNKOyDC7)?kNs<@F)8Y9$^%&jE4!X9v(PDrrf^r-mt`dngaJJJkv@qx_%9es{IDS&Uzk+*W%%a7}4b-e3gZlNnX*B06_~5$m z;5W0rT&s@pCaf`x8@*N~k(Dpamt#tLBA|>@7-b5_&BGG&yn+ipyW2<+fp32fhX}g?vUO z7VrfpXNTfcp-H|oRq5f&UL|Nls-FuLTiypp06 zsjQGI&d3E4mh;Dzmc;R9#$V|f-P(T=U?}!~syg>aCcOWTZzfjcTF7ma%MuZlSZs4k z2t`G?Of2eclKX9&i9#+jD#>LkQn_V9?yKB$m-{vMx!>6gpY{Fi`#-$SIj`sI^*oQ~ z*1$`(KI?InZ!afJMcgXwqT9AQud=TgfZ}50(wWln)V#7l;M~-++1FE+AEmFbd0SjM znOM_(UVh*HqZux_I)3sH$802u{|T&J)1=46y%<@_hrH#Huy32B^^gC@rnAl+DFGFR z*0LA-`Ju;5NN0&_O)NM2i%p?2j`OQg=BqvR((V@lTGS@%q{a14 zON+yo5~fV6>at9P14Nv&}gu6`js+Rn>*kW?ERPgg0t|;!1 zM9p4Eyv)XvYTr7JJ$QlpbRxo!?dJK!b#_&|G<6Hv=Ld*5*MIbJ>bDG6(}~qRT_Y*~ zirytK2S)@zJ^-xnkuj1?37knDNz-Bu?jb+1B3?to^O;YM>NsMlyn8aTLl9e>c)C=_fu2*9@n}0dw|VUw+3ag6J;{6eVV5a~??WSt0k=s| zNVTEd8MjGFJHO6mhYIRHSi5vcktkC#Ezv4D1I{+}VkzUxP+mY7)SO(_re z?htB(^_11exC2vSRAFL2@J0qHnbCrBPDUJWCM^GP!eGNJxbDqcKX||U( zc94S;b8=j0^N)-(Q?EWM!OsQLGHT#|@bP{ofcJ?YF@x8GR5}NmD+b6SerDF?hNs?}8L8Ln@8EH4m-|3YqPM!Hwe?R` z+}zOjzgg)B2`lY1)Adbmr1R-V9y}x!??rCB`~EanirOzdZtVGsq5K!8KSY~P@!$iQ zc`B>=D&sIeo?FRv7lo%(wY<_cAJ8F*rnW_IvEbeNDS=bZ&`sYmc5(TD6>qjWtM2hY zO0sX%^Zy$6u+H8a{z-pOd1cAgZTslog)2=!n2n2{LHf(_al7#oCz_4@e?$w<4j*~q zU;p&^U0&ds(7VC9H+0x5-#1{%-u7`N&Cd~~wB5r}f8@Ju=`E>-kc6~~$H%g61+^6nE~s5+*3PVO^wgUx?H4YLAve+(t+U=Ufo%++;dpkOz5Lyy;|ahg zxV7(%pMmN?Od}9c4HVA+0T{wf84(uq-wOXqKYCA*#t--eVX&Jf;26(!`b&fD?WLii z$qNL~t*Pna0{oIf`Z;Id!9>@j0AiKLaj*V$A=EoItiZ`dl4djZoAq{Kv!{S@UZtcJ zi1J&(D@Q@n-e!hjv!Tq6?t3>s_W63=j_8mPsyBt{n3Ry8f{(qAa4b>i*OXtg;H|wD zlcrssO)_(fLo%E4cRrW-Pr&$k-`UuTGSR$Llk8jC+wkq3`p2o}8f0(CiMboDDqKw= zbg>0wTnSQoVSa39eSu&&{t~R*S5RLfNzzIyCcPH^)Qy)%^CO zl7d#|Uv8>Xx-2=kY+pyhhdd2hpSmS@U#~X)Rp6pE!@z&3&P=j3x$MLcOI+j7v36cH zqA_9zk+*j;)avx=&o;ctN{_4I_;W)??fL*Zpcq-K2vVpK1});n(-M!1gP~evDMv5> z$TRV`CMtlj+leA(tw^632F3SP&RiPe`EKqHfR2yC*5&u(6)F8>(Q)B`xnJBw)z945 z*njeKJnVVB{>o$2alWT({Q;4z)nX$_Rz^cqJo;d3>1;SxNB3tbz>6>luR#Vb+61C_ zW9acmBt~AYNs~cW6ZQHbN?_bc`o7yLiTd{fYa*`T$8I;qiLpj1iHMQ>j}-KRNI zHNmN^-*q;%ursRh6eQRjZzmD9EEw$F&q52%GdCBkX7Lu_v>W_`9{Gk^PJD4x@ST41xI#G%BYYTy{3zC)_0DODVr z!h1K#DrVB~G|n^WQY0!sHRCmwi8^NUd%5YF`#Q}?0dyY7Prd(v8#_q7nXKy5MDOAg z3)%bt83~$*f@#oz4nFq0tE*ul*Pa~=O!Fj68a&$vM5thXR3I?G_(gjWxudK9fy^CH z8X5-i1OpQ^ZAJJB^~sqv3Ln(uIa&w;{BjtdXyG%A-x-k@=*rf7L5{h|D0z)(nc>#z zAt?ip5n^w+lP#ud!z}9J9LnH+TQ_0pji8#Q7hxKNi`g^b1el!D^Akn#Csoxt#|8z^ z_%CkuUqdnRx`b~V^e?p9)}5~3(p^6xg_&2<1|M*8HfbQn2Ho66AKI7S!)wtncQbw7wr_bFnyEKZCINhkqNg_OH$}0=3oNMwGtrC(5;u-VwL57dmJw?g)-;9TdU7{WnFi( za?2woxMtT8%lLry&T^+T1`oSbB@cnE7woll6pBk`H5a!ovLiP7XH%*tTT3itJ}-bf|??shCy+L4~xg9+4 z9Brq)Lw32L0%DwiPf(TlCRyc1(gN+zrPZy|=e4#+l*TqcUUuIn3!=aRsz?hecm<<6kr-x5;dZfPaULKERYB*eVT?!JvDpyrs}om z0>*#W8Fgdz!^2YIN+V2ObowcnPRRGt;$>^jIK%Of6}<+zJ9{r^$6wGi8M~#0#)o`g zO9f>?=-r>9C1!&>#Ts5I=ZW3RRb1iR0svm$4X)+4D%k)4K&`9b!22jmMV6q5OU6*&S->nVMe$D5s|7Np~j~!Oo)fQ5| zMegtAmcaxAy1B}W-P8O+8sVM5r{LIb1;Ptw3bT%9ElLbCNWKNBfn^(ue*;=V6uEDc!huBBD!Vpc%QpJCn}OG~UoG zc_Dn*(eT{U1gCar0^hiDsAA=YQ>VyzPhL!<%ow#4pX8ptZCs%#23cQ9M=sNc_k3F} zFUcM@*f^^y59dhQu#c$~rwBdyX`zZW=eFSP=uY*{7>yS?l&hn+M$NSng1UiT1AhN* zP>esEHAa?cEKSLW>v;_VFmweT!2Ac!+Wj+9w0Qh0Mh<#^{;HgSG%Zgx-rH+3Tu~~8 zOy>`z^pd&DLtDMa3(1`8tfrNJ#Fd2{{=2HyAWXILau@bv^T9UebSCL;uqr0X%_03G2f z8=h9wy1pG3LU5==vD3d^p#!~(U(i!>w81y1T@BJr&d+q4d7&SpaE zj(*U$q5>(;^>YS0y&^ie_x={m)D%dB2_f*%jCZE+!DXH1E?ldq+H3=q#Zz;v>#Ri_ z?eAq?i#vtzd@yVKD($03w{Ip1v-$Z%;}ILc&#H!V!06Vp)_X^Xt3)#?R`G)~2l=3& zK5wpoLE>}EUKPcj><#Jwl)r!AY9*16A_w63PUai`1VJ6c#CG?mfq}{fz}2r&K4*%_ z-cW%+?O4NQrn(ot%K5!E?UGw0Lz};4jj;0E$ zP8ssjKN!I?e=)y_R9eVC@6{1%hTR=z{QcGyq#;UHU#q(Av0zm>*MV*sG0J$GqhFn6 ztKFgsxpPnOyE$k6K~j)Ztf`CPcw*mljqR7;_H!YEQwHv|xzjo{ciYk}IZ(tl z#cTT#kO@GpT;|caPLM!a+o~zVIrV5XygZ&GD^(|}dN37xrpWqwe-2V2VQpS%6a_z3 zK4Ll`_Ap_6uoAIRGZ*Qpu(;Xc5CI_RJ|m!A+E+l+re+1bT;@k97zt& zY|`9rf?v{DOSVYMjqMd$mwn87%^4NF@$e1NO^z;c0RCXeF34UgIIL!$sRjFD;gJ z{DFWGY$DqbKQCb%1nKvYrWT}2 zBFP+%zX_b7`|3kJZhdGzN5RHVSqz+o=ON?r3<{`5D@k{XN3S zWd!VAW0=^ff;Sf*BV>*U{$M@g^^s4+<7?mI`z9u)t}fyx<3q6R@GetI5S$?&$I=TY zL#RSE0^)0WZwB7_cIm>_F?btOpR&s$Q8n{hA1VS`3GPyTl#`*tvR|*YM}^)SdF;Z8 zrD@nnl$%vrOwV5Z<#H}iwEl7wJXu#{kTul!3UEYlHK&DbS4qa1%!2F(<4+$Nh8f+t z0A0eH)*xucA3-$3ufxBYXEvUxWMrwLn&S{cfzH?Rnb!p36J53N)W3;HJ!kDrQGQIg z*}fgfi6kz2cfPx+PaO{icHA<8j8Ewk`46@zV28;m;aI;Z97 zRW4fy`9%2=2ALT5Oe~(0_je;pGwo;lBv%FmOIorLwI@sY8kOprR#fh;SWc?&6{R!Y zniCiw9Y~$)hAE8kBsYdSett_(bSgF-vuntG`c>S(LAisV=4FoP%~XlPp2Mq#w3|p8 z^)4uqHf|$;?GVzE2pBUzy>j^h?gIT)0d9H&>gb(~bbJ`LW6Z2t!LMvk0tBH-!#y%V58Wy;+$)#fbEPl(F@kRRGy{Z(@cp4#&5FDIMx}Rv$`1SP(XJpY{Nx)5qV{_LC9^2 z<{_?FZ2xLkspy>PqLgWoh4j)_i_zgV4~j}o%3cd|SJ|chIZ-QF?M^tK@spRr#VFE14fvU0`PUgubmtZ0Fk`k5wEe$Q}MB+5Z2@nX@()F3jjz0pb3BR zX{va-sl@n>TayL>+R;R!nmOp~&CBfo>ldvYrwhpqk7!i?v;F@3x3u9K=sN*EGo_xy zj4`TOn-7L0X-nfli6j9M2qgKrHrKvu(G6xj?S$+K&$*rL(i*(1BFl?8c{77xE??Vv z$=>^V`5ljRVu5DD%1$`zC9STmPUIsi#ksfGv)PH5o0ihJ2DGWTYRSN*U2J#9M5t{1 zVZGt@Zb}zzY2{DBCMIqA7Lazj>|_-#2nq&cj@?wt1{V^NvhEhxoHn&oA7u16UxZoq zBJ{El3?GRUzpY*}BG1#gN1C1vQB|mEJ{jtLnq4nio%iq_cANl5iDHYzeE!fkX0{Tm zyZsG_68wwm!-Kt2h=9QfoRcB19q4@$X>3bZQ`>JnR+&=14<4_ii>?A9Q)}aRC5cs; zW>VwprfuMeV3-8)I+~a(^#+h&T{VB(c3Y`MI`H$(ZnO?_sy-|BoF~A5%&$@D_Vc-? zCcV?>%0!RH)|4tf)(Eo0XixvX3S3(Q$2~68b=kE*+EWke_P|%3J>8vdm@+lpcedn49p|jzmCB*wf8orhbs0-axQwfih zCYh4?)iJI$EvMc)*+b86%AA_e-f@%O687JZxrE1dsb>93h2}30Sa65b#U7>!B(y5n z`}}70jlsJ^vQCCB{CkBK4BVTB-+=pZz}T)CUe3YPp5ii|$Y2sVRb^LhN#1$fsBPw) zf5IxGJ%k|3<-;9E@Z23(J5$dObcnHv0>s(D`)~3)elfjlNknPgpAdZ#4-lrUKDwtf zPX5-dOEK~o^<~t;5QTzjHLJ>xJX&bo0sV<{3B}ecNx^k8=Q&IA)8q`ccg@Z5O*&PB ze>RQ+ry>q}(j00Kt*-dGn>DzbS53(B&IX!kL1MqK(Pn;BS372WQgPbbd8HB`8;Ly9 zK)n`I+=_4);-sC;yye2vsSA!5nKG&nmiAm7Z6mZQAQv823-TJs>-AJ#Mbw#~w+6CZ ztC!R9ZnffH6>1x`uRh(?HE+&t&huh*CYf#1?>;gdhYQ+d&cbins$W*%K=mrPIhWon z?F3Elf6QHuMzbdT)c<juX_uLp!Yg(cv zH;eV<@HX7`SDr-pm%!`ox7XR|S}ex;|5`T@dYSp3*DbM8G|hLr1)f1())^1OO8$a= z1Ni1Rp0ZWrl_6&_jNwS?krtO&O{lg{N0h!UCXbHLMs_Z4Q-wc9x^+HjA4V;$TYEbg)r8!i}&V*6J-( z!Gc06*B1kxRsWA2C0Qhs@j)xt||$%DXT;? z3jsvLY^Gd5Jv)ccln0!*r1UoBaEC8aZ)#{{a1bkREJ{(X_%CB?k7?2^LNw-_8s7XN zZymH2O$th_a>e;D>P8!1Bd$HA3THcAN~#}EKmNG{Wg{gezqIu^*X*v;Pgz-Ga(>y^ zgFZ~r^x*IYiLcPwn!>dI=!#B?=}&IIn_X^Y32%HWTYcA>su9!@T6UhhH`zBcuApJD z1Ln{h6R6?3XhZQaS-iFEY=V^P{Tk4!UgtrGsgLtfj{C4L+CZ>f*@ z?&?@`9-yx}5`VR0|DYrRM%VrTJyw<~m)t#uu0S-T1&^$v)gh<`sWHb{ZRMb^PG}NMBDz_{iShZ zqgB-}P3$Xhaqcf2n;R8l0M3Wz?U-^H;6opKEz8<5-m9d|&s;1mRLrS3 zo0TNxoPsYiF_*Z@j2-B2dK+BWv^?7240jU^nR?YW;i8?LSzilVXsk?8+R$#0gS5tK zsqw*(jo0AY+q;GZe$JODLXcLi{k&gJWhCGgbF~3i+hNz5IoV(6u@?Q|Tn{gw!#96%@l9mAyB-ADx6SrS^s74IthE{l>?CtEZ z&_Zq%FJ=>s-lnO$hKvZN_fA4HP}z`ikixa6wIVd2Sme*tIZkt|kpTc~KM#nsm;uHe zuqHsX-kHRjZfHr*tIdtmo`en1Um2eBz(mjZX-ZHOp8@Z?ghs&m?5o&SRlrFLKVm_q zG^(pEA1evW(D3aun*u%T>r!OyLfbH{KNj14@$b98#|9z`8tSdFEoiwu4;?KnL5nia zu1k9l+v4w+2nucIk!EUfk7UQ9B&F&;i>(ic325w(Wb0QY8Xnbk+V~RS*4;4ZNXadf zu_607C#*Zaw0L}Sq5TCdSuo(Q%zX0G9QKsOERx11_-<01^$&zsvH&MjDvl2*@P2b& z1sl81$FWPdhcTpH&Q0C{JsI@@yt;x^iowuoy%@Z5HJG z!Y!_VHQ`ghGgyGo{+~xijzsuS4}QURY!J8c%0n(DWU*^)F#uPGkn%Z2DLnFRGcw7F zn>1Yu$|#gska(jQ>#P9xG2Q%_=>FNOLTjU4c_FAe`$d&j8TTAc7+t;mV0)(s*SGnH zqympE4D7sPK0~Djblh)iJ=&!h9mqx*N$myiw*W15lP>iJd3iX*`fu;4+hgaAkmy`l zw_eE{(#2_O`1bT7q^2vXaiigFuEn&CXvi9wgquR|l=tqxpcR7c4xPKzT66GhWenwT z{zYigBr}r!5!d`dij+_I;$g0pn|AhBXmx^GofR5Un@{3mZw8`^Lt2C&1qX}7hvTiqK@*-j!v z35&HRKZI|HiSbIVRb`to_O16uwBsA2z-{?4i=G^0pj+;)tv^{N$Yno?-eXSkjlE%d ztzt_v+Z%6MaOvFRO~%enD$;_~T^n4#rCRi7VEmuf`FE@Gak|_t0G|l;6yJz*O%I%* z2mn>j9OYP=BEge~?dBs`VlWAuxvN?S+A`hrgvJjy7LaOLDD*2~ z0rzFuM$vC(wBIFdb8+i-QX5tVK4ZgsIyx_hDd+tuwL$key1rR8T(4!IgI~?X?RE0j z7nqUsU$xTS^q`GT2Df68D3;z=7PS0YEeKhO6#dz%$=@InB+-89|Bb`_1jTB2S^kqs zbVwzBZ@c8(P>lN`FwRY)2GZd@=$uGk7khacCwn{Z5A7)q&TMEB^a9EWutU62?;wpj zr2`%sE~@~|$Ybj$hZG}-Aed54mRcX2vs%BcxIwbVw65^#?2d<}n4x}KuZU_b0f+Gu zSL-ppIP24@n;dXobAE?yYLdIBC1q)`+?w>V&_j9fh1Ha7nbYJ!-F5oq>>3lHE46w; z&dxgWg%o$srM1K|cDwY*A%{hE`BSXY)#S(dvZnWyR`T@LC8HTrW9}Ne#H82?2G(AK z9d4s|3SqbuQ$4KMUJ^9CUI~` z6!BhnSbJfO&6ai$IGBPV%XCh!FR8#}mb$ebH(=Cov>o)p zuBOvl*)eC|kuBu3#NPINJ-zx{P)uUQo}{zgKNhzWgm@^-C2i@*2dl!nwY{Rw=sFuvj8AThklSr#KV@j>P1MAid?qo=@fS4N^1JIwm>%d1INFerAKqFf!5XnVne8efn4od4JxNJ1~}fZj8*VM zj)bs-6QWt)o1D(6WXpLac#0QjHqVTSe>pf|pS1OEY*5f*qfym6ek?e7eoIYcfm0Wj zBR57r9wLv=!YGwCuW<)X=x6JBhv3T#4yz^AcIRxeLlDe!Ka2R=%Gtv&_JrePeovs` z`0K-#bt&hf!-c}aG4~=alY}mq_X;Cmv-|3@8mZt($aWEBgs|D}u?OHGa!3+GtMeb! zP>1&rM28T^cV?$k>`7#d9}q*Le)~PekgwpfAufE*P9HQem>gLl>}-pLA!C0yUoI!w z!uvlK34Q%7?~db4Fpug=!cQ+Ix@k#aTx=)wjMSNB2WBSqZg#j!+$^KPKbO$iOjGWO z|5DBGwDgq0w@X@z6Gc4TR7i9z{-oQ^jKb;O=vDgXvC-{=bk#-@M(6U1HT_gMMS28T zIr?|tXUPFe{Pb!{aTrUS>)WkdFbi$qi2b9=IM}H!M*Rn{OQSxIndqR%_DV_+x~b6-B?T%j`vaWuzM>+a~qB>#}|vx;Fx|EGy<+ zqu<9UFtFT>$m|H)U42Eu6^m?Mk?&C*%cYpde2Wdv)g5#zO#r>qFpBXK7#d$^>9+Xl{_N=lJx0@;9ie literal 0 HcmV?d00001 diff --git a/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx b/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx new file mode 100644 index 000000000000..52a91614eb9f --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { OsdsBreadcrumb } from '@ovhcloud/ods-components/react'; +import { useParams } from 'react-router-dom'; +import { + usePciBreadcrumb, + BreadcrumbItem, +} from '@/hooks/breadcrumb/useBreadcrumb'; +import { useProject } from '@/data/hooks/pci/useProject'; +import appConfig from '@/pci-ai-endpoints.config'; + +export interface BreadcrumbProps { + customRootLabel?: string; + appName?: string; + items?: BreadcrumbItem[]; +} + +function Breadcrumb({ customRootLabel }: BreadcrumbProps): JSX.Element { + const label = customRootLabel || appConfig.rootLabel; + + const { projectId } = useParams(); + const { project } = useProject({ projectId }); + + if (project) { + const breadcrumbPci = usePciBreadcrumb({ + projectId, + appName: 'pci-ai-endpoints', + }); + return ; + } +} + +export default Breadcrumb; diff --git a/packages/manager/apps/pci-ai-endpoints/src/components/Error/Error.scss b/packages/manager/apps/pci-ai-endpoints/src/components/Error/Error.scss new file mode 100644 index 000000000000..c73220cd3be3 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/components/Error/Error.scss @@ -0,0 +1,18 @@ +.manager-error-page { + margin-left: auto; + margin-right: auto; + max-width: 600px; + width: 100%; + display: grid; + height: 100%; + overflow: hidden; + .manager-error-page-image { + img { + width: 100%; + } + } + .manager-error-page-footer { + text-align: right; + overflow: hidden; + } +} diff --git a/packages/manager/apps/pci-ai-endpoints/src/components/Error/Error.tsx b/packages/manager/apps/pci-ai-endpoints/src/components/Error/Error.tsx new file mode 100644 index 000000000000..66f2a411a7fa --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/components/Error/Error.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { ShellContext } from '@ovh-ux/manager-react-shell-client'; +import { + ErrorMessage, + TRACKING_LABELS, +} from '@ovh-ux/manager-react-components/src/components/'; +import { ErrorBanner } from '@ovh-ux/manager-react-components'; + +interface ErrorObject { + [key: string]: any; +} + +function getTrackingTypology(error: ErrorMessage) { + if (error?.detail?.status && Math.floor(error.detail.status / 100) === 4) { + return [401, 403].includes(error.detail.status) + ? TRACKING_LABELS.UNAUTHORIZED + : TRACKING_LABELS.SERVICE_NOT_FOUND; + } + return TRACKING_LABELS.PAGE_LOAD; +} + +const Errors: React.FC = ({ error }) => { + const navigate = useNavigate(); + const location = useLocation(); + const { shell } = React.useContext(ShellContext); + const { tracking, environment } = shell; + const env = environment.getEnvironment(); + + React.useEffect(() => { + env.then((response) => { + const { applicationName } = response; + const name = `errors::${getTrackingTypology(error)}::${applicationName}`; + tracking.trackPage({ + name, + level2: '81', + type: 'navigation', + page_category: location.pathname, + }); + }); + }, []); + + return ( + navigate(location.pathname, { replace: true })} + onRedirectHome={() => navigate('/', { replace: true })} + /> + ); +}; + +export default Errors; diff --git a/packages/manager/apps/pci-ai-endpoints/src/components/Loading/Loading.tsx b/packages/manager/apps/pci-ai-endpoints/src/components/Loading/Loading.tsx new file mode 100644 index 000000000000..1f8e45b3d2ed --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/components/Loading/Loading.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { OsdsSpinner } from '@ovhcloud/ods-components/react'; + +export default function Loading() { + return ( +
+
+ +
+
+ ); +} diff --git a/packages/manager/apps/pci-ai-endpoints/src/data/api/pci-ai-endpoints.ts b/packages/manager/apps/pci-ai-endpoints/src/data/api/pci-ai-endpoints.ts new file mode 100644 index 000000000000..b1639a2c2ac4 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/data/api/pci-ai-endpoints.ts @@ -0,0 +1,17 @@ +import { fetchIcebergV2, apiClient } from '@ovh-ux/manager-core-api'; + +export type GetpublicCloudProjectProjectIdParams = { + /** Project ID */ + projectId?: any; +}; + +export const getpublicCloudProjectProjectIdQueryKey = ( + params: GetpublicCloudProjectProjectIdParams, +) => [`get/publicCloud/project/${params.projectId}`]; + +/** + * Manage Public Cloud projects : Get details on a Public Cloud project + */ +export const getpublicCloudProjectProjectId = async ( + params: GetpublicCloudProjectProjectIdParams, +): Promise => apiClient.v2.get(`/publicCloud/project/${params.projectId}`); diff --git a/packages/manager/apps/pci-ai-endpoints/src/hooks/breadcrumb/useBreadcrumb.tsx b/packages/manager/apps/pci-ai-endpoints/src/hooks/breadcrumb/useBreadcrumb.tsx new file mode 100644 index 000000000000..11f507284c78 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/hooks/breadcrumb/useBreadcrumb.tsx @@ -0,0 +1,89 @@ +import { useEffect, useState, useContext } from 'react'; +import { useLocation } from 'react-router-dom'; +import { ShellContext } from '@ovh-ux/manager-react-shell-client'; +import { useProject, PciProject } from '@/data/hooks/pci/useProject'; + +export type BreadcrumbItem = { + label: string | undefined; + href?: string; +}; + +export interface BreadcrumbProps { + rootLabel?: string; + appName?: string; + projectId?: string; + items?: BreadcrumbItem[]; +} +export const usePciBreadcrumb = ({ + projectId, + appName, + items, +}: BreadcrumbProps) => { + const { shell } = useContext(ShellContext); + const [root, setRoot] = useState([]); + const [appRoot, setAppRoot] = useState([]); + const { project } = useProject({ projectId }); + + useEffect(() => { + const { description } = project as PciProject; + const fetchRoot = async () => { + try { + const response = (await shell?.navigation.getURL( + 'public-cloud', + `#/pci/projects/${projectId}`, + {}, + )) as string; + const rootItem = { + label: description, + href: response as string, + }; + setRoot([rootItem]); + setAppRoot([ + { + label: appName, + href: response.split('public-cloud')[1], + }, + ]); + } catch (error) { + console.error('Error fetching root URL:', error); + } + }; + if (project) fetchRoot(); + }, [project]); + + return [...root, ...appRoot]; +}; + +export const useBreadcrumb = ({ rootLabel, appName }: BreadcrumbProps) => { + const { shell } = useContext(ShellContext); + const [root, setRoot] = useState([]); + const [paths, setPaths] = useState([]); + const location = useLocation(); + const pathnames = location.pathname.split('/').filter((x) => x); + + useEffect(() => { + const fetchRoot = async () => { + try { + const response = await shell?.navigation.getURL(appName, '#/', {}); + const rootItem = { + label: rootLabel, + href: String(response), + }; + setRoot([rootItem]); + } catch { + // Fetch navigation error + } + }; + fetchRoot(); + }, [rootLabel, appName, shell?.navigation]); + + useEffect(() => { + const pathsTab = pathnames.map((value) => ({ + label: value, + href: `/#/${appName}/${value}`, + })); + setPaths(pathsTab); + }, [location]); + + return [...root, ...paths]; +}; diff --git a/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx b/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx new file mode 100644 index 000000000000..2971e624318e --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx @@ -0,0 +1,100 @@ +import { useContext, useEffect, useState } from 'react'; +import { CountryCode } from '@ovh-ux/manager-config'; +import { ShellContext } from '@ovh-ux/manager-react-shell-client'; + +const docUrl = 'https://docs.ovh.com'; + +type GuideLinks = { [key in CountryCode]: string }; + +const GUIDE_LIST: { [guideName: string]: Partial } = { + guideLink1: { + DE: '/update-path', + ES: '/update-path', + IE: '/en/update-path', + IT: '/update-path', + PL: '/update-path', + PT: '/update-path', + FR: '/update-path', + GB: '/update-path', + CA: '/update-path', + QC: '/update-path', + WE: '/update-path', + WS: '/update-path', + US: '/update-path', + }, + guideLink2: { + DE: '/guide-link-2-path', + ES: '/guide-link-2-path', + IE: '/en/guide-link-2-path', + IT: '/guide-link-2-path', + PL: '/guide-link-2-path', + PT: '/guide-link-2-path', + FR: '/guide-link-2-path', + GB: '/guide-link-2-path', + CA: '/update-path', + QC: '/update-path', + WE: '/update-path', + WS: '/update-path', + US: '/update-path', + }, + guideLink3: { + DE: '/guide-link-3-path', + ES: '/guide-link-3-path', + IE: '/en/guide-link-3-path', + IT: '/guide-link-3-path', + PL: '/guide-link-3-path', + PT: '/guide-link-3-path', + FR: '/guide-link-3-path', + GB: '/guide-link-3-path', + CA: '/update-path', + QC: '/update-path', + WE: '/update-path', + WS: '/update-path', + US: '/update-path', + }, + /* + addNewGuideLink : { + DEFAULT: '/guide-link-3-path', + DE: '/guide-link-3-path', + ES: '/guide-link-3-path', + ... + } + */ +}; + +type GetGuideLinkProps = { + name?: string; + subsidiary: CountryCode | string; +}; + +function getGuideListLink({ subsidiary }: GetGuideLinkProps) { + const list: { [guideName: string]: string } = {}; + const keys = Object.entries(GUIDE_LIST); + keys.forEach((key) => { + list[key[0]] = docUrl + GUIDE_LIST[key[0]][subsidiary as CountryCode]; + }); + return list; +} + +interface GuideLinkProps { + [guideName: string]: string; +} + +function useGuideUtils() { + const { shell } = useContext(ShellContext); + const { environment } = shell; + const [list, setList] = useState({}); + + useEffect(() => { + const getSubSidiary = async () => { + const env = await environment.getEnvironment(); + const { ovhSubsidiary } = env.getUser(); + const guideList = getGuideListLink({ subsidiary: ovhSubsidiary }); + setList(guideList); + }; + getSubSidiary(); + }, []); + return list as GuideLinkProps; +} + +export default useGuideUtils; diff --git a/packages/manager/apps/pci-ai-endpoints/src/index.scss b/packages/manager/apps/pci-ai-endpoints/src/index.scss new file mode 100644 index 000000000000..65dd5f63a7df --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/index.scss @@ -0,0 +1 @@ +@tailwind utilities; diff --git a/packages/manager/apps/pci-ai-endpoints/src/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/index.tsx new file mode 100644 index 000000000000..0342fb7c826a --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/index.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { + ShellContext, + initShellContext, + initI18n, +} from '@ovh-ux/manager-react-shell-client'; +import App from './App'; +import '@ovhcloud/ods-theme-blue-jeans/dist/index.css'; +import './index.scss'; +import './vite-hmr'; + +import { UNIVERSE, SUB_UNIVERSE, APP_NAME, LEVEL2 } from './tracking.constant'; + +const trackingContext = { + chapter1: UNIVERSE, + chapter2: SUB_UNIVERSE, + chapter3: APP_NAME, + appName: APP_NAME, + pageTheme: UNIVERSE, + level2Config: LEVEL2, +}; + +const init = async (appName: string) => { + const context = await initShellContext(appName, trackingContext); + + await initI18n({ + context, + reloadOnLocaleChange: true, + defaultNS: appName, + ns: ['listing', 'dashboard', 'onboarding'], + }); + + const region = context.environment.getRegion(); + context.shell.tracking.setConfig(region, LEVEL2); + try { + await import(`./config-${region}.js`); + } catch (error) { + // nothing to do + } + + ReactDOM.createRoot(document.getElementById('root')!).render( + + + + + , + ); +}; + +init('pci-ai-endpoints'); diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/404.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/404.tsx new file mode 100644 index 000000000000..d052f1ebcbbf --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/pages/404.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +export default function NotFound() { + // @TODO: add a redirection here in order to catch /:serviceName given from iframe + + return

404 - route not found

; +} diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/general-informations/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/general-informations/index.tsx new file mode 100644 index 000000000000..a785e6d2ff0f --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/general-informations/index.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +function GeneralInfos() { + return
Information Générales Tab
; +} + +export default GeneralInfos; diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/index.tsx new file mode 100644 index 000000000000..1eb591f9842a --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/index.tsx @@ -0,0 +1,90 @@ +import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + Outlet, + NavLink, + useLocation, + useNavigate, + useParams, + useResolvedPath, +} from 'react-router-dom'; +import { + OsdsTabs, + OsdsTabBar, + OsdsTabBarItem, +} from '@ovhcloud/ods-components/react'; + +import { BaseLayout } from '@ovh-ux/manager-react-components'; + +import Breadcrumb from '@/components/Breadcrumb/Breadcrumb'; + +export type DashboardTabItemProps = { + name: string; + title: string; + to: string; +}; + +export type DashboardLayoutProps = { + tabs: DashboardTabItemProps[]; +}; + +export default function DashboardPage() { + const [panel, setActivePanel] = useState(''); + const { serviceName } = useParams(); + const location = useLocation(); + const navigate = useNavigate(); + const { t } = useTranslation('dashboard'); + + const tabsList = [ + { + name: 'general_informations', + title: 'Informations générales', + to: useResolvedPath('').pathname, + }, + { + name: 'Tab 2', + title: 'Tab 2', + to: useResolvedPath('Tab2').pathname, + }, + ]; + + useEffect(() => { + const activeTab = tabsList.find((tab) => tab.to === location.pathname); + if (activeTab) { + setActivePanel(activeTab.name); + } else { + setActivePanel(tabsList[0].name); + navigate(`${tabsList[0].to}`); + } + }, [location.pathname]); + + const header = { + title: t('title'), + }; + + return ( + } + header={header} + description="Description du pci-ai-endpoints" + tabs={ + + + {tabsList.map((tab: DashboardTabItemProps) => ( + + + {tab.title} + + + ))} + + + } + > + + + ); +} diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/tab2/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/tab2/index.tsx new file mode 100644 index 000000000000..9cb100623734 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/tab2/index.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +function Tab2() { + return
Tab 2
; +} + +export default Tab2; diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/index.tsx new file mode 100644 index 000000000000..d341b8a602bf --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/pages/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +export default function PciAiEndpoints() { + const { t } = useTranslation('pci-ai-endpoints'); + + return ( +
+

{t('title')}

+
Start your application
+
+ ); +} diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/layout.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/layout.tsx new file mode 100644 index 000000000000..dc85c83ab2fb --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/pages/layout.tsx @@ -0,0 +1,31 @@ +import React, { useEffect, useContext } from 'react'; +import { defineCurrentPage } from '@ovh-ux/request-tagger'; +import { Outlet, useLocation, useMatches } from 'react-router-dom'; +import { + useOvhTracking, + useRouteSynchro, + ShellContext, +} from '@ovh-ux/manager-react-shell-client'; + +export default function Layout() { + const location = useLocation(); + const { shell } = useContext(ShellContext); + const matches = useMatches(); + const { trackCurrentPage } = useOvhTracking(); + useRouteSynchro(); + + useEffect(() => { + const match = matches.slice(-1); + defineCurrentPage(`app.pci-ai-endpoints-${match[0]?.id}`); + }, [location]); + + useEffect(() => { + trackCurrentPage(); + }, [location]); + + useEffect(() => { + shell.ux.hidePreloader(); + }, []); + + return ; +} diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/listing/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/listing/index.tsx new file mode 100644 index 000000000000..a7fb6e8735e8 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/pages/listing/index.tsx @@ -0,0 +1,117 @@ +import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useParams, useNavigate, useLocation } from 'react-router-dom'; + +import { OsdsLink } from '@ovhcloud/ods-components/react'; +import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming'; +import { + BaseLayout, + Datagrid, + DataGridTextCell, + useResourcesIcebergV2, +} from '@ovh-ux/manager-react-components'; + +import Loading from '@/components/Loading/Loading'; +import ErrorBanner from '@/components/Error/Error'; +import Breadcrumb from '@/components/Breadcrumb/Breadcrumb'; + +import appConfig from '@/pci-ai-endpoints.config'; +import { urls } from '@/routes/routes.constant'; + +export default function Listing() { + const { t } = useTranslation('listing'); + const myConfig = appConfig; + const serviceKey = myConfig.listing?.datagrid?.serviceKey; + const [columns, setColumns] = useState([]); + const navigate = useNavigate(); + const location = useLocation(); + const { projectId } = useParams(); + const { + data, + fetchNextPage, + hasNextPage, + flattenData, + isError, + isLoading, + sorting, + setSorting, + error, + status, + } = useResourcesIcebergV2({ + route: `/publicCloud/project/${projectId}`, + queryKey: ['pci-ai-endpoints', `/publicCloud/project/${projectId}`], + }); + + const navigateToDashboard = (label: string) => { + const path = + location.pathname.indexOf('pci') > -1 ? `${location.pathname}/` : '/'; + navigate(`${path}${label}`); + }; + + useEffect(() => { + if (status === 'success' && data?.pages[0].data.length === 0) { + navigate(urls.onboarding); + } else if (status === 'success' && data?.pages.length > 0 && !flattenData) { + const tmp = Object.keys(data?.pages[0].data[0]) + .filter((element) => element !== 'iam') + .map((element) => ({ + id: element, + label: element, + cell: (props: any) => { + const label = props[element] as string; + if (typeof label === 'string' || typeof label === 'number') { + if (serviceKey === element) + return ( + + navigateToDashboard(label)} + > + {label} + + + ); + return {label}; + } + return
-
; + }, + })); + setColumns(tmp); + } + }, [data]); + + if (isError) { + return ; + } + + if (isLoading && !flattenData) { + return ( +
+ +
+ ); + } + + const header = { + title: t('title'), + }; + + return ( + } header={header}> + + {columns && flattenData && ( + + )} + + + ); +} diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.scss b/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.scss new file mode 100644 index 000000000000..995e5c3b0be6 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.scss @@ -0,0 +1,10 @@ +.tile-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-gap: 30px; + padding-top: 3rem; + + @media (min-width: 1024px) { + grid-template-columns: repeat(3, 1fr); + } +} diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.tsx new file mode 100644 index 000000000000..93e59c16a128 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Card, OnboardingLayout } from '@ovh-ux/manager-react-components'; +import useGuideUtils from '@/hooks/guide/useGuideUtils'; +import Breadcrumb from '@/components/Breadcrumb/Breadcrumb'; +import onboardingImgSrc from './onboarding-img.png'; + +export default function Onboarding() { + const { t } = useTranslation('onboarding'); + const link = useGuideUtils(); + + const tileList = [ + { + id: 1, + texts: { + title: t('guide1Title'), + description: t('guide1Description'), + category: t('guideCategory'), + }, + href: link?.guideLink1, + }, + { + id: 2, + texts: { + title: t('guide2Title'), + description: t('guide2Description'), + category: t('guideCategory'), + }, + href: link?.guideLink2, + }, + { + id: 3, + texts: { + title: t('guide3Title'), + description: t('guide3Description'), + category: t('guideCategory'), + }, + href: link?.guideLink3, + }, + ]; + + const title: string = t('title'); + const description: string = t('description'); + const imgSrc = { + src: onboardingImgSrc, + }; + + return ( + <> + + + {tileList.map((tile) => ( + + ))} + + + ); +} diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/onboarding-img.png b/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/onboarding-img.png new file mode 100644 index 0000000000000000000000000000000000000000..1ac8d6473c95008e4a957539d3567870852b874e GIT binary patch literal 8250 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 KUaD&K;y(a7f*-B` literal 0 HcmV?d00001 diff --git a/packages/manager/apps/pci-ai-endpoints/src/pci-ai-endpoints.config.ts b/packages/manager/apps/pci-ai-endpoints/src/pci-ai-endpoints.config.ts new file mode 100644 index 000000000000..2ff8b25a2bdc --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/pci-ai-endpoints.config.ts @@ -0,0 +1,8 @@ +export default { + listing: { + datagrid: { + serviceKey: 'pci-ai-endpoints', + }, + }, + rootLabel: 'pci-ai-endpoints', +}; diff --git a/packages/manager/apps/pci-ai-endpoints/src/routes/routes.constant.ts b/packages/manager/apps/pci-ai-endpoints/src/routes/routes.constant.ts new file mode 100644 index 000000000000..f24e2c6c71f3 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/routes/routes.constant.ts @@ -0,0 +1,7 @@ +export const urls = { + root: '/', + onboarding: 'onboarding', + listing: '', + dashboard: ':serviceName', + tab2: 'tab2', +}; diff --git a/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx b/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx new file mode 100644 index 000000000000..2e1325b24596 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { RouteObject } from 'react-router-dom'; +import { PageType } from '@ovh-ux/manager-react-shell-client'; +import NotFound from '@/pages/404'; +import { urls } from '@/routes/routes.constant'; + +const lazyRouteConfig = (importFn: CallableFunction): Partial => { + return { + lazy: async () => { + const { default: moduleDefault, ...moduleExports } = await importFn(); + return { + Component: moduleDefault, + ...moduleExports, + }; + }, + }; +}; + +export const Routes: any = [ + { + path: '/pci/projects/:projectId/ai-endpoints', + ...lazyRouteConfig(() => import('@/pages/layout')), + children: [ + { + id: 'listing', + path: urls.listing, + ...lazyRouteConfig(() => import('@/pages/listing')), + handle: { + tracking: { + pageName: 'listing', + pageType: PageType.listing, + }, + }, + }, + { + path: urls.dashboard, + ...lazyRouteConfig(() => import('@/pages/dashboard')), + children: [ + { + id: 'dashboard', + path: '', + ...lazyRouteConfig(() => + import('@/pages/dashboard/general-informations'), + ), + handle: { + tracking: { + pageName: 'dashboard', + pageType: PageType.dashboard, + }, + }, + }, + { + id: 'tab2', + path: 'Tab2', + ...lazyRouteConfig(() => import('@/pages/dashboard/tab2')), + handle: { + tracking: { + pageName: 'tab2', + pageType: PageType.dashboard, + }, + }, + }, + ], + }, + { + id: 'onboarding', + path: urls.onboarding, + ...lazyRouteConfig(() => import('@/pages/onboarding')), + handle: { + tracking: { + pageName: 'onboarding', + pageType: PageType.onboarding, + }, + }, + }, + ], + }, + { + path: '*', + element: , + }, +]; diff --git a/packages/manager/apps/pci-ai-endpoints/src/tracking.constant.ts b/packages/manager/apps/pci-ai-endpoints/src/tracking.constant.ts new file mode 100644 index 000000000000..a8423b9d8b79 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/tracking.constant.ts @@ -0,0 +1,20 @@ +export const LEVEL2 = { + EU: { + config: { + level2: '86', + }, + }, + CA: { + config: { + level2: '86', + }, + }, + US: { + config: { + level2: '86', + }, + }, +}; +export const UNIVERSE = 'PublicCloud'; +export const SUB_UNIVERSE = 'PublicCloud'; +export const APP_NAME = 'pci-ai-endpoints'; diff --git a/packages/manager/apps/pci-ai-endpoints/src/vite-hmr.ts b/packages/manager/apps/pci-ai-endpoints/src/vite-hmr.ts new file mode 100644 index 000000000000..473d87630039 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/vite-hmr.ts @@ -0,0 +1,5 @@ +if (import.meta.hot) { + import.meta.hot.on('iframe-reload', () => { + window.location.reload(); + }); +} diff --git a/packages/manager/apps/pci-ai-endpoints/tailwind.config.js b/packages/manager/apps/pci-ai-endpoints/tailwind.config.js new file mode 100644 index 000000000000..657ab11bb87d --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/tailwind.config.js @@ -0,0 +1,14 @@ +import path from 'path'; +import config from '@ovh-ux/manager-tailwind-config'; + +/** @type {import('tailwindcss').Config} */ +module.exports = { + ...config, + content: [ + './src/**/*.{js,jsx,ts,tsx}', + path.join( + path.dirname(require.resolve('@ovh-ux/manager-react-components')), + '**/*.{js,jsx,ts,tsx}', + ), + ], +}; diff --git a/packages/manager/apps/pci-ai-endpoints/tsconfig.json b/packages/manager/apps/pci-ai-endpoints/tsconfig.json new file mode 100644 index 000000000000..e2104f471575 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "lib": ["dom", "es2020"], + "noEmit": true, + "target": "es2020", + "types": ["vite/client", "node"], + "module": "ES2020", + "moduleResolution": "node", + "removeComments": true, + "outDir": "dist", + "esModuleInterop": true, + "isolatedModules": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "noImplicitAny": true, + "declaration": true, + "resolveJsonModule": true, + "allowJs": true, + "jsx": "react", + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src"], + "exclude": ["node_modules", "dist", "types", "src/__tests__"] +} diff --git a/packages/manager/apps/pci-ai-endpoints/vite.config.mjs b/packages/manager/apps/pci-ai-endpoints/vite.config.mjs new file mode 100644 index 000000000000..f33ab6dc98cd --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/vite.config.mjs @@ -0,0 +1,8 @@ +import { defineConfig } from 'vite'; +import { getBaseConfig } from '@ovh-ux/manager-vite-config'; +import { resolve } from 'path'; + +export default defineConfig({ + ...getBaseConfig(), + root: resolve(process.cwd()), +}); diff --git a/yarn.lock b/yarn.lock index 15601c181765..ec83bcc0cb75 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5184,6 +5184,9 @@ dependencies: lodash "^4.17.21" +"@ovh-ux/request-tagger@*": + version "0.4.0-alpha.0" + "@ovh-ux/rollup-plugin-less-inject@^1.0.5": version "1.0.6" resolved "https://registry.yarnpkg.com/@ovh-ux/rollup-plugin-less-inject/-/rollup-plugin-less-inject-1.0.6.tgz#8db90747f02d2bb95e3a5563d1c5a77a25edd0ad" From d417c8d5d26c78ba0a80de9087e16452a37d3bd4 Mon Sep 17 00:00:00 2001 From: Chris Fradet Date: Tue, 5 Nov 2024 09:36:35 +0100 Subject: [PATCH 02/17] feat(pci-ai-endpoints): update dependencies ref:AIS-859 Signed-off-by: Chris Fradet --- packages/manager/apps/pci-ai-endpoints/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/apps/pci-ai-endpoints/package.json b/packages/manager/apps/pci-ai-endpoints/package.json index 250b6741576f..323028182acb 100644 --- a/packages/manager/apps/pci-ai-endpoints/package.json +++ b/packages/manager/apps/pci-ai-endpoints/package.json @@ -28,7 +28,7 @@ "@ovh-ux/manager-react-core-application": "^0.10.8-alpha.0", "@ovh-ux/manager-react-shell-client": "^0.8.0-alpha.0", "@ovh-ux/manager-tailwind-config": "*", - "@ovh-ux/request-tagger": "*", + "@ovh-ux/request-tagger": "^^0.4.0-alpha.0", "@ovh-ux/shell": "^3.11.0-alpha.0", "@ovhcloud/ods-common-core": "17.2.1", "@ovhcloud/ods-common-theming": "17.2.1", From c86f998b055e0fe597caf3a66f5c4c991041c07d Mon Sep 17 00:00:00 2001 From: Chris Fradet Date: Tue, 5 Nov 2024 10:08:25 +0100 Subject: [PATCH 03/17] feat(pci-ai-endpoints): update yarn.lock file ref:AIS-859 Signed-off-by: Chris Fradet --- yarn.lock | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index ec83bcc0cb75..6b542f4639e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5184,8 +5184,10 @@ dependencies: lodash "^4.17.21" -"@ovh-ux/request-tagger@*": +"@ovh-ux/request-tagger@^^0.4.0-alpha.0": version "0.4.0-alpha.0" + resolved "https://registry.yarnpkg.com/@ovh-ux/request-tagger/-/request-tagger-0.4.0-alpha.0.tgz#2a828c545b3cd71f42c7a9797556d53df3dc24e4" + integrity sha512-lwK1kTnFnk/KH3FYxuXWIH/Bf1FZKCX31Fum6GxpQP0yWa5pHPBaD3eb4G83ZKO2AlAsrP+BxDOHVRC3tdn6pQ== "@ovh-ux/rollup-plugin-less-inject@^1.0.5": version "1.0.6" From 9b2db0d99175a1a96709b72994da31a0d851b765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ars=C3=A8ne?= Date: Tue, 5 Nov 2024 18:39:11 +0100 Subject: [PATCH 04/17] feat(pci-ai-endpoints): fix build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref: AIS-859 Signed-off-by: David Arsène --- .../apps/pci-ai-endpoints/package.json | 4 +- .../src/components/Breadcrumb/Breadcrumb.tsx | 4 +- .../src/core/HidePreloader.tsx | 12 +++ .../src/core/ShellRoutingSync.tsx | 16 ++++ .../src/hooks/breadcrumb/useBreadcrumb.tsx | 6 +- .../src/hooks/usePageTracking.ts | 33 ++++++++ .../pci-ai-endpoints/src/pages/layout.tsx | 83 ++++++++++++------- .../src/pages/listing/index.tsx | 7 +- .../pci-ai-endpoints/src/routes/routes.tsx | 56 +++++++------ .../src/tracking.constants.ts | 4 + .../apps/pci-ai-endpoints/tsconfig.json | 15 ++-- .../apps/pci-ai-endpoints/tsconfig.test.json | 6 ++ yarn.lock | 7 +- 13 files changed, 177 insertions(+), 76 deletions(-) create mode 100644 packages/manager/apps/pci-ai-endpoints/src/core/HidePreloader.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/core/ShellRoutingSync.tsx create mode 100644 packages/manager/apps/pci-ai-endpoints/src/hooks/usePageTracking.ts create mode 100644 packages/manager/apps/pci-ai-endpoints/src/tracking.constants.ts create mode 100644 packages/manager/apps/pci-ai-endpoints/tsconfig.test.json diff --git a/packages/manager/apps/pci-ai-endpoints/package.json b/packages/manager/apps/pci-ai-endpoints/package.json index 323028182acb..1e2886549f46 100644 --- a/packages/manager/apps/pci-ai-endpoints/package.json +++ b/packages/manager/apps/pci-ai-endpoints/package.json @@ -17,7 +17,7 @@ "start:dev": "lerna exec --stream --scope='@ovh-ux/manager-pci-ai-endpoints-app' --include-dependencies -- npm run dev --if-present", "start:watch": "lerna exec --stream --parallel --scope='@ovh-ux/manager-pci-ai-endpoints-app' --include-dependencies -- npm run dev:watch --if-present", "test:e2e": "tsc && node ../../../../scripts/run-playwright-bdd.js", - "test:e2e:ci": "tsc && node ../../../../scripts/run-playwright-bdd.js --ci" + "test:e2e:cii": "tsc && node ../../../../scripts/run-playwright-bdd.js --ci" }, "dependencies": { "@ovh-ux/manager-config": "^7.5.3-alpha.0", @@ -28,7 +28,7 @@ "@ovh-ux/manager-react-core-application": "^0.10.8-alpha.0", "@ovh-ux/manager-react-shell-client": "^0.8.0-alpha.0", "@ovh-ux/manager-tailwind-config": "*", - "@ovh-ux/request-tagger": "^^0.4.0-alpha.0", + "@ovh-ux/request-tagger": "^0.4.0-alpha.0", "@ovh-ux/shell": "^3.11.0-alpha.0", "@ovhcloud/ods-common-core": "17.2.1", "@ovhcloud/ods-common-theming": "17.2.1", diff --git a/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx b/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx index 52a91614eb9f..9cc650a46189 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { OsdsBreadcrumb } from '@ovhcloud/ods-components/react'; import { useParams } from 'react-router-dom'; +import { useProject } from '@ovh-ux/manager-pci-common'; import { usePciBreadcrumb, BreadcrumbItem, } from '@/hooks/breadcrumb/useBreadcrumb'; -import { useProject } from '@/data/hooks/pci/useProject'; import appConfig from '@/pci-ai-endpoints.config'; export interface BreadcrumbProps { @@ -18,7 +18,7 @@ function Breadcrumb({ customRootLabel }: BreadcrumbProps): JSX.Element { const label = customRootLabel || appConfig.rootLabel; const { projectId } = useParams(); - const { project } = useProject({ projectId }); + const { data: project } = useProject(); if (project) { const breadcrumbPci = usePciBreadcrumb({ diff --git a/packages/manager/apps/pci-ai-endpoints/src/core/HidePreloader.tsx b/packages/manager/apps/pci-ai-endpoints/src/core/HidePreloader.tsx new file mode 100644 index 000000000000..f75add24164d --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/core/HidePreloader.tsx @@ -0,0 +1,12 @@ +import { ShellContext } from '@ovh-ux/manager-react-shell-client'; +import { useContext, useEffect } from 'react'; + +export default function HidePreloader(): any { + const { ux } = useContext(ShellContext).shell; + + useEffect(() => { + ux.hidePreloader(); + }, []); + + return null; +} diff --git a/packages/manager/apps/pci-ai-endpoints/src/core/ShellRoutingSync.tsx b/packages/manager/apps/pci-ai-endpoints/src/core/ShellRoutingSync.tsx new file mode 100644 index 000000000000..a9dc656edb43 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/core/ShellRoutingSync.tsx @@ -0,0 +1,16 @@ +import { ShellContext } from '@ovh-ux/manager-react-shell-client'; +import { useContext, useEffect } from 'react'; +import { useLocation } from 'react-router-dom'; + +export default function ShellRoutingSync(): any { + const location = useLocation(); + + const { routing } = useContext(ShellContext).shell; + useEffect(() => { + routing.stopListenForHashChange(); + }, []); + useEffect(() => { + routing.onHashChange(); + }, [location]); + return null; +} diff --git a/packages/manager/apps/pci-ai-endpoints/src/hooks/breadcrumb/useBreadcrumb.tsx b/packages/manager/apps/pci-ai-endpoints/src/hooks/breadcrumb/useBreadcrumb.tsx index 11f507284c78..16b6b92a524b 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/hooks/breadcrumb/useBreadcrumb.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/hooks/breadcrumb/useBreadcrumb.tsx @@ -1,7 +1,7 @@ import { useEffect, useState, useContext } from 'react'; import { useLocation } from 'react-router-dom'; import { ShellContext } from '@ovh-ux/manager-react-shell-client'; -import { useProject, PciProject } from '@/data/hooks/pci/useProject'; +import { useProject } from '@ovh-ux/manager-pci-common'; export type BreadcrumbItem = { label: string | undefined; @@ -22,10 +22,10 @@ export const usePciBreadcrumb = ({ const { shell } = useContext(ShellContext); const [root, setRoot] = useState([]); const [appRoot, setAppRoot] = useState([]); - const { project } = useProject({ projectId }); + const { data: project } = useProject(); useEffect(() => { - const { description } = project as PciProject; + const { description } = project; const fetchRoot = async () => { try { const response = (await shell?.navigation.getURL( diff --git a/packages/manager/apps/pci-ai-endpoints/src/hooks/usePageTracking.ts b/packages/manager/apps/pci-ai-endpoints/src/hooks/usePageTracking.ts new file mode 100644 index 000000000000..643388a143f8 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/hooks/usePageTracking.ts @@ -0,0 +1,33 @@ +import { useContext, useEffect } from 'react'; +import { useLocation, useRouteLoaderData } from 'react-router-dom'; +import { ShellContext } from '@ovh-ux/manager-react-shell-client'; +import { TProject } from '@ovh-ux/manager-pci-common'; +import { PAGE_PREFIX, PCI_LEVEL2 } from '@/tracking.constants'; + +const DISCOVERY_PLANCODE = 'project.discovery'; + +export default function usePageTracking() { + const location = useLocation(); + const project = useRouteLoaderData('public-gateway') as TProject; + const { setPciProjectMode, trackPage } = useContext( + ShellContext, + ).shell.tracking; + + useEffect(() => { + if (project) { + setPciProjectMode({ + projectId: project.project_id, + isDiscoveryProject: project.planCode === DISCOVERY_PLANCODE, + }); + } + }, [project]); + + useEffect(() => { + const pageId = location.pathname.split('/').pop(); + const pageKey = pageId === 'public-gateway' ? '' : `::${pageId}`; + trackPage({ + name: `${PAGE_PREFIX}::public-gateway${pageKey}`, + level2: PCI_LEVEL2, + }); + }, [location]); +} diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/layout.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/layout.tsx index dc85c83ab2fb..31bbf70475ff 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/pages/layout.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/pages/layout.tsx @@ -1,31 +1,58 @@ -import React, { useEffect, useContext } from 'react'; -import { defineCurrentPage } from '@ovh-ux/request-tagger'; -import { Outlet, useLocation, useMatches } from 'react-router-dom'; -import { - useOvhTracking, - useRouteSynchro, - ShellContext, -} from '@ovh-ux/manager-react-shell-client'; +import React, { Suspense, useContext } from 'react'; +import { ErrorBanner } from '@ovh-ux/manager-react-components'; +import { Outlet, useRouteError } from 'react-router-dom'; +import { ResponseAPIError, useProject } from '@ovh-ux/manager-pci-common'; +import { ShellContext } from '@ovh-ux/manager-react-shell-client'; +import HidePreloader from '@/core/HidePreloader'; +import ShellRoutingSync from '@/core/ShellRoutingSync'; +import usePageTracking from '@/hooks/usePageTracking'; export default function Layout() { - const location = useLocation(); - const { shell } = useContext(ShellContext); - const matches = useMatches(); - const { trackCurrentPage } = useOvhTracking(); - useRouteSynchro(); - - useEffect(() => { - const match = matches.slice(-1); - defineCurrentPage(`app.pci-ai-endpoints-${match[0]?.id}`); - }, [location]); - - useEffect(() => { - trackCurrentPage(); - }, [location]); - - useEffect(() => { - shell.ux.hidePreloader(); - }, []); - - return ; + const { isSuccess } = useProject(); + + usePageTracking(); + + return ( +
+ + + {isSuccess && ( + <> + + + + )} + +
+ ); } + +export const ErrorBoundary = () => { + const error = useRouteError() as ResponseAPIError; + const { navigation } = useContext(ShellContext).shell; + + const redirectionApplication = 'public-cloud'; + + const navigateToHomePage = () => { + navigation.navigateTo(redirectionApplication, '', {}); + }; + + const reloadPage = () => { + navigation.reload(); + }; + + return ( + + + + + + ); +}; diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/listing/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/listing/index.tsx index a7fb6e8735e8..65cb6c7185fb 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/pages/listing/index.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/pages/listing/index.tsx @@ -8,7 +8,7 @@ import { BaseLayout, Datagrid, DataGridTextCell, - useResourcesIcebergV2, + useResourcesIcebergV6, } from '@ovh-ux/manager-react-components'; import Loading from '@/components/Loading/Loading'; @@ -26,6 +26,7 @@ export default function Listing() { const navigate = useNavigate(); const location = useLocation(); const { projectId } = useParams(); + const { data, fetchNextPage, @@ -37,8 +38,8 @@ export default function Listing() { setSorting, error, status, - } = useResourcesIcebergV2({ - route: `/publicCloud/project/${projectId}`, + } = useResourcesIcebergV6({ + route: `/cloud/project/${projectId}/ai/app`, queryKey: ['pci-ai-endpoints', `/publicCloud/project/${projectId}`], }); diff --git a/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx b/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx index 2e1325b24596..dc8270bb3fb9 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx @@ -18,7 +18,11 @@ const lazyRouteConfig = (importFn: CallableFunction): Partial => { export const Routes: any = [ { - path: '/pci/projects/:projectId/ai-endpoints', + path: '/', + ...lazyRouteConfig(() => import('@/pages/layout')), + }, + { + path: '/pci/projects/:projectId/ai/endpoints', ...lazyRouteConfig(() => import('@/pages/layout')), children: [ { @@ -31,34 +35,36 @@ export const Routes: any = [ pageType: PageType.listing, }, }, - }, - { - path: urls.dashboard, - ...lazyRouteConfig(() => import('@/pages/dashboard')), children: [ { - id: 'dashboard', - path: '', - ...lazyRouteConfig(() => - import('@/pages/dashboard/general-informations'), - ), - handle: { - tracking: { - pageName: 'dashboard', - pageType: PageType.dashboard, + path: urls.dashboard, + ...lazyRouteConfig(() => import('@/pages/dashboard')), + children: [ + { + id: 'dashboard', + path: '', + ...lazyRouteConfig(() => + import('@/pages/dashboard/general-informations'), + ), + handle: { + tracking: { + pageName: 'dashboard', + pageType: PageType.dashboard, + }, + }, }, - }, - }, - { - id: 'tab2', - path: 'Tab2', - ...lazyRouteConfig(() => import('@/pages/dashboard/tab2')), - handle: { - tracking: { - pageName: 'tab2', - pageType: PageType.dashboard, + { + id: 'tab2', + path: 'Tab2', + ...lazyRouteConfig(() => import('@/pages/dashboard/tab2')), + handle: { + tracking: { + pageName: 'tab2', + pageType: PageType.dashboard, + }, + }, }, - }, + ], }, ], }, diff --git a/packages/manager/apps/pci-ai-endpoints/src/tracking.constants.ts b/packages/manager/apps/pci-ai-endpoints/src/tracking.constants.ts new file mode 100644 index 000000000000..4b28ddb077db --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/src/tracking.constants.ts @@ -0,0 +1,4 @@ +export const PCI_LEVEL2 = '86'; +export const PAGE_PREFIX = 'PublicCloud::pci::projects::project'; +export const ACTION_PREFIX = `${PAGE_PREFIX}::ai-endpoints`; +export const DISCOVERY_PLANCODE = 'project.discovery'; diff --git a/packages/manager/apps/pci-ai-endpoints/tsconfig.json b/packages/manager/apps/pci-ai-endpoints/tsconfig.json index e2104f471575..cf730882826c 100644 --- a/packages/manager/apps/pci-ai-endpoints/tsconfig.json +++ b/packages/manager/apps/pci-ai-endpoints/tsconfig.json @@ -1,11 +1,11 @@ { "compilerOptions": { - "lib": ["dom", "es2020"], + "lib": ["dom", "es2020", "ESNext"], "noEmit": true, - "target": "es2020", - "types": ["vite/client", "node"], - "module": "ES2020", - "moduleResolution": "node", + "target": "ESNext", + "types": ["vite/client", "node", "jest", "@testing-library/jest-dom"], + "module": "esnext", + "moduleResolution": "Node", "removeComments": true, "outDir": "dist", "esModuleInterop": true, @@ -17,9 +17,10 @@ "resolveJsonModule": true, "allowJs": true, "jsx": "react", - "baseUrl": ".", "paths": { - "@/*": ["./src/*"] + "@/*": ["./src/*"], + "@playwright-helpers/*": ["../../../../playwright-helpers/*"], + "@translation/*": ["./public/translations/*"] } }, "include": ["src"], diff --git a/packages/manager/apps/pci-ai-endpoints/tsconfig.test.json b/packages/manager/apps/pci-ai-endpoints/tsconfig.test.json new file mode 100644 index 000000000000..7048c297c8f6 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/tsconfig.test.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS" + } +} diff --git a/yarn.lock b/yarn.lock index 6b542f4639e7..db1caaf4c640 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5184,11 +5184,6 @@ dependencies: lodash "^4.17.21" -"@ovh-ux/request-tagger@^^0.4.0-alpha.0": - version "0.4.0-alpha.0" - resolved "https://registry.yarnpkg.com/@ovh-ux/request-tagger/-/request-tagger-0.4.0-alpha.0.tgz#2a828c545b3cd71f42c7a9797556d53df3dc24e4" - integrity sha512-lwK1kTnFnk/KH3FYxuXWIH/Bf1FZKCX31Fum6GxpQP0yWa5pHPBaD3eb4G83ZKO2AlAsrP+BxDOHVRC3tdn6pQ== - "@ovh-ux/rollup-plugin-less-inject@^1.0.5": version "1.0.6" resolved "https://registry.yarnpkg.com/@ovh-ux/rollup-plugin-less-inject/-/rollup-plugin-less-inject-1.0.6.tgz#8db90747f02d2bb95e3a5563d1c5a77a25edd0ad" @@ -25956,7 +25951,7 @@ string-length@^4.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^2.1.0: +string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== From 399395d02e94637ba3aad28f3b60397d09abd978 Mon Sep 17 00:00:00 2001 From: Chris Fradet Date: Wed, 13 Nov 2024 17:22:42 +0100 Subject: [PATCH 05/17] feat(pci-ai-endpoints): fix sonarQube erros ref:AIS-859 Signed-off-by: Chris Fradet --- .../universe/public-cloud/pci-menu.ts | 3 +- .../navigation-tree/services/publicCloud.ts | 8 +- .../src/components/Breadcrumb/Breadcrumb.tsx | 13 ++- .../apps/pci-ai-endpoints/src/pages/404.tsx | 2 - .../dashboard/general-informations/index.tsx | 7 -- .../src/pages/dashboard/index.tsx | 90 ------------------- .../src/pages/dashboard/tab2/index.tsx | 7 -- .../pci-ai-endpoints/src/routes/routes.tsx | 32 ------- 8 files changed, 12 insertions(+), 150 deletions(-) delete mode 100644 packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/general-informations/index.tsx delete mode 100644 packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/index.tsx delete mode 100644 packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/tab2/index.tsx diff --git a/packages/manager/apps/container/src/container/legacy/server-sidebar/universe/public-cloud/pci-menu.ts b/packages/manager/apps/container/src/container/legacy/server-sidebar/universe/public-cloud/pci-menu.ts index 9f9785a191df..6db51cdbc176 100644 --- a/packages/manager/apps/container/src/container/legacy/server-sidebar/universe/public-cloud/pci-menu.ts +++ b/packages/manager/apps/container/src/container/legacy/server-sidebar/universe/public-cloud/pci-menu.ts @@ -387,8 +387,7 @@ export function getPciProjectMenu( id: 'endpoints', title: 'AI Endpoints', badge: 'alpha', - href: 'https://endpoints.ai.cloud.ovh.net/', - external: true, + href: getURL('public-cloud', `#/pci/projects/${projectId}/ai/endpoints`), }, ], }); diff --git a/packages/manager/apps/container/src/container/nav-reshuffle/sidebar/navigation-tree/services/publicCloud.ts b/packages/manager/apps/container/src/container/nav-reshuffle/sidebar/navigation-tree/services/publicCloud.ts index 1543f86b98e6..040b459bb1ac 100644 --- a/packages/manager/apps/container/src/container/nav-reshuffle/sidebar/navigation-tree/services/publicCloud.ts +++ b/packages/manager/apps/container/src/container/nav-reshuffle/sidebar/navigation-tree/services/publicCloud.ts @@ -556,11 +556,13 @@ pciNode.children = [ idAttr: 'pci-ai-endpoints-link', universe: PUBLICCLOUD_UNIVERSE_ID, translation: 'sidebar_pci_ai_endpoints', - count: false, - url: 'https://endpoints.ai.cloud.ovh.net/', + routing: { + application: 'public-cloud', + hash: '#/pci/projects/{projectId}/ai/endpoints', + }, features: ['ai-endpoints'], + forceVisibility: true, tag: NodeTag.ALPHA, - isExternal: true, }, ], }, diff --git a/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx b/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx index 9cc650a46189..e8c14f5ab368 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx @@ -20,13 +20,12 @@ function Breadcrumb({ customRootLabel }: BreadcrumbProps): JSX.Element { const { projectId } = useParams(); const { data: project } = useProject(); - if (project) { - const breadcrumbPci = usePciBreadcrumb({ - projectId, - appName: 'pci-ai-endpoints', - }); - return ; - } + const breadcrumbPci = usePciBreadcrumb({ + projectId, + appName: 'pci-ai-endpoints', + }); + + return ; } export default Breadcrumb; diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/404.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/404.tsx index d052f1ebcbbf..a699dc45f8da 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/pages/404.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/pages/404.tsx @@ -1,7 +1,5 @@ import React from 'react'; export default function NotFound() { - // @TODO: add a redirection here in order to catch /:serviceName given from iframe - return

404 - route not found

; } diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/general-informations/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/general-informations/index.tsx deleted file mode 100644 index a785e6d2ff0f..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/general-informations/index.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; - -function GeneralInfos() { - return
Information Générales Tab
; -} - -export default GeneralInfos; diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/index.tsx deleted file mode 100644 index 1eb591f9842a..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/index.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { - Outlet, - NavLink, - useLocation, - useNavigate, - useParams, - useResolvedPath, -} from 'react-router-dom'; -import { - OsdsTabs, - OsdsTabBar, - OsdsTabBarItem, -} from '@ovhcloud/ods-components/react'; - -import { BaseLayout } from '@ovh-ux/manager-react-components'; - -import Breadcrumb from '@/components/Breadcrumb/Breadcrumb'; - -export type DashboardTabItemProps = { - name: string; - title: string; - to: string; -}; - -export type DashboardLayoutProps = { - tabs: DashboardTabItemProps[]; -}; - -export default function DashboardPage() { - const [panel, setActivePanel] = useState(''); - const { serviceName } = useParams(); - const location = useLocation(); - const navigate = useNavigate(); - const { t } = useTranslation('dashboard'); - - const tabsList = [ - { - name: 'general_informations', - title: 'Informations générales', - to: useResolvedPath('').pathname, - }, - { - name: 'Tab 2', - title: 'Tab 2', - to: useResolvedPath('Tab2').pathname, - }, - ]; - - useEffect(() => { - const activeTab = tabsList.find((tab) => tab.to === location.pathname); - if (activeTab) { - setActivePanel(activeTab.name); - } else { - setActivePanel(tabsList[0].name); - navigate(`${tabsList[0].to}`); - } - }, [location.pathname]); - - const header = { - title: t('title'), - }; - - return ( - } - header={header} - description="Description du pci-ai-endpoints" - tabs={ - - - {tabsList.map((tab: DashboardTabItemProps) => ( - - - {tab.title} - - - ))} - - - } - > - - - ); -} diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/tab2/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/tab2/index.tsx deleted file mode 100644 index 9cb100623734..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/src/pages/dashboard/tab2/index.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; - -function Tab2() { - return
Tab 2
; -} - -export default Tab2; diff --git a/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx b/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx index dc8270bb3fb9..9ec8c4500980 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx @@ -35,38 +35,6 @@ export const Routes: any = [ pageType: PageType.listing, }, }, - children: [ - { - path: urls.dashboard, - ...lazyRouteConfig(() => import('@/pages/dashboard')), - children: [ - { - id: 'dashboard', - path: '', - ...lazyRouteConfig(() => - import('@/pages/dashboard/general-informations'), - ), - handle: { - tracking: { - pageName: 'dashboard', - pageType: PageType.dashboard, - }, - }, - }, - { - id: 'tab2', - path: 'Tab2', - ...lazyRouteConfig(() => import('@/pages/dashboard/tab2')), - handle: { - tracking: { - pageName: 'tab2', - pageType: PageType.dashboard, - }, - }, - }, - ], - }, - ], }, { id: 'onboarding', From e02bb57444fc35f2e3138db6b8a32e58995089ae Mon Sep 17 00:00:00 2001 From: Arthur Bullet Date: Wed, 13 Nov 2024 19:18:58 +0100 Subject: [PATCH 06/17] feat(ai.endpoints): clean branch and rework onboarding Signed-off-by: Arthur Bullet --- .../universe/public-cloud/pci-menu.ts | 11 +- .../navigation-tree/services/publicCloud.ts | 15 ++- .../dashboard/Messages_fr_FR.json | 7 -- .../translations/listing/Messages_fr_FR.json | 4 - .../onboarding/Messages_fr_FR.json | 28 +++-- .../pci-ai-endpoints/Messages_fr_FR.json | 2 +- .../src/components/Breadcrumb/Breadcrumb.tsx | 2 +- .../src/hooks/guide/useGuideUtils.tsx | 38 +++--- .../apps/pci-ai-endpoints/src/index.tsx | 2 +- .../src/pages/listing/index.tsx | 118 ------------------ .../src/pages/onboarding/index.tsx | 79 ++++++++++-- .../src/routes/routes.constant.ts | 5 +- .../pci-ai-endpoints/src/routes/routes.tsx | 13 +- 13 files changed, 138 insertions(+), 186 deletions(-) delete mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/dashboard/Messages_fr_FR.json delete mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/listing/Messages_fr_FR.json delete mode 100644 packages/manager/apps/pci-ai-endpoints/src/pages/listing/index.tsx diff --git a/packages/manager/apps/container/src/container/legacy/server-sidebar/universe/public-cloud/pci-menu.ts b/packages/manager/apps/container/src/container/legacy/server-sidebar/universe/public-cloud/pci-menu.ts index 6db51cdbc176..8a7dabadda19 100644 --- a/packages/manager/apps/container/src/container/legacy/server-sidebar/universe/public-cloud/pci-menu.ts +++ b/packages/manager/apps/container/src/container/legacy/server-sidebar/universe/public-cloud/pci-menu.ts @@ -50,6 +50,7 @@ export const features = [ 'ai-endpoints', 'key-management-service', 'pci-savings-plan', + 'pci-ai-endpoints', ]; export function getPciProjectMenu( @@ -383,11 +384,17 @@ export function getPciProjectMenu( title: 'AI Deploy', href: getURL('public-cloud', `#/pci/projects/${projectId}/ai/apps`), }, - isFeaturesAvailable('ai-endpoints') && { - id: 'endpoints', + isFeaturesAvailable('pci-ai-endpoints') ? { + id: 'pci-ai-endpoints', title: 'AI Endpoints', badge: 'alpha', href: getURL('public-cloud', `#/pci/projects/${projectId}/ai/endpoints`), + } : { + id: 'ai-endpoints', + title: 'AI Endpoints', + badge: 'alpha', + href: 'https://endpoints.ai.cloud.ovh.net/', + external: true, }, ], }); diff --git a/packages/manager/apps/container/src/container/nav-reshuffle/sidebar/navigation-tree/services/publicCloud.ts b/packages/manager/apps/container/src/container/nav-reshuffle/sidebar/navigation-tree/services/publicCloud.ts index 040b459bb1ac..77b03743d729 100644 --- a/packages/manager/apps/container/src/container/nav-reshuffle/sidebar/navigation-tree/services/publicCloud.ts +++ b/packages/manager/apps/container/src/container/nav-reshuffle/sidebar/navigation-tree/services/publicCloud.ts @@ -496,7 +496,7 @@ pciNode.children = [ idAttr: 'pci-ai-link', universe: PUBLICCLOUD_UNIVERSE_ID, translation: 'sidebar_pci_ai', - features: ['notebooks', 'ai-apps', 'training', 'ai-dashboard'], + features: ['notebooks', 'ai-apps', 'training', 'ai-dashboard', 'ai-endpoints', 'pci-ai-endpoints'], forceVisibility: true, children: [ { @@ -556,11 +556,22 @@ pciNode.children = [ idAttr: 'pci-ai-endpoints-link', universe: PUBLICCLOUD_UNIVERSE_ID, translation: 'sidebar_pci_ai_endpoints', + count: false, + url: 'https://endpoints.ai.cloud.ovh.net/', + features: ['ai-endpoints'], + tag: NodeTag.ALPHA, + isExternal: true, + }, + { + id: 'pci-ai-endpoints-app', + idAttr: 'pci-ai-endpoints-app-link', + universe: PUBLICCLOUD_UNIVERSE_ID, + translation: 'sidebar_pci_ai_endpoints', routing: { application: 'public-cloud', hash: '#/pci/projects/{projectId}/ai/endpoints', }, - features: ['ai-endpoints'], + features: ['pci-ai-endpoints'], forceVisibility: true, tag: NodeTag.ALPHA, }, diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/dashboard/Messages_fr_FR.json b/packages/manager/apps/pci-ai-endpoints/public/translations/dashboard/Messages_fr_FR.json deleted file mode 100644 index f42a27b3366a..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/public/translations/dashboard/Messages_fr_FR.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "title": "Dashboard page", - "error_service": "No services info", - "general_informations": "Informations générales", - "tab2": "Tab 2", - "back_link": "Retour à la liste" -} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/listing/Messages_fr_FR.json b/packages/manager/apps/pci-ai-endpoints/public/translations/listing/Messages_fr_FR.json deleted file mode 100644 index c882bc92cfec..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/public/translations/listing/Messages_fr_FR.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Listing page", - "listing_resultats": "résultats" -} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json index 08cb4cbb9107..f74cde56bf32 100644 --- a/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json @@ -1,13 +1,19 @@ { - "title": "pci-ai-endpoints", - "description": "Découvrez des services de stockage managés qui s’appuient sur le système de fichiers OpenZFS. Bénéficiez en quelques clics d’espaces de stockage centralisés pour entreposer ou sauvegarder vos données et fichiers.", - "orderButtonLabel": "Commander un pci-ai-endpoints", - "moreInfoButtonLabel": "En savoir plus sur pci-ai-endpoints", - "guideCategory": "Tutoriel", - "guide1Title": "Premiers pas avec un pci-ai-endpoints", - "guide1Description": "Découvrez comment gérer un NAS-HA depuis l'espace-client OVHcloud", - "guide2Title": "Monter votre NAS via un partage NFS", - "guide2Description": "Découvrez comment monter un NAS via un partage NFS", - "guide3Title": "Monter votre NAS sur Windows Server via CIFS", - "guide3Description": "Découvrez comment monter un NAS sur Windows Server via le protocole CIFS" + "title": "Enhance your applications with AI Endpoints", + "description": "Designed with simplicity in mind, our platform allows developers of all skill levels to enhance their applications with cutting-edge AI APIs — no AI expertise required.", + "description1": "These endpoints require", + "description2": "no AI expertise", + "description3": "or dedicated infrastructure, as the serverless platform provides", + "description4": "access to advanced AI models", + "description5": "including Large Language Models (LLMs), natural language processing, translation, speech recognition, image recognition, and more. Developers can select from a range of models, including", + "description6": "open-source options like Mistral AI, Llama and Stable Diffusion.", + "goToAiEndpoint": "AI Endpoints", + "guideCategory": "Guide", + "guide1Title": "Premiers pas avec AI Endpoint", + "guide1Description": "Découvrez comment utiliser AI Endpoints", + "guide2Title": "Boostez vos applications avec AI Endpoint", + "guide2Description": "Découvrez comment utiliser AI Endpoint dans vos applications", + "tutoCategory": "Tutorials", + "guide3Title": "Decouvrir nos tutos sur mesure", + "guide3Description": "Découvrez comment utiliser AI Endpoint avec des tutos sur mesure" } diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_fr_FR.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_fr_FR.json index b794794292e0..b142b36232e5 100644 --- a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_fr_FR.json +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_fr_FR.json @@ -1,6 +1,6 @@ { "title": "Bienvenue uapp", - "crumb": "pci-ai-endpoints", + "crumb": "AI-Endpoints", "tabs_2": "Tabs 2", "onboarding": "Onboarding" } diff --git a/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx b/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx index e8c14f5ab368..9148cf670135 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/components/Breadcrumb/Breadcrumb.tsx @@ -22,7 +22,7 @@ function Breadcrumb({ customRootLabel }: BreadcrumbProps): JSX.Element { const breadcrumbPci = usePciBreadcrumb({ projectId, - appName: 'pci-ai-endpoints', + appName: 'AI-Endpoints', }); return ; diff --git a/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx b/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx index 2971e624318e..0207b6697aeb 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx @@ -37,21 +37,6 @@ const GUIDE_LIST: { [guideName: string]: Partial } = { WS: '/update-path', US: '/update-path', }, - guideLink3: { - DE: '/guide-link-3-path', - ES: '/guide-link-3-path', - IE: '/en/guide-link-3-path', - IT: '/guide-link-3-path', - PL: '/guide-link-3-path', - PT: '/guide-link-3-path', - FR: '/guide-link-3-path', - GB: '/guide-link-3-path', - CA: '/update-path', - QC: '/update-path', - WE: '/update-path', - WS: '/update-path', - US: '/update-path', - }, /* addNewGuideLink : { DEFAULT: '/guide-link-3-path', @@ -62,6 +47,24 @@ const GUIDE_LIST: { [guideName: string]: Partial } = { */ }; +const TUTO_LIST: { [guideName: string]: Partial } = { + tutoLink1: { + DE: 'https://blog.ovhcloud.com/tag/ai-endpoints/', + ES: 'https://blog.ovhcloud.com/tag/ai-endpoints/', + IE: 'https://blog.ovhcloud.com/tag/ai-endpoints/', + IT: 'https://blog.ovhcloud.com/tag/ai-endpoints/', + PL: 'https://blog.ovhcloud.com/tag/ai-endpoints/', + PT: 'https://blog.ovhcloud.com/tag/ai-endpoints/', + FR: 'https://blog.ovhcloud.com/tag/ai-endpoints/', + GB: 'https://blog.ovhcloud.com/tag/ai-endpoints/', + CA: 'https://blog.ovhcloud.com/tag/ai-endpoints/', + QC: 'https://blog.ovhcloud.com/tag/ai-endpoints/', + WE: 'https://blog.ovhcloud.com/tag/ai-endpoints/', + WS: 'https://blog.ovhcloud.com/tag/ai-endpoints/', + US: 'https://blog.ovhcloud.com/tag/ai-endpoints/', + }, +}; + type GetGuideLinkProps = { name?: string; subsidiary: CountryCode | string; @@ -73,6 +76,11 @@ function getGuideListLink({ subsidiary }: GetGuideLinkProps) { keys.forEach((key) => { list[key[0]] = docUrl + GUIDE_LIST[key[0]][subsidiary as CountryCode]; }); + + const tutos = Object.entries(TUTO_LIST); + tutos.forEach((key) => { + list[key[0]] = TUTO_LIST[key[0]][subsidiary as CountryCode]; + }); return list; } diff --git a/packages/manager/apps/pci-ai-endpoints/src/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/index.tsx index 0342fb7c826a..7f4ab09081e1 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/index.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/index.tsx @@ -28,7 +28,7 @@ const init = async (appName: string) => { context, reloadOnLocaleChange: true, defaultNS: appName, - ns: ['listing', 'dashboard', 'onboarding'], + ns: ['onboarding'], }); const region = context.environment.getRegion(); diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/listing/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/listing/index.tsx deleted file mode 100644 index 65cb6c7185fb..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/src/pages/listing/index.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useParams, useNavigate, useLocation } from 'react-router-dom'; - -import { OsdsLink } from '@ovhcloud/ods-components/react'; -import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming'; -import { - BaseLayout, - Datagrid, - DataGridTextCell, - useResourcesIcebergV6, -} from '@ovh-ux/manager-react-components'; - -import Loading from '@/components/Loading/Loading'; -import ErrorBanner from '@/components/Error/Error'; -import Breadcrumb from '@/components/Breadcrumb/Breadcrumb'; - -import appConfig from '@/pci-ai-endpoints.config'; -import { urls } from '@/routes/routes.constant'; - -export default function Listing() { - const { t } = useTranslation('listing'); - const myConfig = appConfig; - const serviceKey = myConfig.listing?.datagrid?.serviceKey; - const [columns, setColumns] = useState([]); - const navigate = useNavigate(); - const location = useLocation(); - const { projectId } = useParams(); - - const { - data, - fetchNextPage, - hasNextPage, - flattenData, - isError, - isLoading, - sorting, - setSorting, - error, - status, - } = useResourcesIcebergV6({ - route: `/cloud/project/${projectId}/ai/app`, - queryKey: ['pci-ai-endpoints', `/publicCloud/project/${projectId}`], - }); - - const navigateToDashboard = (label: string) => { - const path = - location.pathname.indexOf('pci') > -1 ? `${location.pathname}/` : '/'; - navigate(`${path}${label}`); - }; - - useEffect(() => { - if (status === 'success' && data?.pages[0].data.length === 0) { - navigate(urls.onboarding); - } else if (status === 'success' && data?.pages.length > 0 && !flattenData) { - const tmp = Object.keys(data?.pages[0].data[0]) - .filter((element) => element !== 'iam') - .map((element) => ({ - id: element, - label: element, - cell: (props: any) => { - const label = props[element] as string; - if (typeof label === 'string' || typeof label === 'number') { - if (serviceKey === element) - return ( - - navigateToDashboard(label)} - > - {label} - - - ); - return {label}; - } - return
-
; - }, - })); - setColumns(tmp); - } - }, [data]); - - if (isError) { - return ; - } - - if (isLoading && !flattenData) { - return ( -
- -
- ); - } - - const header = { - title: t('title'), - }; - - return ( - } header={header}> - - {columns && flattenData && ( - - )} - - - ); -} diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.tsx index 93e59c16a128..60e3d1344fa9 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.tsx @@ -1,6 +1,12 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { Card, OnboardingLayout } from '@ovh-ux/manager-react-components'; +import { OsdsText } from '@ovhcloud/ods-components/react'; +import { + ODS_THEME_COLOR_INTENT, + ODS_THEME_TYPOGRAPHY_SIZE, +} from '@ovhcloud/ods-common-theming'; +import { ODS_TEXT_LEVEL } from '@ovhcloud/ods-components'; import useGuideUtils from '@/hooks/guide/useGuideUtils'; import Breadcrumb from '@/components/Breadcrumb/Breadcrumb'; import onboardingImgSrc from './onboarding-img.png'; @@ -33,14 +39,13 @@ export default function Onboarding() { texts: { title: t('guide3Title'), description: t('guide3Description'), - category: t('guideCategory'), + category: t('tutoCategory'), }, - href: link?.guideLink3, + href: link?.tutoLink1, }, ]; const title: string = t('title'); - const description: string = t('description'); const imgSrc = { src: onboardingImgSrc, }; @@ -51,11 +56,69 @@ export default function Onboarding() { + + {t('description')} + +
+
+ + {t('description1')} + {' '} + + {t('description2')} + {' '} + + {t('description3')} + {' '} + + {t('description4')} + {' '} + + {t('description5')} + {' '} + + {t('description6')} + +
+
+ + } + moreInfoButtonLabel={t('goToAiEndpoint')} + moreInfoHref="https://endpoints.ai.cloud.ovh.net/" > {tileList.map((tile) => ( diff --git a/packages/manager/apps/pci-ai-endpoints/src/routes/routes.constant.ts b/packages/manager/apps/pci-ai-endpoints/src/routes/routes.constant.ts index f24e2c6c71f3..f5bd97e6a296 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/routes/routes.constant.ts +++ b/packages/manager/apps/pci-ai-endpoints/src/routes/routes.constant.ts @@ -1,7 +1,4 @@ export const urls = { - root: '/', + root: '/onboarding', onboarding: 'onboarding', - listing: '', - dashboard: ':serviceName', - tab2: 'tab2', }; diff --git a/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx b/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx index 9ec8c4500980..21d33fee52ea 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/routes/routes.tsx @@ -25,20 +25,9 @@ export const Routes: any = [ path: '/pci/projects/:projectId/ai/endpoints', ...lazyRouteConfig(() => import('@/pages/layout')), children: [ - { - id: 'listing', - path: urls.listing, - ...lazyRouteConfig(() => import('@/pages/listing')), - handle: { - tracking: { - pageName: 'listing', - pageType: PageType.listing, - }, - }, - }, { id: 'onboarding', - path: urls.onboarding, + path: '', ...lazyRouteConfig(() => import('@/pages/onboarding')), handle: { tracking: { From 8144a3e427bb9c46ead0ec25fd2948b806912ed0 Mon Sep 17 00:00:00 2001 From: Arthur Bullet Date: Wed, 13 Nov 2024 19:52:00 +0100 Subject: [PATCH 07/17] feat(ai.endpoints): clean branch and use epic branch Signed-off-by: Arthur Bullet --- .../pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx | 8 -------- .../apps/pci-ai-endpoints/src/routes/routes.constant.ts | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx b/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx index 0207b6697aeb..c4da839d62c3 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx @@ -37,14 +37,6 @@ const GUIDE_LIST: { [guideName: string]: Partial } = { WS: '/update-path', US: '/update-path', }, - /* - addNewGuideLink : { - DEFAULT: '/guide-link-3-path', - DE: '/guide-link-3-path', - ES: '/guide-link-3-path', - ... - } - */ }; const TUTO_LIST: { [guideName: string]: Partial } = { diff --git a/packages/manager/apps/pci-ai-endpoints/src/routes/routes.constant.ts b/packages/manager/apps/pci-ai-endpoints/src/routes/routes.constant.ts index f5bd97e6a296..a2a71cf31dd0 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/routes/routes.constant.ts +++ b/packages/manager/apps/pci-ai-endpoints/src/routes/routes.constant.ts @@ -1,4 +1,4 @@ export const urls = { - root: '/onboarding', + root: '/', onboarding: 'onboarding', }; From 2b783c9a4d43b9fde47b22ee79adf8f8d326afcf Mon Sep 17 00:00:00 2001 From: Arthur Bullet Date: Thu, 14 Nov 2024 10:58:05 +0100 Subject: [PATCH 08/17] feat(ai.endpoints): remove useless file Signed-off-by: Arthur Bullet --- .../e2e/features/error.feature | 12 ----- .../e2e/features/onboarding.feature | 7 --- .../e2e/step-definitions/error.step.ts | 53 ------------------- .../e2e/step-definitions/onboarding.step.ts | 32 ----------- .../pci-ai-endpoints/e2e/utils/constants.ts | 7 --- .../apps/pci-ai-endpoints/e2e/utils/index.tsx | 2 - .../pci-ai-endpoints/e2e/utils/network.ts | 28 ---------- .../mocks/example/example-data.json | 11 ---- .../pci-ai-endpoints/mocks/example/example.ts | 30 ----------- .../apps/pci-ai-endpoints/mocks/index.ts | 1 - .../pci-ai-endpoints/playwright.config.ts | 20 ------- 11 files changed, 203 deletions(-) delete mode 100644 packages/manager/apps/pci-ai-endpoints/e2e/features/error.feature delete mode 100644 packages/manager/apps/pci-ai-endpoints/e2e/features/onboarding.feature delete mode 100644 packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/error.step.ts delete mode 100644 packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/onboarding.step.ts delete mode 100644 packages/manager/apps/pci-ai-endpoints/e2e/utils/constants.ts delete mode 100644 packages/manager/apps/pci-ai-endpoints/e2e/utils/index.tsx delete mode 100644 packages/manager/apps/pci-ai-endpoints/e2e/utils/network.ts delete mode 100644 packages/manager/apps/pci-ai-endpoints/mocks/example/example-data.json delete mode 100644 packages/manager/apps/pci-ai-endpoints/mocks/example/example.ts delete mode 100644 packages/manager/apps/pci-ai-endpoints/mocks/index.ts delete mode 100644 packages/manager/apps/pci-ai-endpoints/playwright.config.ts diff --git a/packages/manager/apps/pci-ai-endpoints/e2e/features/error.feature b/packages/manager/apps/pci-ai-endpoints/e2e/features/error.feature deleted file mode 100644 index e20e56f5f592..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/e2e/features/error.feature +++ /dev/null @@ -1,12 +0,0 @@ -Feature: Error - - Scenario Outline: Display an error if request fails - Given The service to fetch the data is - When User navigates to Home page - Then User "" the list of data - Then User sees error - - Examples: - | apiOk | sees | anyError | - | OK | sees | no | - | KO | doesn't see | an | diff --git a/packages/manager/apps/pci-ai-endpoints/e2e/features/onboarding.feature b/packages/manager/apps/pci-ai-endpoints/e2e/features/onboarding.feature deleted file mode 100644 index 1f3e77078742..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/e2e/features/onboarding.feature +++ /dev/null @@ -1,7 +0,0 @@ -Feature: Onboarding page - - Scenario: User wants to find informations related to pci-ai-endpoints - Given User has 0 elements in the Listing page - When User navigates to Listing page - Then User gets redirected to Onboarding page - Then User sees 3 guides diff --git a/packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/error.step.ts b/packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/error.step.ts deleted file mode 100644 index e6a5279653c3..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/error.step.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Given, When, Then } from '@cucumber/cucumber'; -import { expect } from '@playwright/test'; -import { ICustomWorld } from '../../../../../../playwright-helpers'; -import { ConfigParams, getUrl, setupNetwork } from '../utils'; -import { title } from '../../public/translations/listing/Messages_fr_FR.json'; -import { - manager_error_page_title, - manager_error_page_action_home_label, - manager_error_page_action_reload_label, -} from '../../public/translations/pci-ai-endpoints/error/Messages_fr_FR.json'; - -Given('The service to fetch the data is {word}', function( - this: ICustomWorld, - apiState: 'OK' | 'KO', -) { - this.handlersConfig.isKo = apiState === 'KO'; -}); - -When('User navigates to Home page', async function( - this: ICustomWorld, -) { - await setupNetwork(this); - await this.page.goto(this.testContext.initialUrl || getUrl('root'), { - waitUntil: 'load', - }); -}); - -Then('User {string} the list of data', async function( - this: ICustomWorld, - see: 'sees' | "doesn't see", -) { - if (see === 'sees') { - const titleElement = await this.page.getByText(title); - await expect(titleElement).toBeVisible(); - } -}); - -Then('User sees {word} error', async function( - this: ICustomWorld, - anyError: 'an' | 'no', -) { - if (anyError === 'an') { - await expect(this.page.getByText(manager_error_page_title)).toBeVisible(); - - await expect( - this.page.getByText(manager_error_page_action_home_label), - ).toBeVisible(); - - await expect( - this.page.getByText(manager_error_page_action_reload_label), - ).toBeVisible(); - } -}); diff --git a/packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/onboarding.step.ts b/packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/onboarding.step.ts deleted file mode 100644 index 67b20b6e52c8..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/e2e/step-definitions/onboarding.step.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Given, When, Then } from '@cucumber/cucumber'; -import { expect } from '@playwright/test'; -import { ICustomWorld } from '../../../../../../playwright-helpers'; -import { ConfigParams, getUrl, setupNetwork } from '../utils'; - -Given('User has {int} elements in the Listing page', function( - this: ICustomWorld, - nb: number, -) { - this.handlersConfig.nb = nb; -}); - -When('User navigates to Listing page', async function( - this: ICustomWorld, -) { - await setupNetwork(this); - await this.page.goto(getUrl('listing'), { waitUntil: 'load' }); -}); - -Then('User gets redirected to Onboarding page', async function( - this: ICustomWorld, -) { - await expect(this.page).toHaveURL(getUrl('onboarding')); -}); - -Then('User sees {int} guides', async function( - this: ICustomWorld, - nbGuides: number, -) { - const guides = await this.page.locator('osds-tile'); - await expect(guides).toHaveCount(nbGuides); -}); diff --git a/packages/manager/apps/pci-ai-endpoints/e2e/utils/constants.ts b/packages/manager/apps/pci-ai-endpoints/e2e/utils/constants.ts deleted file mode 100644 index 866e6dadabe9..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/e2e/utils/constants.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { urls } from '../../src/routes/routes.constant'; - -export const appUrl = 'http://localhost:9001/app'; - -export type AppRoute = keyof typeof urls; - -export const getUrl = (route: AppRoute) => `${appUrl}/#${urls[route]}`; diff --git a/packages/manager/apps/pci-ai-endpoints/e2e/utils/index.tsx b/packages/manager/apps/pci-ai-endpoints/e2e/utils/index.tsx deleted file mode 100644 index 24a453c58aa9..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/e2e/utils/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export * from './network'; -export * from './constants'; diff --git a/packages/manager/apps/pci-ai-endpoints/e2e/utils/network.ts b/packages/manager/apps/pci-ai-endpoints/e2e/utils/network.ts deleted file mode 100644 index 46d894b9a08e..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/e2e/utils/network.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { BrowserContext } from '@playwright/test'; -import { - ICustomWorld, - toPlaywrightMockHandler, - Handler, -} from '../../../../../../playwright-helpers'; -import { - GetAuthenticationMocks, - getAuthenticationMocks, -} from '../../../../../../playwright-helpers/mocks/auth'; -import { getExampleMocks, GetExampleMocksParams } from '../../mocks'; - -export type ConfigParams = GetAuthenticationMocks & GetExampleMocksParams; - -export const getConfig = (params: ConfigParams): Handler[] => - [getAuthenticationMocks, getExampleMocks].flatMap((getMocks) => - getMocks(params), - ); - -export const setupNetwork = async (world: ICustomWorld) => - Promise.all( - getConfig({ - ...((world?.handlersConfig as ConfigParams) || ({} as ConfigParams)), - isAuthMocked: true, - }) - .reverse() - .map(toPlaywrightMockHandler(world.context as BrowserContext)), - ); diff --git a/packages/manager/apps/pci-ai-endpoints/mocks/example/example-data.json b/packages/manager/apps/pci-ai-endpoints/mocks/example/example-data.json deleted file mode 100644 index 16b09c4e47fd..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/mocks/example/example-data.json +++ /dev/null @@ -1,11 +0,0 @@ -[ - { - "id": 20374 - }, - { - "id": 20375 - }, - { - "id": 20379 - } -] diff --git a/packages/manager/apps/pci-ai-endpoints/mocks/example/example.ts b/packages/manager/apps/pci-ai-endpoints/mocks/example/example.ts deleted file mode 100644 index 77c033e7b392..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/mocks/example/example.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Handler } from '../../../../../../playwright-helpers'; -import exampleList from './example-data.json'; - -export type GetExampleMocksParams = { isKo?: boolean; nb?: number }; - -export const getExampleMocks = ({ - isKo, - nb = Number.POSITIVE_INFINITY, -}: GetExampleMocksParams): Handler[] => [ - { - url: '*', - response: isKo - ? { - message: 'Example error', - } - : exampleList.slice(0, nb), - status: isKo ? 500 : 200, - api: 'v6', - }, - { - url: '*', - response: isKo - ? { - message: 'Example error', - } - : exampleList.slice(0, nb), - status: isKo ? 500 : 200, - api: 'v2', - }, -]; diff --git a/packages/manager/apps/pci-ai-endpoints/mocks/index.ts b/packages/manager/apps/pci-ai-endpoints/mocks/index.ts deleted file mode 100644 index 4356d0ac05ac..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/mocks/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './example/example'; diff --git a/packages/manager/apps/pci-ai-endpoints/playwright.config.ts b/packages/manager/apps/pci-ai-endpoints/playwright.config.ts deleted file mode 100644 index feb249bcbe3f..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/playwright.config.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { defineConfig } from '@playwright/test'; - -export default defineConfig({ - workers: 3, - fullyParallel: false, - timeout: 30 * 1000, - reporter: [['html', { open: 'on-failure' }]], - expect: { - timeout: 20000, - }, - use: { - // Collect trace when retrying the failed test. - trace: 'retain-on-failure', - }, - testMatch: '**/*.e2e.ts', - webServer: { - command: 'yarn run dev', - url: 'http://localhost:9000/', - }, -}); From 0def53863fe69007410421a3cf0d971d11c7c0c8 Mon Sep 17 00:00:00 2001 From: Arthur Bullet Date: Thu, 14 Nov 2024 12:16:15 +0100 Subject: [PATCH 09/17] feat(ai.endpoints): rework onboarding page Signed-off-by: Arthur Bullet --- .../onboarding/Messages_fr_FR.json | 11 +--- .../src/hooks/breadcrumb/useBreadcrumb.tsx | 6 +- .../src/pages/onboarding/index.tsx | 62 ++++-------------- .../src/pages/onboarding/onboarding-img.png | Bin 8250 -> 57782 bytes 4 files changed, 19 insertions(+), 60 deletions(-) diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json index f74cde56bf32..b879d3436ac2 100644 --- a/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json @@ -1,12 +1,7 @@ { - "title": "Enhance your applications with AI Endpoints", - "description": "Designed with simplicity in mind, our platform allows developers of all skill levels to enhance their applications with cutting-edge AI APIs — no AI expertise required.", - "description1": "These endpoints require", - "description2": "no AI expertise", - "description3": "or dedicated infrastructure, as the serverless platform provides", - "description4": "access to advanced AI models", - "description5": "including Large Language Models (LLMs), natural language processing, translation, speech recognition, image recognition, and more. Developers can select from a range of models, including", - "description6": "open-source options like Mistral AI, Llama and Stable Diffusion.", + "title": "Améliorez vos applications avec AI Endpoints", + "description": "Conçu avec la simplicité à l'esprit, notre plateforme permet aux développeurs de tous niveaux de compétence d'améliorer leurs applications avec des API IA de pointe — aucune expertise en IA requise.", + "descriptionBis": "Ces API n'exigent aucune expertise en IA ni d'infrastructure dédiée, car la plateforme serverless permet d'accéder à des modèles d'IA avancés, notamment les modèles de langage (LLMs), le traitement du langage naturel, la traduction, la reconnaissance vocale, la reconnaissance d'images et bien plus encore. Les développeurs peuvent choisir parmi une gamme de modèles, en particlier des options en open source comme Mistral AI, Llama et Stable Diffusion..", "goToAiEndpoint": "AI Endpoints", "guideCategory": "Guide", "guide1Title": "Premiers pas avec AI Endpoint", diff --git a/packages/manager/apps/pci-ai-endpoints/src/hooks/breadcrumb/useBreadcrumb.tsx b/packages/manager/apps/pci-ai-endpoints/src/hooks/breadcrumb/useBreadcrumb.tsx index 16b6b92a524b..903e38e54e85 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/hooks/breadcrumb/useBreadcrumb.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/hooks/breadcrumb/useBreadcrumb.tsx @@ -12,13 +12,13 @@ export interface BreadcrumbProps { rootLabel?: string; appName?: string; projectId?: string; - items?: BreadcrumbItem[]; + // items?: BreadcrumbItem[]; } export const usePciBreadcrumb = ({ projectId, appName, - items, -}: BreadcrumbProps) => { +}: // items, +BreadcrumbProps) => { const { shell } = useContext(ShellContext); const [root, setRoot] = useState([]); const [appRoot, setAppRoot] = useState([]); diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.tsx b/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.tsx index 60e3d1344fa9..9a686b639988 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/index.tsx @@ -49,6 +49,7 @@ export default function Onboarding() { const imgSrc = { src: onboardingImgSrc, }; + const descBis: string = t('descriptionBis'); return ( <> @@ -66,55 +67,18 @@ export default function Onboarding() { > {t('description')} -
-
- - {t('description1')} - {' '} - - {t('description2')} - {' '} - - {t('description3')} - {' '} - - {t('description4')} - {' '} - - {t('description5')} - {' '} - - {t('description6')} - -
-
+ + + } moreInfoButtonLabel={t('goToAiEndpoint')} diff --git a/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/onboarding-img.png b/packages/manager/apps/pci-ai-endpoints/src/pages/onboarding/onboarding-img.png index 1ac8d6473c95008e4a957539d3567870852b874e..e2fe738374015364a8126dfd18cd0aee7f53215c 100644 GIT binary patch literal 57782 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 ee)xYs%?6m3XQK+~8 literal 8250 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 KUaD&K;y(a7f*-B` From 8e3a4a4651f5368abe9ab08810ca7386ca3856f0 Mon Sep 17 00:00:00 2001 From: Arthur Bullet Date: Thu, 14 Nov 2024 14:52:42 +0100 Subject: [PATCH 10/17] feat(ai.endpoints): fix build issue Signed-off-by: Arthur Bullet --- .../manager/apps/pci-ai-endpoints/package.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/manager/apps/pci-ai-endpoints/package.json b/packages/manager/apps/pci-ai-endpoints/package.json index 1e2886549f46..e2818033c13d 100644 --- a/packages/manager/apps/pci-ai-endpoints/package.json +++ b/packages/manager/apps/pci-ai-endpoints/package.json @@ -20,16 +20,16 @@ "test:e2e:cii": "tsc && node ../../../../scripts/run-playwright-bdd.js --ci" }, "dependencies": { - "@ovh-ux/manager-config": "^7.5.3-alpha.0", - "@ovh-ux/manager-core-api": "^0.9.0-alpha.0", + "@ovh-ux/manager-config": "^8.0.0", + "@ovh-ux/manager-core-api": "^0.9.0", "@ovh-ux/manager-core-utils": "*", - "@ovh-ux/manager-pci-common": "^0.8.0-alpha.3", - "@ovh-ux/manager-react-components": "^1.41.0-alpha.2", - "@ovh-ux/manager-react-core-application": "^0.10.8-alpha.0", - "@ovh-ux/manager-react-shell-client": "^0.8.0-alpha.0", + "@ovh-ux/manager-pci-common": "^0.8.1", + "@ovh-ux/manager-react-components": "^1.41.1", + "@ovh-ux/manager-react-core-application": "^0.11.1", + "@ovh-ux/manager-react-shell-client": "^0.8.1", "@ovh-ux/manager-tailwind-config": "*", - "@ovh-ux/request-tagger": "^0.4.0-alpha.0", - "@ovh-ux/shell": "^3.11.0-alpha.0", + "@ovh-ux/request-tagger": "^0.4.0", + "@ovh-ux/shell": "^4.0.1", "@ovhcloud/ods-common-core": "17.2.1", "@ovhcloud/ods-common-theming": "17.2.1", "@ovhcloud/ods-components": "17.2.1", From 740a7b923a079ac79184c1315fd348dffee50e19 Mon Sep 17 00:00:00 2001 From: Arthur Bullet Date: Thu, 14 Nov 2024 16:32:51 +0100 Subject: [PATCH 11/17] feat(ai.endpoints): fix pr comments Signed-off-by: Arthur Bullet --- .../apps/pci-ai-endpoints/src/components/Error/Error.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/manager/apps/pci-ai-endpoints/src/components/Error/Error.tsx b/packages/manager/apps/pci-ai-endpoints/src/components/Error/Error.tsx index 66f2a411a7fa..f3c146ce05c2 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/components/Error/Error.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/components/Error/Error.tsx @@ -4,8 +4,8 @@ import { ShellContext } from '@ovh-ux/manager-react-shell-client'; import { ErrorMessage, TRACKING_LABELS, -} from '@ovh-ux/manager-react-components/src/components/'; -import { ErrorBanner } from '@ovh-ux/manager-react-components'; + ErrorBanner, +} from '@ovh-ux/manager-react-components'; interface ErrorObject { [key: string]: any; From 526ee5dc8c88161efc6abebd4073c580c86c3405 Mon Sep 17 00:00:00 2001 From: Arthur Bullet Date: Mon, 18 Nov 2024 16:55:35 +0100 Subject: [PATCH 12/17] feat(ai.endpoints): update guide link Signed-off-by: Arthur Bullet --- .../onboarding/Messages_fr_FR.json | 14 ++-- .../src/hooks/guide/useGuideUtils.tsx | 64 +++++++++++-------- 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json index b879d3436ac2..b2562ac5bc0d 100644 --- a/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_FR.json @@ -4,11 +4,11 @@ "descriptionBis": "Ces API n'exigent aucune expertise en IA ni d'infrastructure dédiée, car la plateforme serverless permet d'accéder à des modèles d'IA avancés, notamment les modèles de langage (LLMs), le traitement du langage naturel, la traduction, la reconnaissance vocale, la reconnaissance d'images et bien plus encore. Les développeurs peuvent choisir parmi une gamme de modèles, en particlier des options en open source comme Mistral AI, Llama et Stable Diffusion..", "goToAiEndpoint": "AI Endpoints", "guideCategory": "Guide", - "guide1Title": "Premiers pas avec AI Endpoint", - "guide1Description": "Découvrez comment utiliser AI Endpoints", - "guide2Title": "Boostez vos applications avec AI Endpoint", - "guide2Description": "Découvrez comment utiliser AI Endpoint dans vos applications", - "tutoCategory": "Tutorials", - "guide3Title": "Decouvrir nos tutos sur mesure", - "guide3Description": "Découvrez comment utiliser AI Endpoint avec des tutos sur mesure" + "guide1Title": "Premiers pas avec AI Endpoints", + "guide1Description": "Découvrez AI Endpoints, la plateforme serverless sécurisée d'OVHcloud pour les développeurs qui permet d'accéder aux meilleurs modèles d'intelligence artificielle grâce à des API faciles à utiliser. Aucune expertise en IA n'est nécessaire.", + "guide2Title": "Fonctionnalités, capacités et limitations", + "guide2Description": "Découvrez les fonctionnalités, capacités et limitations actuelles d'AI Endpoints", + "tutoCategory": "Tutoriels", + "guide3Title": "Cas d'usage avec AI Endpoints", + "guide3Description": "Explorez nos tutoriels pour enrichir vos applications avec AI Endpoints. Apprenez comment créer des chatbots, intégrer des LLM ou des outils pour des solutions d'IA puissantes." } diff --git a/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx b/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx index c4da839d62c3..24d3b3dbbae5 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx @@ -2,40 +2,50 @@ import { useContext, useEffect, useState } from 'react'; import { CountryCode } from '@ovh-ux/manager-config'; import { ShellContext } from '@ovh-ux/manager-react-shell-client'; -const docUrl = 'https://docs.ovh.com'; +const docUrl = 'https://help.ovhcloud.com'; type GuideLinks = { [key in CountryCode]: string }; const GUIDE_LIST: { [guideName: string]: Partial } = { guideLink1: { - DE: '/update-path', - ES: '/update-path', - IE: '/en/update-path', - IT: '/update-path', - PL: '/update-path', - PT: '/update-path', - FR: '/update-path', - GB: '/update-path', - CA: '/update-path', - QC: '/update-path', - WE: '/update-path', - WS: '/update-path', - US: '/update-path', + DE: + '/csm/de-public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065406', + ES: + '/csm/es--public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065402', + IE: + '/csm/en-ie-public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065398', + IT: + '/csm/it-public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065399', + PL: + '/csm/pl-public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065419', + PT: + '/csm/pt-public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065400', + FR: + '/csm/fr-public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065411', + GB: + '/csm/en-gb-public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065401', + CA: + '/csm/fr-ca-public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065405', }, guideLink2: { - DE: '/guide-link-2-path', - ES: '/guide-link-2-path', - IE: '/en/guide-link-2-path', - IT: '/guide-link-2-path', - PL: '/guide-link-2-path', - PT: '/guide-link-2-path', - FR: '/guide-link-2-path', - GB: '/guide-link-2-path', - CA: '/update-path', - QC: '/update-path', - WE: '/update-path', - WS: '/update-path', - US: '/update-path', + DE: + '/csm/de-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065404', + ES: + '/csm/es-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065425', + IE: + '/csm/en-ie-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065412', + IT: + '/csm/it-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065422', + PL: + '/csm/pl-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065423', + PT: + '/csm/pt-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065426', + FR: + '/csm/fr-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065424', + GB: + '/csm/en-gb-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065417', + CA: + '/csm/fr-ca-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065416', }, }; From a4b3d68ed928480a0bf2deffc79a62544898d101 Mon Sep 17 00:00:00 2001 From: Arthur Bullet Date: Tue, 19 Nov 2024 08:53:39 +0100 Subject: [PATCH 13/17] feat(ai.endpoints): update pci menu to display beta badge Signed-off-by: Arthur Bullet --- .../legacy/server-sidebar/universe/public-cloud/pci-menu.ts | 4 ++-- .../sidebar/navigation-tree/services/publicCloud.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/manager/apps/container/src/container/legacy/server-sidebar/universe/public-cloud/pci-menu.ts b/packages/manager/apps/container/src/container/legacy/server-sidebar/universe/public-cloud/pci-menu.ts index 8a7dabadda19..a1093de52e27 100644 --- a/packages/manager/apps/container/src/container/legacy/server-sidebar/universe/public-cloud/pci-menu.ts +++ b/packages/manager/apps/container/src/container/legacy/server-sidebar/universe/public-cloud/pci-menu.ts @@ -387,12 +387,12 @@ export function getPciProjectMenu( isFeaturesAvailable('pci-ai-endpoints') ? { id: 'pci-ai-endpoints', title: 'AI Endpoints', - badge: 'alpha', + badge: 'beta', href: getURL('public-cloud', `#/pci/projects/${projectId}/ai/endpoints`), } : { id: 'ai-endpoints', title: 'AI Endpoints', - badge: 'alpha', + badge: 'beta', href: 'https://endpoints.ai.cloud.ovh.net/', external: true, }, diff --git a/packages/manager/apps/container/src/container/nav-reshuffle/sidebar/navigation-tree/services/publicCloud.ts b/packages/manager/apps/container/src/container/nav-reshuffle/sidebar/navigation-tree/services/publicCloud.ts index 77b03743d729..8e09566e1f94 100644 --- a/packages/manager/apps/container/src/container/nav-reshuffle/sidebar/navigation-tree/services/publicCloud.ts +++ b/packages/manager/apps/container/src/container/nav-reshuffle/sidebar/navigation-tree/services/publicCloud.ts @@ -559,7 +559,7 @@ pciNode.children = [ count: false, url: 'https://endpoints.ai.cloud.ovh.net/', features: ['ai-endpoints'], - tag: NodeTag.ALPHA, + tag: NodeTag.BETA, isExternal: true, }, { @@ -573,7 +573,7 @@ pciNode.children = [ }, features: ['pci-ai-endpoints'], forceVisibility: true, - tag: NodeTag.ALPHA, + tag: NodeTag.BETA, }, ], }, From 4da30e4a9cf24f76441d3252096a6e37fcd4ea5f Mon Sep 17 00:00:00 2001 From: Arthur Bullet Date: Tue, 19 Nov 2024 09:37:21 +0100 Subject: [PATCH 14/17] feat(ai.endpoints): fix pr comments Signed-off-by: Arthur Bullet --- .../manager/apps/pci-ai-endpoints/cucumber.js | 20 ------------------- .../apps/pci-ai-endpoints/package.json | 8 ++++---- 2 files changed, 4 insertions(+), 24 deletions(-) delete mode 100644 packages/manager/apps/pci-ai-endpoints/cucumber.js diff --git a/packages/manager/apps/pci-ai-endpoints/cucumber.js b/packages/manager/apps/pci-ai-endpoints/cucumber.js deleted file mode 100644 index 8e6abbfbca8f..000000000000 --- a/packages/manager/apps/pci-ai-endpoints/cucumber.js +++ /dev/null @@ -1,20 +0,0 @@ -const isCI = process.env.CI; - -module.exports = { - default: { - paths: ['e2e/features/**/*.feature'], - require: [ - '../../../../playwright-helpers/bdd-setup.ts', - 'e2e/**/*.step.ts', - ], - requireModule: ['ts-node/register'], - format: [ - 'summary', - isCI ? 'progress' : 'progress-bar', - !isCI && ['html', 'e2e/reports/cucumber-results-report.html'], - !isCI && ['usage-json', 'e2e/reports/cucumber-usage-report.json'], - ].filter(Boolean), - formatOptions: { snippetInterface: 'async-await' }, - retry: 1, - }, -}; diff --git a/packages/manager/apps/pci-ai-endpoints/package.json b/packages/manager/apps/pci-ai-endpoints/package.json index e2818033c13d..8fae46a3865b 100644 --- a/packages/manager/apps/pci-ai-endpoints/package.json +++ b/packages/manager/apps/pci-ai-endpoints/package.json @@ -32,10 +32,6 @@ "@ovh-ux/shell": "^4.0.1", "@ovhcloud/ods-common-core": "17.2.1", "@ovhcloud/ods-common-theming": "17.2.1", - "@ovhcloud/ods-components": "17.2.1", - "@ovhcloud/ods-theme-blue-jeans": "17.2.1", - "@tanstack/react-query": "^5.51.21", - "@tanstack/react-query-devtools": "^5.51.21", "axios": "^1.1.2", "clsx": "^1.2.1", "i18next": "^23.8.2", @@ -49,7 +45,11 @@ "devDependencies": { "@cucumber/cucumber": "^10.3.1", "@ovh-ux/manager-vite-config": "*", + "@ovhcloud/ods-components": "17.2.1", + "@ovhcloud/ods-theme-blue-jeans": "17.2.1", "@playwright/test": "^1.41.2", + "@tanstack/react-query": "^5.51.21", + "@tanstack/react-query-devtools": "^5.51.21", "typescript": "^5.1.6", "vite": "^5.2.13" }, From 2f5057ee537266105c78f32a93acddfeecb21cce Mon Sep 17 00:00:00 2001 From: Arthur Bullet Date: Tue, 19 Nov 2024 11:46:28 +0100 Subject: [PATCH 15/17] feat(ai.endpoints): fix ca guide link issue Signed-off-by: Arthur Bullet --- .../src/hooks/guide/useGuideUtils.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx b/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx index 24d3b3dbbae5..1793a1589cbb 100644 --- a/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx +++ b/packages/manager/apps/pci-ai-endpoints/src/hooks/guide/useGuideUtils.tsx @@ -25,7 +25,15 @@ const GUIDE_LIST: { [guideName: string]: Partial } = { GB: '/csm/en-gb-public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065401', CA: + '/csm/en-ca-public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065397', + QC: '/csm/fr-ca-public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065405', + WE: + '/csm/en-ca-public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065397', + WS: + '/csm/en-ca-public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065397', + US: + '/csm/en-public-cloud-ai-endpoints-getting-started?id=kb_article_view&sysparm_article=KB0065403', }, guideLink2: { DE: @@ -45,7 +53,15 @@ const GUIDE_LIST: { [guideName: string]: Partial } = { GB: '/csm/en-gb-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065417', CA: + '/csm/en-ca-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065420', + QC: '/csm/fr-ca-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065416', + WE: + '/csm/en-ca-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065420', + WS: + '/csm/en-ca-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065420', + US: + '/csm/en-public-cloud-ai-endpoints-capabilities?id=kb_article_view&sysparm_article=KB0065421', }, }; From 1e684683dc5b8e91b4ff3211ae812ed5bd463274 Mon Sep 17 00:00:00 2001 From: CDS Translator Agent Date: Thu, 21 Nov 2024 08:34:45 +0000 Subject: [PATCH 16/17] fix(i18n): add missing translations [CDS 133] Signed-off-by: CDS Translator Agent --- .../translations/onboarding/Messages_de_DE.json | 14 ++++++++++++++ .../translations/onboarding/Messages_en_GB.json | 14 ++++++++++++++ .../translations/onboarding/Messages_es_ES.json | 14 ++++++++++++++ .../translations/onboarding/Messages_fr_CA.json | 14 ++++++++++++++ .../translations/onboarding/Messages_it_IT.json | 14 ++++++++++++++ .../translations/onboarding/Messages_pl_PL.json | 14 ++++++++++++++ .../translations/onboarding/Messages_pt_PT.json | 14 ++++++++++++++ .../pci-ai-endpoints/Messages_de_DE.json | 6 ++++++ .../pci-ai-endpoints/Messages_en_GB.json | 6 ++++++ .../pci-ai-endpoints/Messages_es_ES.json | 6 ++++++ .../pci-ai-endpoints/Messages_fr_CA.json | 6 ++++++ .../pci-ai-endpoints/Messages_it_IT.json | 6 ++++++ .../pci-ai-endpoints/Messages_pl_PL.json | 6 ++++++ .../pci-ai-endpoints/Messages_pt_PT.json | 6 ++++++ .../pci-ai-endpoints/error/Messages_de_DE.json | 8 ++++++++ .../pci-ai-endpoints/error/Messages_en_GB.json | 8 ++++++++ .../pci-ai-endpoints/error/Messages_es_ES.json | 8 ++++++++ .../pci-ai-endpoints/error/Messages_fr_CA.json | 8 ++++++++ .../pci-ai-endpoints/error/Messages_it_IT.json | 8 ++++++++ .../pci-ai-endpoints/error/Messages_pl_PL.json | 8 ++++++++ .../pci-ai-endpoints/error/Messages_pt_PT.json | 8 ++++++++ 21 files changed, 196 insertions(+) create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_de_DE.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_en_GB.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_es_ES.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_CA.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_it_IT.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_pl_PL.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_pt_PT.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_de_DE.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_en_GB.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_es_ES.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_fr_CA.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_it_IT.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_pl_PL.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_pt_PT.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_de_DE.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_en_GB.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_es_ES.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_fr_CA.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_it_IT.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_pl_PL.json create mode 100644 packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_pt_PT.json diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_de_DE.json b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_de_DE.json new file mode 100644 index 000000000000..24598e80f1bb --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_de_DE.json @@ -0,0 +1,14 @@ +{ + "title": "Verbessern Sie Ihre Anwendungen mit AI Endpoints", + "description": "Unsere einfach zu bedienende Plattform ermöglicht es Entwicklern aller Stufen, ihre Anwendungen mit AI-APIs auf dem neuesten Stand der Technik zu verbessern - ohne KI-Fachwissen.", + "descriptionBis": "Für diese APIs ist keinerlei KI-Expertise oder dedizierte Infrastruktur erforderlich, da die Serverless-Plattform Zugriff auf fortgeschrittene KI-Modelle bietet, darunter Sprachmodelle (LLMs), natürliche Sprachverarbeitung, Übersetzung, Spracherkennung, Bilderkennung und vieles mehr. Entwickler können aus einer Reihe von Modellen wählen, insbesondere Open-Source-Optionen wie Mistral AI, Llama und Stable Diffusion. .", + "goToAiEndpoint": "AI Endpoints", + "guideCategory": "Anleitung", + "guide1Title": "Erste Schritte mit AI Endpoints", + "guide1Description": "Entdecken Sie AI Endpoints, die sichere Serverless-Plattform von OVHcloud für Entwickler, die über benutzerfreundliche APIs Zugriff auf die besten Modelle für künstliche Intelligenz bietet. Es ist keine KI-Expertise erforderlich.", + "guide2Title": "Funktionen, Kapazitäten und Einschränkungen", + "guide2Description": "Entdecken Sie die aktuellen Funktionen, Kapazitäten und Einschränkungen von AI Endpoints", + "tutoCategory": "Tutorials ", + "guide3Title": "Use Cases mit AI Endpoints", + "guide3Description": "Entdecken Sie unsere Tutorials, um Ihre Anwendungen mit AI Endpoints zu erweitern. Hier erfahren Sie, wie Sie Chatbots erstellen, LLMs oder Tools für leistungsstarke KI-Lösungen integrieren." +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_en_GB.json b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_en_GB.json new file mode 100644 index 000000000000..d3ea47d8c94f --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_en_GB.json @@ -0,0 +1,14 @@ +{ + "title": "Enhance your applications with AI Endpoints", + "description": "Designed with simplicity in mind, our platform allows developers of all skill levels to enhance their applications with cutting-edge AI APIs — no AI expertise required.", + "descriptionBis": "These APIs require no AI expertise or dedicated infrastructure, as the serverless platform provides access to advanced AI models , including language models (LLMs), natural language processing, translation, voice recognition, image recognition, and much more. Developers can choose from a range of models, particularly open-source options like Mistral AI, Llama and Stable Diffusion. .", + "goToAiEndpoint": "AI Endpoints", + "guideCategory": "Guide", + "guide1Title": "Getting started with AI Endpoints", + "guide1Description": "Discover AI Endpoints, OVHcloud's secure server platform for developers that provides access to the best AI models through easy-to-use APIs. No AI expertise is required.", + "guide2Title": "Features, capabilities and limitations", + "guide2Description": "Discover the current features, capabilities and limitations of AI Endpoints", + "tutoCategory": "Tutorials ", + "guide3Title": "Use cases with AI Endpoints", + "guide3Description": "Explore our tutorials to enrich your applications with AI Endpoints. Learn how to build chatbots, integrate LLM or tools for powerful AI solutions." +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_es_ES.json b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_es_ES.json new file mode 100644 index 000000000000..1a18a1edf51b --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_es_ES.json @@ -0,0 +1,14 @@ +{ + "title": "Mejore sus aplicaciones con AI Endpoints", + "description": "Diseñada con la simplicidad en mente, nuestra plataforma permite a los desarrolladores de todos los niveles de habilidad mejorar sus aplicaciones con API de IA de vanguardia, sin necesidad de conocimientos en IA.", + "descriptionBis": "Estas API no requieren conocimientos de IA ni de infraestructura dedicada, ya que la plataforma serverless permite acceder a modelos de IA avanzados, como los modelos de lenguaje (LLM), procesamiento de lenguaje natural, traducción, reconocimiento de voz, reconocimiento de imágenes, etc. Los desarrolladores pueden elegir entre una gama de modelos, en particular opciones de código abierto como Mistral AI, Llama y Stable Diffusion. .", + "goToAiEndpoint": "AI Endpoints", + "guideCategory": "Guía", + "guide1Title": "Primeros pasos con AI Endpoints", + "guide1Description": "Descubra AI Endpoints, la plataforma de servidores seguros para desarrolladores de OVHcloud que permite acceder a los mejores modelos de inteligencia artificial gracias a las API, fáciles de utilizar. No es necesario tener experiencia en IA.", + "guide2Title": "Funcionalidades, capacidades y limitaciones", + "guide2Description": "Descubra las funcionalidades, capacidades y limitaciones actuales de AI Endpoints", + "tutoCategory": "Tutoriales", + "guide3Title": "Casos de uso con AI Endpoints", + "guide3Description": "Explore nuestros tutoriales para enriquecer sus aplicaciones con AI Endpoints. Aprenda a crear chatbots, integrar LLM o herramientas para potentes soluciones de IA." +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_CA.json b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_CA.json new file mode 100644 index 000000000000..b2562ac5bc0d --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_fr_CA.json @@ -0,0 +1,14 @@ +{ + "title": "Améliorez vos applications avec AI Endpoints", + "description": "Conçu avec la simplicité à l'esprit, notre plateforme permet aux développeurs de tous niveaux de compétence d'améliorer leurs applications avec des API IA de pointe — aucune expertise en IA requise.", + "descriptionBis": "Ces API n'exigent aucune expertise en IA ni d'infrastructure dédiée, car la plateforme serverless permet d'accéder à des modèles d'IA avancés, notamment les modèles de langage (LLMs), le traitement du langage naturel, la traduction, la reconnaissance vocale, la reconnaissance d'images et bien plus encore. Les développeurs peuvent choisir parmi une gamme de modèles, en particlier des options en open source comme Mistral AI, Llama et Stable Diffusion..", + "goToAiEndpoint": "AI Endpoints", + "guideCategory": "Guide", + "guide1Title": "Premiers pas avec AI Endpoints", + "guide1Description": "Découvrez AI Endpoints, la plateforme serverless sécurisée d'OVHcloud pour les développeurs qui permet d'accéder aux meilleurs modèles d'intelligence artificielle grâce à des API faciles à utiliser. Aucune expertise en IA n'est nécessaire.", + "guide2Title": "Fonctionnalités, capacités et limitations", + "guide2Description": "Découvrez les fonctionnalités, capacités et limitations actuelles d'AI Endpoints", + "tutoCategory": "Tutoriels", + "guide3Title": "Cas d'usage avec AI Endpoints", + "guide3Description": "Explorez nos tutoriels pour enrichir vos applications avec AI Endpoints. Apprenez comment créer des chatbots, intégrer des LLM ou des outils pour des solutions d'IA puissantes." +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_it_IT.json b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_it_IT.json new file mode 100644 index 000000000000..9e0c46d91339 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_it_IT.json @@ -0,0 +1,14 @@ +{ + "title": "Migliora le tue applicazioni con AI Endpoint", + "description": "Progettata per semplicità, la nostra piattaforma consente agli sviluppatori di tutti i livelli di competenza di migliorare le proprie applicazioni con API di IA all'avanguardia: non sono necessarie competenze di IA.", + "descriptionBis": "Queste API non richiedono competenze di IA o un'infrastruttura dedicata, in quanto la piattaforma serverless consente l'accesso a modelli di IA avanzati, tra cui i modelli di linguaggio (LLMs), l'elaborazione del linguaggio naturale, la traduzione, il riconoscimento vocale, il riconoscimento delle immagini e altro ancora. Gli sviluppatori possono scegliere tra una vasta gamma di modelli, in particolare opzioni open source come Mistral AI, Llama e Stable Diffusion. .", + "goToAiEndpoint": "AI Endpoints", + "guideCategory": "Guida", + "guide1Title": "Iniziare a utilizzare AI Endpoints", + "guide1Description": "Scopri AI Endpoints, la piattaforma server sicura di OVHcloud per sviluppatori che permette di accedere ai migliori modelli di Intelligenza Artificiale grazie ad API di facile utilizzo. Non è necessario possedere competenze in IA.", + "guide2Title": "Funzionalità, funzionalità e limitazioni", + "guide2Description": "Scopri le funzionalità, le funzionalità e le limitazioni attuali di AI Endpoint", + "tutoCategory": "Tutorial", + "guide3Title": "Casi pratici con AI Endpoints", + "guide3Description": "Esplora i tutorial per aggiungere valore alle tue applicazioni con AI Endpoints. Scopri come creare chatbot, integrare LLM o strumenti per potenti soluzioni di IA." +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_pl_PL.json b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_pl_PL.json new file mode 100644 index 000000000000..4d3ba2ebdf7a --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_pl_PL.json @@ -0,0 +1,14 @@ +{ + "title": "Ulepsz swoje aplikacje za pomocą AI Endpoints", + "description": "Nasza platforma, zaprojektowana z myślą o prostocie, pozwala programistom o różnych umiejętnościach ulepszać aplikacje za pomocą najnowocześniejszych interfejsów API AI — bez konieczności posiadania specjalistycznej wiedzy w zakresie sztucznej inteligencji.", + "descriptionBis": "Interfejsy API nie wymagają wiedzy w zakresie AI ani dedykowanej infrastruktury, ponieważ platforma serverless umożliwia dostęp do zaawansowanych modeli AI, w tym modeli językowych (LLM), przetwarzania języka naturalnego, tłumaczeń, rozpoznawania mowy, rozpoznawania obrazów i wielu innych. Programiści mogą wybierać spośród gamy modeli, w szczególności opcje open source, takie jak Mistral AI, Llama i Stable Diffusion. .", + "goToAiEndpoint": "AI Endpoints", + "guideCategory": "Przewodnik", + "guide1Title": "Pierwsze kroki z AI Endpoints", + "guide1Description": "Odkryj AI Endpoints, bezpieczną platformę serwerową OVHcloud dla programistów, która umożliwia dostęp do najlepszych modeli sztucznej inteligencji za pomocą łatwych w użyciu interfejsów API. Doświadczenie w zakresie sztucznej inteligencji nie jest wymagane.", + "guide2Title": "Funkcje, możliwości i ograniczenia", + "guide2Description": "Poznaj aktualne funkcje, możliwości i ograniczenia rozwiązania AI Endpoints", + "tutoCategory": "Dokumentacja", + "guide3Title": "Przykłady zastosowania z AI Endpoints", + "guide3Description": "Przejrzyj nasze tutoriale, aby wzbogacić Twoje aplikacje za pomocą AI Endpoints. Dowiedz się, jak tworzyć chatbota, integrować LLM lub narzędzia do wydajnych rozwiązań AI." +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_pt_PT.json b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_pt_PT.json new file mode 100644 index 000000000000..4292720f4a1c --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/onboarding/Messages_pt_PT.json @@ -0,0 +1,14 @@ +{ + "title": "Melhore as suas aplicações com o AI Endpoints", + "description": "Concebida com a simplicidade em mente, a nossa plataforma permite que os programadores de todos os níveis de competência melhorem as suas aplicações com APIs de IA de ponta — sem conhecimentos de IA necessários.", + "descriptionBis": "Estas APIs não requerem conhecimentos especializados em IA nem infraestruturas dedicadas, uma vez que a plataforma serverless permite aceder a modelos de IA avançados, nomeadamente os modelos de linguagem (LLMs), o tratamento da linguagem natural, a tradução, o reconhecimento de voz, o reconhecimento de imagens e muito mais. Os programadores podem escolher entre uma gama de modelos, em particular opções em open source como o Mistral AI, o Llama e o Stable Diffusion. .", + "goToAiEndpoint": "AI Endpoints", + "guideCategory": "Manual", + "guide1Title": "Primeiros passos com o AI Endpoints", + "guide1Description": "Descubra o AI Endpoints, a plataforma de servidores segura da OVHcloud para programadores que permite aceder aos melhores modelos de inteligência artificial graças a API fáceis de utilizar. Não é necessária qualquer experiência em IA.", + "guide2Title": "Funcionalidades, capacidades e limitações", + "guide2Description": "Descubra as funcionalidades, capacidades e limitações atuais do AI Endpoints", + "tutoCategory": "Tutoriais", + "guide3Title": "Casos práticos com o AI Endpoints", + "guide3Description": "Explore os nossos tutoriais para enriquecer as suas aplicações com o AI Endpoints. Saiba como criar chatbots, integrar LLMs ou ferramentas para soluções de IA potentes." +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_de_DE.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_de_DE.json new file mode 100644 index 000000000000..1f78f3097d12 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_de_DE.json @@ -0,0 +1,6 @@ +{ + "title": "Willkommen bei der UAPP", + "crumb": "AI-Endpoints", + "tabs_2": "Tabs 2", + "onboarding": "Onboarding" +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_en_GB.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_en_GB.json new file mode 100644 index 000000000000..16752777f6e5 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_en_GB.json @@ -0,0 +1,6 @@ +{ + "title": "Welcome to Uapp", + "crumb": "AI-Endpoints", + "tabs_2": "Table 2", + "onboarding": "Onboarding" +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_es_ES.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_es_ES.json new file mode 100644 index 000000000000..49771600c820 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_es_ES.json @@ -0,0 +1,6 @@ +{ + "title": "Bienvenido/a a a la aplicación", + "crumb": "AI-Endpoints", + "tabs_2": "Tabs 2", + "onboarding": "Acompañamiento inicial" +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_fr_CA.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_fr_CA.json new file mode 100644 index 000000000000..b142b36232e5 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_fr_CA.json @@ -0,0 +1,6 @@ +{ + "title": "Bienvenue uapp", + "crumb": "AI-Endpoints", + "tabs_2": "Tabs 2", + "onboarding": "Onboarding" +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_it_IT.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_it_IT.json new file mode 100644 index 000000000000..30c815d04f60 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_it_IT.json @@ -0,0 +1,6 @@ +{ + "title": "Benvenuto in uapp", + "crumb": "AI-Endpoints", + "tabs_2": "Tabs 2", + "onboarding": "Onboarding" +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_pl_PL.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_pl_PL.json new file mode 100644 index 000000000000..dc61659d107b --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_pl_PL.json @@ -0,0 +1,6 @@ +{ + "title": "Witamy", + "crumb": "AI-Endpoints", + "tabs_2": "Tabela 2", + "onboarding": "Onboarding" +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_pt_PT.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_pt_PT.json new file mode 100644 index 000000000000..47a49c099930 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/Messages_pt_PT.json @@ -0,0 +1,6 @@ +{ + "title": "Bem-vindo uapp", + "crumb": "AI-Endpoints", + "tabs_2": "Quadro 2", + "onboarding": "Integração" +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_de_DE.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_de_DE.json new file mode 100644 index 000000000000..bf0fba32976b --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_de_DE.json @@ -0,0 +1,8 @@ +{ + "manager_error_page_title": "Oops...!", + "manager_error_page_button_cancel": "Abbrechen", + "manager_error_page_detail_code": "Fehlercode: ", + "manager_error_page_action_reload_label": "Erneut versuchen", + "manager_error_page_action_home_label": "Zurück zur Startseite", + "manager_error_page_default": "Beim Laden der Seite ist ein Fehler aufgetreten." +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_en_GB.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_en_GB.json new file mode 100644 index 000000000000..b17691e2bc6d --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_en_GB.json @@ -0,0 +1,8 @@ +{ + "manager_error_page_title": "Oops!", + "manager_error_page_button_cancel": "Cancel", + "manager_error_page_detail_code": "Error code: ", + "manager_error_page_action_reload_label": "Try again", + "manager_error_page_action_home_label": "Back to homepage", + "manager_error_page_default": "An error has occurred loading the page." +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_es_ES.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_es_ES.json new file mode 100644 index 000000000000..02cb3fbb1a80 --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_es_ES.json @@ -0,0 +1,8 @@ +{ + "manager_error_page_title": "¡Vaya!", + "manager_error_page_button_cancel": "Cancelar", + "manager_error_page_detail_code": "Código de error: ", + "manager_error_page_action_reload_label": "Volver a intentarlo", + "manager_error_page_action_home_label": "Volver a la página de inicio", + "manager_error_page_default": "Se ha producido un error al cargar la página" +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_fr_CA.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_fr_CA.json new file mode 100644 index 000000000000..2c575c63588e --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_fr_CA.json @@ -0,0 +1,8 @@ +{ + "manager_error_page_title": "Oops …!", + "manager_error_page_button_cancel": "Annuler", + "manager_error_page_detail_code": "Code d'erreur : ", + "manager_error_page_action_reload_label": "Réessayer", + "manager_error_page_action_home_label": "Retour à la page d'accueil", + "manager_error_page_default": "Une erreur est survenue lors du chargement de la page." +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_it_IT.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_it_IT.json new file mode 100644 index 000000000000..ce0517c42dde --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_it_IT.json @@ -0,0 +1,8 @@ +{ + "manager_error_page_title": "Ops!", + "manager_error_page_button_cancel": "Annulla", + "manager_error_page_detail_code": "Codice di errore: ", + "manager_error_page_action_reload_label": "Riprova", + "manager_error_page_action_home_label": "Torna alla home page", + "manager_error_page_default": "Si è verificato un errore durante il caricamento della pagina." +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_pl_PL.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_pl_PL.json new file mode 100644 index 000000000000..6fc5dc1b135d --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_pl_PL.json @@ -0,0 +1,8 @@ +{ + "manager_error_page_title": "Ups ...!", + "manager_error_page_button_cancel": "Anuluj", + "manager_error_page_detail_code": "Kod błędu: ", + "manager_error_page_action_reload_label": "Spróbuj ponownie", + "manager_error_page_action_home_label": "Powrót do strony głównej", + "manager_error_page_default": "Wystąpił błąd podczas ładowania strony." +} diff --git a/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_pt_PT.json b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_pt_PT.json new file mode 100644 index 000000000000..1edd72c0f09a --- /dev/null +++ b/packages/manager/apps/pci-ai-endpoints/public/translations/pci-ai-endpoints/error/Messages_pt_PT.json @@ -0,0 +1,8 @@ +{ + "manager_error_page_title": "Oops!", + "manager_error_page_button_cancel": "Anular", + "manager_error_page_detail_code": "Código de erro: ", + "manager_error_page_action_reload_label": "Tentar novamente", + "manager_error_page_action_home_label": "Voltar à página inicial", + "manager_error_page_default": "Ocorreu um erro aquando do carregamento da página." +} From a38c1e048aebc253f17cdf272ab0b9e3d942ffd1 Mon Sep 17 00:00:00 2001 From: Arthur Bullet Date: Thu, 21 Nov 2024 11:48:50 +0100 Subject: [PATCH 17/17] feat(ai.endpoints): resolve conflict Signed-off-by: Arthur Bullet --- yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index db1caaf4c640..c2b4e92c1417 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25951,7 +25951,7 @@ string-length@^4.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: +string-width@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -29137,4 +29137,4 @@ zustand@^4.5.5: zwitch@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" - integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== + integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== \ No newline at end of file