From 0d748a9e64a0d1bb05e34398651ccada191460e9 Mon Sep 17 00:00:00 2001 From: Arthur Bullet Date: Thu, 4 Jul 2024 17:23:18 +0200 Subject: [PATCH 1/9] feat(pci.ai-notebooks): initializing ai notebooks apps Signed-off-by: Arthur Bullet --- .eslintignore | 1 + .prettierignore | 4 +- .../universe/public-cloud/pci-menu.ts | 3 +- .../manager/apps/pci-ai-notebooks/.gitignore | 2 + .../manager/apps/pci-ai-notebooks/README.md | 3 + .../apps/pci-ai-notebooks/components.json | 17 + .../apps/pci-ai-notebooks/package.json | 127 ++++ .../apps/pci-ai-notebooks/postcss.config.cjs | 6 + .../pci-ai-notebooks/public/assets/oops.png | Bin 0 -> 60234 bytes .../translations/bytes/Messages_de_DE.json | 20 + .../translations/bytes/Messages_en_GB.json | 20 + .../translations/bytes/Messages_es_ES.json | 20 + .../translations/bytes/Messages_fr_CA.json | 20 + .../translations/bytes/Messages_fr_FR.json | 20 + .../translations/bytes/Messages_it_IT.json | 20 + .../translations/bytes/Messages_pl_PL.json | 20 + .../translations/bytes/Messages_pt_PT.json | 20 + .../translations/error/Messages_de_DE.json | 8 + .../translations/error/Messages_en_GB.json | 8 + .../translations/error/Messages_es_ES.json | 8 + .../translations/error/Messages_fr_CA.json | 8 + .../translations/error/Messages_fr_FR.json | 8 + .../translations/error/Messages_it_IT.json | 8 + .../translations/error/Messages_pl_PL.json | 8 + .../translations/error/Messages_pt_PT.json | 8 + .../pci-ai-notebooks/Messages_fr_FR.json | 3 + .../translations/regions/Messages_de_DE.json | 67 ++ .../translations/regions/Messages_en_GB.json | 67 ++ .../translations/regions/Messages_es_ES.json | 67 ++ .../translations/regions/Messages_fr_CA.json | 67 ++ .../translations/regions/Messages_fr_FR.json | 67 ++ .../translations/regions/Messages_it_IT.json | 67 ++ .../translations/regions/Messages_pl_PL.json | 67 ++ .../translations/regions/Messages_pt_PT.json | 67 ++ .../manager/apps/pci-ai-notebooks/src/App.tsx | 34 + .../src/__tests__/helpers/pointerEvent.ts | 19 + .../src/__tests__/helpers/selectHelper.ts | 39 + .../helpers/wrappers/QueryClientWrapper.tsx | 10 + .../wrappers/RouterWithLocationWrapper.tsx | 57 ++ .../src/__tests__/setupTest.ts | 7 + .../src/components/Breadcrumb/Breadcrumb.tsx | 82 +++ .../components/Breadcrumb/BreadcrumbItem.tsx | 12 + .../src/components/Loading/Loading.tsx | 11 + .../src/components/Loading/ProgressLoader.tsx | 35 + .../src/components/PageLayout/PageLayout.tsx | 11 + .../src/components/errorBoundary.tsx | 74 ++ .../pci-ai-notebooks/src/components/links.tsx | 154 ++++ .../src/components/table-date.tsx | 17 + .../src/components/tabs-menu.tsx | 85 +++ .../src/components/ui/accordion.tsx | 56 ++ .../src/components/ui/alert-dialog.tsx | 139 ++++ .../src/components/ui/alert.tsx | 69 ++ .../src/components/ui/badge.tsx | 44 ++ .../src/components/ui/button.tsx | 64 ++ .../src/components/ui/calendar.tsx | 64 ++ .../src/components/ui/card.tsx | 79 ++ .../src/components/ui/collapsible.tsx | 9 + .../src/components/ui/command.tsx | 154 ++++ .../src/components/ui/data-table.tsx | 262 +++++++ .../src/components/ui/dialog.tsx | 120 +++ .../src/components/ui/dropdown-menu.tsx | 218 ++++++ .../src/components/ui/form.tsx | 176 +++++ .../src/components/ui/input.tsx | 25 + .../src/components/ui/label.tsx | 24 + .../src/components/ui/pagination.tsx | 117 +++ .../src/components/ui/popover.tsx | 29 + .../src/components/ui/progress.tsx | 26 + .../src/components/ui/radio-group.tsx | 42 ++ .../src/components/ui/scroll-area.tsx | 46 ++ .../src/components/ui/select.tsx | 158 ++++ .../src/components/ui/skeleton.tsx | 15 + .../src/components/ui/slider.tsx | 26 + .../src/components/ui/sonner.tsx | 29 + .../src/components/ui/switch.tsx | 27 + .../src/components/ui/table.tsx | 117 +++ .../src/components/ui/tabs.tsx | 53 ++ .../src/components/ui/time-picker-input.tsx | 117 +++ .../src/components/ui/time-picker-utils.tsx | 146 ++++ .../src/components/ui/time-picker.tsx | 62 ++ .../src/components/ui/toast.tsx | 127 ++++ .../src/components/ui/toaster.tsx | 33 + .../src/components/ui/tooltip.tsx | 28 + .../src/components/ui/use-toast.ts | 192 +++++ .../src/contexts/loadingIndicatorContext.tsx | 36 + .../pci-ai-notebooks/src/data/api/api.type.ts | 20 + .../pci-ai-notebooks/src/data/api/index.tsx | 43 ++ .../pci-ai-notebooks/src/data/api/projects.ts | 7 + .../src/data/hooks/pciProjects.api.hooks.tsx | 15 + .../apps/pci-ai-notebooks/src/global.css | 116 +++ .../src/hooks/useDateFnsLocale.hook.tsx | 26 + .../pci-ai-notebooks/src/hooks/useLocale.tsx | 35 + .../pci-ai-notebooks/src/hooks/useModale.tsx | 29 + .../src/hooks/useQueryState.tsx | 44 ++ .../pci-ai-notebooks/src/hooks/useUser.tsx | 21 + .../manager/apps/pci-ai-notebooks/src/i18n.ts | 44 ++ .../apps/pci-ai-notebooks/src/index.html | 23 + .../apps/pci-ai-notebooks/src/index.scss | 11 + .../apps/pci-ai-notebooks/src/index.tsx | 52 ++ .../pci-ai-notebooks/src/lib/bytesHelper.ts | 96 +++ .../src/lib/durationHelper.ts | 65 ++ .../apps/pci-ai-notebooks/src/lib/utils.ts | 6 + .../apps/pci-ai-notebooks/src/pages/404.tsx | 3 + .../apps/pci-ai-notebooks/src/pages/_app.tsx | 8 + .../pci-ai-notebooks/src/pages/home/index.tsx | 7 + .../pci-ai-notebooks/src/pages/layout.tsx | 63 ++ .../apps/pci-ai-notebooks/src/query.client.ts | 11 + .../pci-ai-notebooks/src/react-app-env.d.ts | 4 + .../pci-ai-notebooks/src/routes/Router.tsx | 8 + .../pci-ai-notebooks/src/routes/routes.tsx | 39 + .../pci-ai-notebooks/src/types/catalog.ts | 693 ++++++++++++++++++ .../pci-ai-notebooks/src/types/project.ts | 14 + .../apps/pci-ai-notebooks/src/vite-hmr.ts | 5 + .../apps/pci-ai-notebooks/tailwind.config.ts | 304 ++++++++ .../apps/pci-ai-notebooks/tsconfig.build.json | 4 + .../apps/pci-ai-notebooks/tsconfig.json | 30 + .../apps/pci-ai-notebooks/vite.config.mjs | 8 + .../apps/pci-ai-notebooks/vitest.config.js | 35 + 117 files changed, 6389 insertions(+), 2 deletions(-) create mode 100644 packages/manager/apps/pci-ai-notebooks/.gitignore create mode 100644 packages/manager/apps/pci-ai-notebooks/README.md create mode 100644 packages/manager/apps/pci-ai-notebooks/components.json create mode 100644 packages/manager/apps/pci-ai-notebooks/package.json create mode 100644 packages/manager/apps/pci-ai-notebooks/postcss.config.cjs create mode 100644 packages/manager/apps/pci-ai-notebooks/public/assets/oops.png create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_de_DE.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_en_GB.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_es_ES.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_fr_CA.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_it_IT.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_pl_PL.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_pt_PT.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_de_DE.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_en_GB.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_es_ES.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_fr_CA.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_it_IT.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_pl_PL.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_pt_PT.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_de_DE.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_en_GB.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_es_ES.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_fr_CA.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_fr_FR.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_it_IT.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_pl_PL.json create mode 100644 packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_pt_PT.json create mode 100644 packages/manager/apps/pci-ai-notebooks/src/App.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/pointerEvent.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/selectHelper.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/wrappers/QueryClientWrapper.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/wrappers/RouterWithLocationWrapper.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/__tests__/setupTest.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/Breadcrumb/Breadcrumb.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/Breadcrumb/BreadcrumbItem.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/Loading/Loading.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/Loading/ProgressLoader.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/PageLayout/PageLayout.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/errorBoundary.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/links.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/table-date.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/tabs-menu.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/accordion.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/alert-dialog.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/alert.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/badge.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/button.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/calendar.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/card.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/collapsible.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/command.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/data-table.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/dialog.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/dropdown-menu.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/form.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/input.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/label.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/pagination.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/popover.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/progress.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/radio-group.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/scroll-area.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/select.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/skeleton.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/slider.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/sonner.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/switch.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/table.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/tabs.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/time-picker-input.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/time-picker-utils.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/time-picker.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/toast.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/toaster.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/tooltip.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/components/ui/use-toast.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/contexts/loadingIndicatorContext.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/data/api/api.type.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/data/api/index.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/data/api/projects.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/data/hooks/pciProjects.api.hooks.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/global.css create mode 100644 packages/manager/apps/pci-ai-notebooks/src/hooks/useDateFnsLocale.hook.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/hooks/useLocale.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/hooks/useModale.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/hooks/useQueryState.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/hooks/useUser.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/i18n.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/index.html create mode 100644 packages/manager/apps/pci-ai-notebooks/src/index.scss create mode 100644 packages/manager/apps/pci-ai-notebooks/src/index.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/lib/bytesHelper.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/lib/durationHelper.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/lib/utils.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/pages/404.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/pages/_app.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/pages/home/index.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/pages/layout.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/query.client.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/react-app-env.d.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/routes/Router.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/routes/routes.tsx create mode 100644 packages/manager/apps/pci-ai-notebooks/src/types/catalog.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/types/project.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/src/vite-hmr.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/tailwind.config.ts create mode 100644 packages/manager/apps/pci-ai-notebooks/tsconfig.build.json create mode 100644 packages/manager/apps/pci-ai-notebooks/tsconfig.json create mode 100644 packages/manager/apps/pci-ai-notebooks/vite.config.mjs create mode 100644 packages/manager/apps/pci-ai-notebooks/vitest.config.js diff --git a/.eslintignore b/.eslintignore index 1e490c52e9d5..d5b2a13f623d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -17,3 +17,4 @@ packages/manager/core/generator packages/components/ovh-at-internet/src/ovh-at-internet.ts packages/manager/apps/container packages/manager/apps/pci-databases-analytics/src/components/ui +packages/manager/apps/pci-ai-notebooks/src/components/ui diff --git a/.prettierignore b/.prettierignore index 587d59146e0e..d278b1fb052e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,4 +6,6 @@ loader react vue packages/manager/apps/pci-databases-analytics/src/components/ui -packages/manager/apps/pci-databases-analytics/src/lib/utils.ts \ No newline at end of file +packages/manager/apps/pci-databases-analytics/src/lib/utils.ts +packages/manager/apps/pci-ai-notebooks/src/components/ui +packages/manager/apps/pci-ai-notebooks/src/lib/utils.ts 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..76f5b9b80022 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-notebooks' ]; export function getPciProjectMenu( @@ -371,7 +372,7 @@ export function getPciProjectMenu( isFeaturesAvailable('notebooks') && { id: 'notebooks', title: 'AI Notebooks', - href: getURL('public-cloud', `#/pci/projects/${projectId}/notebooks`), + href: getURL('public-cloud', `#/pci/projects/${projectId}/ai/notebooks`), }, isFeaturesAvailable('training') && { id: 'training', diff --git a/packages/manager/apps/pci-ai-notebooks/.gitignore b/packages/manager/apps/pci-ai-notebooks/.gitignore new file mode 100644 index 000000000000..32ef74fe6562 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/.gitignore @@ -0,0 +1,2 @@ +coverage +yarn-error.log \ No newline at end of file diff --git a/packages/manager/apps/pci-ai-notebooks/README.md b/packages/manager/apps/pci-ai-notebooks/README.md new file mode 100644 index 000000000000..1c5efd0baccd --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/README.md @@ -0,0 +1,3 @@ +# @ovh-ux/manager-pci-ai-notebooks-app + +> pci-ai-notebooks project diff --git a/packages/manager/apps/pci-ai-notebooks/components.json b/packages/manager/apps/pci-ai-notebooks/components.json new file mode 100644 index 000000000000..5fa88201dfd4 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/global.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils" + } +} diff --git a/packages/manager/apps/pci-ai-notebooks/package.json b/packages/manager/apps/pci-ai-notebooks/package.json new file mode 100644 index 000000000000..9564db61118b --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/package.json @@ -0,0 +1,127 @@ +{ + "name": "@ovh-ux/manager-pci-ai-notebooks-app", + "version": "1.0.0", + "private": true, + "description": "pci-ai-notebooks", + "repository": { + "type": "git", + "url": "git+https://github.com/ovh/manager.git", + "directory": "packages/manager/apps/pci-ai-notebooks" + }, + "license": "BSD-3-Clause", + "author": "OVH SAS", + "type": "module", + "scripts": { + "build": "tsc --project tsconfig.build.json && vite build", + "coverage": "vitest run --coverage", + "dev": "tsc && vite", + "start": "lerna exec --stream --scope='@ovh-ux/manager-pci-ai-notebooks-app' --include-dependencies -- npm run build --if-present", + "start:dev": "lerna exec --stream --scope='@ovh-ux/manager-pci-ai-notebooks-app' --include-dependencies -- npm run dev --if-present", + "start:watch": "lerna exec --stream --parallel --scope='@ovh-ux/manager-pci-ai-notebooks-app' --include-dependencies -- npm run dev:watch --if-present", + "test": "vitest", + "test-ui": "vitest --ui --coverage.enabled=true" + }, + "dependencies": { + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.0", + "@hookform/resolvers": "^3.3.4", + "@ovh-ux/manager-config": "*", + "@ovh-ux/manager-core-api": "*", + "@ovh-ux/manager-react-core-application": "*", + "@ovh-ux/manager-react-shell-client": "*", + "@ovh-ux/shell": "*", + "@ovhcloud/manager-components": "^1.3.0", + "@ovhcloud/ods-common-core": "^17.1.0", + "@ovhcloud/ods-common-theming": "^17.1.0", + "@ovhcloud/ods-components": "^17.1.0", + "@ovhcloud/ods-theme-blue-jeans": "^17.1.0", + "@radix-ui/react-accordion": "^1.1.2", + "@radix-ui/react-alert-dialog": "^1.0.5", + "@radix-ui/react-collapsible": "^1.0.3", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-progress": "^1.0.3", + "@radix-ui/react-radio-group": "^1.1.3", + "@radix-ui/react-scroll-area": "^1.0.5", + "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-slider": "^1.1.2", + "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tabs": "^1.0.4", + "@radix-ui/react-toast": "^1.1.5", + "@radix-ui/react-tooltip": "^1.0.7", + "@tanstack/react-query": "^5.8.3", + "@tanstack/react-table": "^8.12.0", + "@testing-library/dom": "^9.3.4", + "@testing-library/jest-dom": "^6.4.2", + "@testing-library/react": "^14.2.2", + "@testing-library/user-event": "^14.5.2", + "@types/node": "^18.0.0", + "chart.js": "^4.4.2", + "chartjs-adapter-date-fns": "^3.0.0", + "chartjs-plugin-zoom": "^2.0.1", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.0", + "cmdk": "^0.2.1", + "date-fns": "^3.4.0", + "duration-fns": "^3.0.2", + "i18next": "^23.10.0", + "i18next-http-backend": "^2.4.2", + "lucide-react": "^0.334.0", + "next-themes": "^0.2.1", + "qs": "^6.11.2", + "react": "^18.2.0", + "react-chartjs-2": "^5.2.0", + "react-day-picker": "^8.10.0", + "react-dom": "^18.2.0", + "react-hook-form": "^7.50.1", + "react-i18next": "^14.0.5", + "react-router": "^6.21.3", + "react-router-dom": "^6.3.0", + "sonner": "^1.4.0", + "tailwind-merge": "^2.2.1", + "tailwindcss": "^3.4.1", + "tailwindcss-animate": "^1.0.7", + "turbo": "^1.4.3", + "zod": "^3.22.4" + }, + "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@jest/globals": "^29.7.0", + "@ovh-ux/manager-vite-config": "*", + "@tanstack/react-query-devtools": "^5.8.3", + "@testing-library/dom": "^9.3.3", + "@testing-library/jest-dom": "^6.1.5", + "@testing-library/react": "^14.1.2", + "@testing-library/user-event": "^13.2.1", + "@types/jest": "^29.5.11", + "@types/node": "^20.10.6", + "@types/react": "^18.2.45", + "@types/react-dom": "^18.2.18", + "@typescript-eslint/eslint-plugin": "^6.16.0", + "@typescript-eslint/parser": "^6.16.0", + "@vitejs/plugin-react": "^4.2.1", + "@vitest/coverage-v8": "^1.2.0", + "@vitest/ui": "^1.4.0", + "autoprefixer": "^10.4.16", + "babel-plugin-transform-import-meta": "^2.2.1", + "element-internals-polyfill": "^1.3.10", + "eslint": "^8.56.0", + "jest-environment-jsdom": "^29.7.0", + "knip": "^3.10.0", + "postcss": "^8.4.32", + "rollup": "^4.4.0", + "tailwindcss": "^3.4.0", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.2", + "typescript": "^5.3.3", + "vite": "4.3.1", + "vitest": "^1.4.0" + }, + "regions": [ + "CA", + "EU" + ] +} diff --git a/packages/manager/apps/pci-ai-notebooks/postcss.config.cjs b/packages/manager/apps/pci-ai-notebooks/postcss.config.cjs new file mode 100644 index 000000000000..12a703d900da --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/packages/manager/apps/pci-ai-notebooks/public/assets/oops.png b/packages/manager/apps/pci-ai-notebooks/public/assets/oops.png new file mode 100644 index 0000000000000000000000000000000000000000..413028afad19a473424e11656d3c6b0400a79fdf GIT binary patch literal 60234 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 literal 0 HcmV?d00001 diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_de_DE.json b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_de_DE.json new file mode 100644 index 000000000000..5928e33d7bbe --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_de_DE.json @@ -0,0 +1,20 @@ +{ + "unitShort-B": "B", + "unitShort-KB": "KB", + "unitShort-MB": "MB", + "unitShort-GB": "GB", + "unitShort-TB": "TB", + "unitShort-PB": "PB", + "unitLong-B_one": "Byte", + "unitLong-B_other": "Byte", + "unitLong-KB_one": "Kilobyte", + "unitLong-KB_other": "Kilobyte", + "unitLong-MB_one": "Megabyte", + "unitLong-MB_other": "Megabyte", + "unitLong-GB_one": "Gigabyte", + "unitLong-GB_other": "Gigabyte", + "unitLong-TB_one": "Terabyte", + "unitLong-TB_other": "Terabyte", + "unitLong-PB_one": "Petabyte", + "unitLong-PB_other": "Petabyte" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_en_GB.json b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_en_GB.json new file mode 100644 index 000000000000..ff5ca5c4de65 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_en_GB.json @@ -0,0 +1,20 @@ +{ + "unitShort-B": "B", + "unitShort-KB": "KB", + "unitShort-MB": "MB", + "unitShort-GB": "GB", + "unitShort-TB": "TB", + "unitShort-PB": "PB", + "unitLong-B_one": "byte", + "unitLong-B_other": "bytes", + "unitLong-KB_one": "kilobyte", + "unitLong-KB_other": "kilobytes", + "unitLong-MB_one": "megabyte", + "unitLong-MB_other": "megabytes", + "unitLong-GB_one": "gigabyte", + "unitLong-GB_other": "gigabytes", + "unitLong-TB_one": "terabyte", + "unitLong-TB_other": "terabytes", + "unitLong-PB_one": "petabyte", + "unitLong-PB_other": "petabytes" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_es_ES.json b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_es_ES.json new file mode 100644 index 000000000000..ff5ca5c4de65 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_es_ES.json @@ -0,0 +1,20 @@ +{ + "unitShort-B": "B", + "unitShort-KB": "KB", + "unitShort-MB": "MB", + "unitShort-GB": "GB", + "unitShort-TB": "TB", + "unitShort-PB": "PB", + "unitLong-B_one": "byte", + "unitLong-B_other": "bytes", + "unitLong-KB_one": "kilobyte", + "unitLong-KB_other": "kilobytes", + "unitLong-MB_one": "megabyte", + "unitLong-MB_other": "megabytes", + "unitLong-GB_one": "gigabyte", + "unitLong-GB_other": "gigabytes", + "unitLong-TB_one": "terabyte", + "unitLong-TB_other": "terabytes", + "unitLong-PB_one": "petabyte", + "unitLong-PB_other": "petabytes" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_fr_CA.json b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_fr_CA.json new file mode 100644 index 000000000000..21953a45b499 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_fr_CA.json @@ -0,0 +1,20 @@ +{ + "unitShort-B": "o", + "unitShort-KB": "Ko", + "unitShort-MB": "Mo", + "unitShort-GB": "Go", + "unitShort-TB": "To", + "unitShort-PB": "Po", + "unitLong-B_one": "octet", + "unitLong-B_other": "octets", + "unitLong-KB_one": "kilooctet", + "unitLong-KB_other": "kilooctets", + "unitLong-MB_one": "mégaoctet", + "unitLong-MB_other": "mégaoctets", + "unitLong-GB_one": "gigaoctet", + "unitLong-GB_other": "gigaoctets", + "unitLong-TB_one": "téraoctet", + "unitLong-TB_other": "téraoctets", + "unitLong-PB_one": "pétaoctet", + "unitLong-PB_other": "pétaoctets" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_fr_FR.json b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_fr_FR.json new file mode 100644 index 000000000000..21953a45b499 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_fr_FR.json @@ -0,0 +1,20 @@ +{ + "unitShort-B": "o", + "unitShort-KB": "Ko", + "unitShort-MB": "Mo", + "unitShort-GB": "Go", + "unitShort-TB": "To", + "unitShort-PB": "Po", + "unitLong-B_one": "octet", + "unitLong-B_other": "octets", + "unitLong-KB_one": "kilooctet", + "unitLong-KB_other": "kilooctets", + "unitLong-MB_one": "mégaoctet", + "unitLong-MB_other": "mégaoctets", + "unitLong-GB_one": "gigaoctet", + "unitLong-GB_other": "gigaoctets", + "unitLong-TB_one": "téraoctet", + "unitLong-TB_other": "téraoctets", + "unitLong-PB_one": "pétaoctet", + "unitLong-PB_other": "pétaoctets" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_it_IT.json b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_it_IT.json new file mode 100644 index 000000000000..375d7b8399d5 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_it_IT.json @@ -0,0 +1,20 @@ +{ + "unitShort-B": "b", + "unitShort-KB": "KB", + "unitShort-MB": "MB", + "unitShort-GB": "GB", + "unitShort-TB": "TB", + "unitShort-PB": "PB", + "unitLong-B_one": "byte", + "unitLong-B_other": "byte", + "unitLong-KB_one": "kilobyte", + "unitLong-KB_other": "kilobyte", + "unitLong-MB_one": "megabyte", + "unitLong-MB_other": "megabyte", + "unitLong-GB_one": "gigabyte", + "unitLong-GB_other": "gigabyte", + "unitLong-TB_one": "terabyte", + "unitLong-TB_other": "terabyte", + "unitLong-PB_one": "petabyte", + "unitLong-PB_other": "petabyte" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_pl_PL.json b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_pl_PL.json new file mode 100644 index 000000000000..1511808fc990 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_pl_PL.json @@ -0,0 +1,20 @@ +{ + "unitShort-B": "B", + "unitShort-KB": "KB", + "unitShort-MB": "MB", + "unitShort-GB": "GB", + "unitShort-TB": "TB", + "unitShort-PB": "PB", + "unitLong-B_one": "bajt", + "unitLong-B_other": "bajty", + "unitLong-KB_one": "kilobajt", + "unitLong-KB_other": "kilobajty", + "unitLong-MB_one": "megabajt", + "unitLong-MB_other": "megabajty", + "unitLong-GB_one": "gigabajt", + "unitLong-GB_other": "gigabajty", + "unitLong-TB_one": "terabajt", + "unitLong-TB_other": "terabajty", + "unitLong-PB_one": "petabajt", + "unitLong-PB_other": "petabajty" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_pt_PT.json b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_pt_PT.json new file mode 100644 index 000000000000..ff5ca5c4de65 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/bytes/Messages_pt_PT.json @@ -0,0 +1,20 @@ +{ + "unitShort-B": "B", + "unitShort-KB": "KB", + "unitShort-MB": "MB", + "unitShort-GB": "GB", + "unitShort-TB": "TB", + "unitShort-PB": "PB", + "unitLong-B_one": "byte", + "unitLong-B_other": "bytes", + "unitLong-KB_one": "kilobyte", + "unitLong-KB_other": "kilobytes", + "unitLong-MB_one": "megabyte", + "unitLong-MB_other": "megabytes", + "unitLong-GB_one": "gigabyte", + "unitLong-GB_other": "gigabytes", + "unitLong-TB_one": "terabyte", + "unitLong-TB_other": "terabytes", + "unitLong-PB_one": "petabyte", + "unitLong-PB_other": "petabytes" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_de_DE.json b/packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_de_DE.json new file mode 100644 index 000000000000..8e10000b4500 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_de_DE.json @@ -0,0 +1,8 @@ +{ + "manager_error_page_title": "Hoppla!", + "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-notebooks/public/translations/error/Messages_en_GB.json b/packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_en_GB.json new file mode 100644 index 000000000000..b17691e2bc6d --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/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-notebooks/public/translations/error/Messages_es_ES.json b/packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_es_ES.json new file mode 100644 index 000000000000..15fc5f79256d --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/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-notebooks/public/translations/error/Messages_fr_CA.json b/packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_fr_CA.json new file mode 100644 index 000000000000..2c575c63588e --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/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-notebooks/public/translations/error/Messages_fr_FR.json b/packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_fr_FR.json new file mode 100644 index 000000000000..2c575c63588e --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/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-notebooks/public/translations/error/Messages_it_IT.json b/packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_it_IT.json new file mode 100644 index 000000000000..fa5055b8cec5 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_it_IT.json @@ -0,0 +1,8 @@ +{ + "manager_error_page_title": "Ops!", + "manager_error_page_button_cancel": "Annullare", + "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-notebooks/public/translations/error/Messages_pl_PL.json b/packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_pl_PL.json new file mode 100644 index 000000000000..eceb9bcca2ca --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_pl_PL.json @@ -0,0 +1,8 @@ +{ + "manager_error_page_title": "Ojej...", + "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-notebooks/public/translations/error/Messages_pt_PT.json b/packages/manager/apps/pci-ai-notebooks/public/translations/error/Messages_pt_PT.json new file mode 100644 index 000000000000..25fac9551c86 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/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 para a página inicial", + "manager_error_page_default": "Ocorreu um erro ao carregar a página." +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/Messages_fr_FR.json b/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/Messages_fr_FR.json new file mode 100644 index 000000000000..63af51e824fb --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/pci-ai-notebooks/Messages_fr_FR.json @@ -0,0 +1,3 @@ +{ + "title": "AI Notebooks" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_de_DE.json b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_de_DE.json new file mode 100644 index 000000000000..24b6c3bde262 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_de_DE.json @@ -0,0 +1,67 @@ +{ + "region_SBG1": "Straßburg (SBG1)", + "region_BHS1": "Beauharnois (BHS1)", + "region_GRA1": "Gravelines (GRA1)", + "region_SBG": "Straßburg", + "region_SBG_micro": "Straßburg ({{ micro }})", + "region_BHS": "Beauharnois", + "region_BHS_micro": "Beauharnois ({{ micro }})", + "region_ERI": "London", + "region_ERI_micro": "London ({{ micro }})", + "region_GRA": "Gravelines", + "region_GRA_micro": "Gravelines ({{ micro }})", + "region_LIM": "Limburg", + "region_LIM_micro": "Limburg ({{ micro }})", + "region_RBX": "Roubaix", + "region_RBX_micro": "Roubaix ({{ micro }})", + "region_WAW": "Warschau", + "region_WAW_micro": "Warschau ({{ micro }})", + "region_DE": "Frankfurt", + "region_DE_micro": "Frankfurt ({{ micro }})", + "region_UK": "London", + "region_UK_micro": "London ({{ micro }})", + "region_SGP": "Singapur", + "region_SGP_micro": "Singapur ({{ micro }})", + "region_SYD": "Sydney", + "region_SYD_micro": "Sydney ({{ micro }})", + "region_US": "USA", + "region_US_micro": "USA ({{ micro }})", + "region_GS": "GS", + "region_MAD": "Madrid", + "region_BRU": "Brüssel", + "region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "region_GS_micro": "Gridscale ({{ micro }})", + "region_MAD_micro": "Madrid ({{ micro }})", + "region_BRU_micro": "Brüssel ({{ micro }})", + "region_localize": "Lokalisieren", + "region_location_SBG": "Mitteleuropa (Frankreich)", + "region_location_WAW": "Mitteleuropa (Polen)", + "region_location_BHS": "Nordamerika (Kanada)", + "region_location_ERI": "Westeuropa (Vereinigtes Königreich)", + "region_location_GRA": "Westeuropa (Frankreich)", + "region_location_GS": "Westeuropa", + "region_location_MAD": "Westeuropa", + "region_location_BRU": "Westeuropa", + "region_location_LIM": "Mitteleuropa (Deutschland)", + "region_location_RBX": "Westeuropa (Frankreich)", + "region_location_DE": "Mitteleuropa (Deutschland)", + "region_location_UK": "Westeuropa (Vereinigtes Königreich)", + "region_location_SGP": "Südostasien (Singapur)", + "region_location_SYD": "Ozeanien (Australien)", + "region_location_US": "USA", + "region_continent_SBG": "Mitteleuropa", + "region_continent_WAW": "Mitteleuropa", + "region_continent_BHS": "Nordamerika", + "region_continent_GRA": "Westeuropa", + "region_continent_RBX": "Westeuropa", + "region_continent_GS": "Westeuropa", + "region_continent_MAD": "Westeuropa", + "region_continent_BRU": "Westeuropa", + "region_continent_DE": "Mitteleuropa", + "region_continent_UK": "Westeuropa", + "region_continent_SGP": "Südostasien", + "region_continent_SYD": "Ozeanien", + "region_continent_US": "USA", + "region_continent_SHA": "Westeuropa", + "region_continent_all": "Alle Standorte" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_en_GB.json b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_en_GB.json new file mode 100644 index 000000000000..f91fd8920068 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_en_GB.json @@ -0,0 +1,67 @@ +{ + "region_SBG1": "Strasbourg (SBG1)", + "region_BHS1": "Beauharnois (BHS1)", + "region_GRA1": "Gravelines (GRA1)", + "region_SBG": "Strasbourg", + "region_SBG_micro": "Strasbourg ({{ micro }})", + "region_BHS": "Beauharnois", + "region_BHS_micro": "Beauharnois ({{ micro }})", + "region_ERI": "London", + "region_ERI_micro": "London ({{ micro }})", + "region_GRA": "Gravelines", + "region_GRA_micro": "Gravelines ({{ micro }})", + "region_LIM": "Limburg", + "region_LIM_micro": "Limburg ({{ micro }})", + "region_RBX": "Roubaix", + "region_RBX_micro": "Roubaix ({{ micro }})", + "region_WAW": "Warsaw", + "region_WAW_micro": "Warsaw ({{ micro }})", + "region_DE": "Frankfurt", + "region_DE_micro": "Frankfurt ({{ micro }})", + "region_UK": "London", + "region_UK_micro": "London ({{ micro }})", + "region_SGP": "Singapore", + "region_SGP_micro": "Singapore ({{ micro }})", + "region_SYD": "Sydney", + "region_SYD_micro": "Sydney ({{ micro }})", + "region_US": "United States of America", + "region_US_micro": "United States ({{ micro }})", + "region_GS": "GS", + "region_MAD": "Madrid", + "region_BRU": "Brussels", + "region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "region_GS_micro": "Gridscale ({{ micro }})", + "region_MAD_micro": "Madrid ({{ micro }})", + "region_BRU_micro": "Brussels ({{ micro }})", + "region_localize": "Locate", + "region_location_SBG": "Central Europe (France)", + "region_location_WAW": "Central Europe (Poland)", + "region_location_BHS": "North America (Canada)", + "region_location_ERI": "Western Europe (United Kingdom)", + "region_location_GRA": "Western Europe (France)", + "region_location_GS": "Western Europe", + "region_location_MAD": "Western Europe", + "region_location_BRU": "Western Europe", + "region_location_LIM": "Central Europe (Germany)", + "region_location_RBX": "Western Europe (France)", + "region_location_DE": "Central Europe (Germany)", + "region_location_UK": "Western Europe (United Kingdom)", + "region_location_SGP": "South-East Asia (Singapore)", + "region_location_SYD": "Oceania (Australia)", + "region_location_US": "United States of America", + "region_continent_SBG": "Central Europe", + "region_continent_WAW": "Central Europe", + "region_continent_BHS": "North America", + "region_continent_GRA": "Western Europe", + "region_continent_RBX": "Western Europe", + "region_continent_GS": "Western Europe", + "region_continent_MAD": "Western Europe", + "region_continent_BRU": "Western Europe", + "region_continent_DE": "Central Europe", + "region_continent_UK": "Western Europe", + "region_continent_SGP": "South-East Asia", + "region_continent_SYD": "Oceania", + "region_continent_US": "United States of America", + "region_continent_SHA": "Western Europe", + "region_continent_all": "All locations" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_es_ES.json b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_es_ES.json new file mode 100644 index 000000000000..d78d3808005d --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_es_ES.json @@ -0,0 +1,67 @@ +{ + "region_SBG1": "Estrasburgo (SBG1)", + "region_BHS1": "Beauharnois (BHS1)", + "region_GRA1": "Gravelines (GRA1)", + "region_SBG": "Estrasburgo", + "region_SBG_micro": "Estrasburgo ({{ micro }})", + "region_BHS": "Beauharnois", + "region_BHS_micro": "Beauharnois ({{ micro }})", + "region_ERI": "Londres", + "region_ERI_micro": "Londres ({{ micro }})", + "region_GRA": "Gravelines", + "region_GRA_micro": "Gravelines ({{ micro }})", + "region_LIM": "Limburgo", + "region_LIM_micro": "Limburgo ({{ micro }})", + "region_RBX": "Roubaix", + "region_RBX_micro": "Roubaix ({{ micro }})", + "region_WAW": "Varsovia", + "region_WAW_micro": "Varsovia ({{ micro }})", + "region_DE": "Fráncfort", + "region_DE_micro": "Fráncfort ({{ micro }})", + "region_UK": "Londres", + "region_UK_micro": "Londres ({{ micro }})", + "region_SGP": "Singapur", + "region_SGP_micro": "Singapur ({{ micro }})", + "region_SYD": "Sídney", + "region_SYD_micro": "Sídney ({{ micro }})", + "region_US": "Estados Unidos", + "region_US_micro": "Estados Unidos ({{ micro }})", + "region_GS": "GS", + "region_MAD": "Madrid", + "region_BRU": "Bruselas", + "region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "region_GS_micro": "Gridscale ({{ micro }})", + "region_MAD_micro": "Madrid ({{ micro }})", + "region_BRU_micro": "Bruselas ({{ micro }})", + "region_localize": "Localizar", + "region_location_SBG": "Europa Central (Francia)", + "region_location_WAW": "Europa Central (Polonia)", + "region_location_BHS": "Norteamérica (Canadá)", + "region_location_ERI": "Europa Occidental (Reino Unido)", + "region_location_GRA": "Europa Occidental (Francia)", + "region_location_GS": "Western Europe", + "region_location_MAD": "Western Europe", + "region_location_BRU": "Western Europe", + "region_location_LIM": "Europa Central (Alemania)", + "region_location_RBX": "Europa Occidental (Francia)", + "region_location_DE": "Europa Central (Alemania)", + "region_location_UK": "Europa Occidental (Reino Unido)", + "region_location_SGP": "Sudeste asiático (Singapur)", + "region_location_SYD": "Oceanía (Australia)", + "region_location_US": "Estados Unidos", + "region_continent_SBG": "Europa Central", + "region_continent_WAW": "Europa Central", + "region_continent_BHS": "Norteamérica", + "region_continent_GRA": "Europa Occidental", + "region_continent_RBX": "Europa Occidental", + "region_continent_GS": "Western Europe", + "region_continent_MAD": "Western Europe", + "region_continent_BRU": "Western Europe", + "region_continent_DE": "Europa Central", + "region_continent_UK": "Europa Occidental", + "region_continent_SGP": "Sudeste Asiático", + "region_continent_SYD": "Oceanía", + "region_continent_US": "Estados Unidos", + "region_continent_SHA": "Europa Occidental", + "region_continent_all": "Todas las localizaciones" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_fr_CA.json b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_fr_CA.json new file mode 100644 index 000000000000..8d6147ad82fb --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_fr_CA.json @@ -0,0 +1,67 @@ +{ + "region_SBG1": "Strasbourg (SBG1)", + "region_BHS1": "Beauharnois (BHS1)", + "region_GRA1": "Gravelines (GRA1)", + "region_SBG": "Strasbourg", + "region_SBG_micro": "Strasbourg ({{ micro }})", + "region_BHS": "Beauharnois", + "region_BHS_micro": "Beauharnois ({{ micro }})", + "region_ERI": "Londres", + "region_ERI_micro": "Londres ({{ micro }})", + "region_GRA": "Gravelines", + "region_GRA_micro": "Gravelines ({{ micro }})", + "region_LIM": "Limburg", + "region_LIM_micro": "Limburg ({{ micro }})", + "region_RBX": "Roubaix", + "region_RBX_micro": "Roubaix ({{ micro }})", + "region_WAW": "Varsovie", + "region_WAW_micro": "Varsovie ({{ micro }})", + "region_DE": "Francfort", + "region_DE_micro": "Francfort ({{ micro }})", + "region_UK": "Londres", + "region_UK_micro": "Londres ({{ micro }})", + "region_SGP": "Singapour", + "region_SGP_micro": "Singapour ({{ micro }})", + "region_SYD": "Sydney", + "region_SYD_micro": "Sydney ({{ micro }})", + "region_US": "États-Unis", + "region_US_micro": "États-Unis ({{ micro }})", + "region_GS": "GS", + "region_MAD": "Madrid", + "region_BRU": "Bruxelles", + "region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "region_GS_micro": "Gridscale ({{ micro }})", + "region_MAD_micro": "Madrid ({{ micro }})", + "region_BRU_micro": "Bruxelles ({{ micro }})", + "region_localize": "Localiser", + "region_location_SBG": "Europe centrale (France)", + "region_location_WAW": "Europe centrale (Pologne)", + "region_location_BHS": "Amérique du Nord (Canada)", + "region_location_ERI": "Europe de l'Ouest (Grande-Bretagne)", + "region_location_GRA": "Europe de l'Ouest (France)", + "region_location_GS": "Western Europe", + "region_location_MAD": "Western Europe", + "region_location_BRU": "Western Europe", + "region_location_LIM": "Europe centrale (Allemagne)", + "region_location_RBX": "Europe de l'Ouest (France)", + "region_location_DE": "Europe centrale (Allemagne)", + "region_location_UK": "Europe de l'Ouest (Grande-Bretagne)", + "region_location_SGP": "Asie du Sud-Est (Singapour)", + "region_location_SYD": "Océanie (Australie)", + "region_location_US": "États-Unis", + "region_continent_SBG": "Europe centrale", + "region_continent_WAW": "Europe centrale", + "region_continent_BHS": "Amérique du Nord", + "region_continent_GRA": "Europe de l'Ouest", + "region_continent_RBX": "Europe de l'Ouest", + "region_continent_GS": "Western Europe", + "region_continent_MAD": "Western Europe", + "region_continent_BRU": "Western Europe", + "region_continent_DE": "Europe centrale", + "region_continent_UK": "Europe de l'Ouest", + "region_continent_SGP": "Asie du Sud-Est", + "region_continent_SYD": "Océanie", + "region_continent_US": "États-Unis", + "region_continent_SHA": "Europe de l'Ouest", + "region_continent_all": "Toutes les localisations" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_fr_FR.json b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_fr_FR.json new file mode 100644 index 000000000000..8d6147ad82fb --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_fr_FR.json @@ -0,0 +1,67 @@ +{ + "region_SBG1": "Strasbourg (SBG1)", + "region_BHS1": "Beauharnois (BHS1)", + "region_GRA1": "Gravelines (GRA1)", + "region_SBG": "Strasbourg", + "region_SBG_micro": "Strasbourg ({{ micro }})", + "region_BHS": "Beauharnois", + "region_BHS_micro": "Beauharnois ({{ micro }})", + "region_ERI": "Londres", + "region_ERI_micro": "Londres ({{ micro }})", + "region_GRA": "Gravelines", + "region_GRA_micro": "Gravelines ({{ micro }})", + "region_LIM": "Limburg", + "region_LIM_micro": "Limburg ({{ micro }})", + "region_RBX": "Roubaix", + "region_RBX_micro": "Roubaix ({{ micro }})", + "region_WAW": "Varsovie", + "region_WAW_micro": "Varsovie ({{ micro }})", + "region_DE": "Francfort", + "region_DE_micro": "Francfort ({{ micro }})", + "region_UK": "Londres", + "region_UK_micro": "Londres ({{ micro }})", + "region_SGP": "Singapour", + "region_SGP_micro": "Singapour ({{ micro }})", + "region_SYD": "Sydney", + "region_SYD_micro": "Sydney ({{ micro }})", + "region_US": "États-Unis", + "region_US_micro": "États-Unis ({{ micro }})", + "region_GS": "GS", + "region_MAD": "Madrid", + "region_BRU": "Bruxelles", + "region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "region_GS_micro": "Gridscale ({{ micro }})", + "region_MAD_micro": "Madrid ({{ micro }})", + "region_BRU_micro": "Bruxelles ({{ micro }})", + "region_localize": "Localiser", + "region_location_SBG": "Europe centrale (France)", + "region_location_WAW": "Europe centrale (Pologne)", + "region_location_BHS": "Amérique du Nord (Canada)", + "region_location_ERI": "Europe de l'Ouest (Grande-Bretagne)", + "region_location_GRA": "Europe de l'Ouest (France)", + "region_location_GS": "Western Europe", + "region_location_MAD": "Western Europe", + "region_location_BRU": "Western Europe", + "region_location_LIM": "Europe centrale (Allemagne)", + "region_location_RBX": "Europe de l'Ouest (France)", + "region_location_DE": "Europe centrale (Allemagne)", + "region_location_UK": "Europe de l'Ouest (Grande-Bretagne)", + "region_location_SGP": "Asie du Sud-Est (Singapour)", + "region_location_SYD": "Océanie (Australie)", + "region_location_US": "États-Unis", + "region_continent_SBG": "Europe centrale", + "region_continent_WAW": "Europe centrale", + "region_continent_BHS": "Amérique du Nord", + "region_continent_GRA": "Europe de l'Ouest", + "region_continent_RBX": "Europe de l'Ouest", + "region_continent_GS": "Western Europe", + "region_continent_MAD": "Western Europe", + "region_continent_BRU": "Western Europe", + "region_continent_DE": "Europe centrale", + "region_continent_UK": "Europe de l'Ouest", + "region_continent_SGP": "Asie du Sud-Est", + "region_continent_SYD": "Océanie", + "region_continent_US": "États-Unis", + "region_continent_SHA": "Europe de l'Ouest", + "region_continent_all": "Toutes les localisations" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_it_IT.json b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_it_IT.json new file mode 100644 index 000000000000..2d2b67b9b45d --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_it_IT.json @@ -0,0 +1,67 @@ +{ + "region_SBG1": "Strasburgo (SBG1)", + "region_BHS1": "Beauharnois (BHS1)", + "region_GRA1": "Gravelines (GRA1)", + "region_SBG": "Strasburgo", + "region_SBG_micro": "Strasburgo ({{ micro }})", + "region_BHS": "Beauharnois", + "region_BHS_micro": "Beauharnois ({{ micro }})", + "region_ERI": "Londra", + "region_ERI_micro": "Londra ({{ micro }})", + "region_GRA": "Gravelines", + "region_GRA_micro": "Gravelines ({{ micro }})", + "region_LIM": "Limburgo", + "region_LIM_micro": "Limburgo ({{ micro }})", + "region_RBX": "Roubaix", + "region_RBX_micro": "Roubaix ({{ micro }})", + "region_WAW": "Varsavia", + "region_WAW_micro": "Varsavia ({{ micro }})", + "region_DE": "Francoforte", + "region_DE_micro": "Francoforte ({{ micro }})", + "region_UK": "Londra", + "region_UK_micro": "Londra ({{ micro }})", + "region_SGP": "Singapore", + "region_SGP_micro": "Singapore ({{ micro }})", + "region_SYD": "Sydney", + "region_SYD_micro": "Sydney ({{ micro }})", + "region_US": "Stati Uniti", + "region_US_micro": "Stati Uniti ({{ micro }})", + "region_GS": "GS", + "region_MAD": "Madrid", + "region_BRU": "Bruxelles", + "region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "region_GS_micro": "Gridscale ({{ micro }})", + "region_MAD_micro": "Madrid ({{ micro }})", + "region_BRU_micro": "Bruxelles ({{ micro }})", + "region_localize": "Localizza", + "region_location_SBG": "Europa centrale (Francia)", + "region_location_WAW": "Europa centrale (Polonia)", + "region_location_BHS": "Nord America (Canada)", + "region_location_ERI": "Europa Occidentale (Gran Bretagna)", + "region_location_GRA": "Europa Occidentale (Francia)", + "region_location_GS": "Europa Occidentale", + "region_location_MAD": "Europa Occidentale", + "region_location_BRU": "Europa Occidentale", + "region_location_LIM": "Europa centrale (Germania)", + "region_location_RBX": "Europa Occidentale (Francia)", + "region_location_DE": "Europa centrale (Germania)", + "region_location_UK": "Europa Occidentale (Gran Bretagna)", + "region_location_SGP": "Sud-est asiatico (Singapore)", + "region_location_SYD": "Oceania (Australia)", + "region_location_US": "Stati Uniti", + "region_continent_SBG": "Europa centrale", + "region_continent_WAW": "Europa centrale", + "region_continent_BHS": "Nord America ", + "region_continent_GRA": "Europa Occidentale", + "region_continent_RBX": "Europa Occidentale", + "region_continent_GS": "Europa Occidentale", + "region_continent_MAD": "Europa Occidentale", + "region_continent_BRU": "Europa Occidentale", + "region_continent_DE": "Europa centrale", + "region_continent_UK": "Europa Occidentale", + "region_continent_SGP": "Asia Pacifica", + "region_continent_SYD": "Oceania", + "region_continent_US": "Stati Uniti", + "region_continent_SHA": "Europa Occidentale", + "region_continent_all": "Tutte le Region" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_pl_PL.json b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_pl_PL.json new file mode 100644 index 000000000000..6f3791ace50e --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_pl_PL.json @@ -0,0 +1,67 @@ +{ + "region_SBG1": "Strasburg (SBG1)", + "region_BHS1": "Beauharnois (BHS1)", + "region_GRA1": "Gravelines (GRA1)", + "region_SBG": "Strasburg", + "region_SBG_micro": "Strasburg ({{ micro }})", + "region_BHS": "Beauharnois", + "region_BHS_micro": "Beauharnois ({{ micro }})", + "region_ERI": "Londyn", + "region_ERI_micro": "Londyn ({{ micro }})", + "region_GRA": "Gravelines", + "region_GRA_micro": "Gravelines ({{ micro }})", + "region_LIM": "Limburg", + "region_LIM_micro": "Limburg ({{ micro }})", + "region_RBX": "Roubaix", + "region_RBX_micro": "Roubaix ({{ micro }})", + "region_WAW": "Warszawa", + "region_WAW_micro": "Warszawa ({{ micro }})", + "region_DE": "Frankfurt", + "region_DE_micro": "Frankfurt ({{ micro }})", + "region_UK": "Londyn", + "region_UK_micro": "Londyn ({{ micro }})", + "region_SGP": "Singapur", + "region_SGP_micro": "Singapur ({{ micro }})", + "region_SYD": "Sydney", + "region_SYD_micro": "Sydney ({{ micro }})", + "region_US": "Stany Zjednoczone", + "region_US_micro": "Stany Zjednoczone ({{ micro }})", + "region_GS": "GS", + "region_MAD": "Madryt", + "region_BRU": "Bruksela", + "region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "region_GS_micro": "Gridscale ({{micro}})", + "region_MAD_micro": "Madryt ({{micro}})", + "region_BRU_micro": "Bruksela ({{micro}})", + "region_localize": "Lokalizacja", + "region_location_SBG": "Europa Środkowa (Francja)", + "region_location_WAW": "Europa Środkowa (Polska)", + "region_location_BHS": "Ameryka Północna (Kanada)", + "region_location_ERI": "Europa Zachodnia (Wielka Brytania)", + "region_location_GRA": "Europa Zachodnia (Francja)", + "region_location_GS": "Europa Zachodnia", + "region_location_MAD": "Europa Zachodnia", + "region_location_BRU": "Europa Zachodnia", + "region_location_LIM": "Europa Środkowa (Niemcy)", + "region_location_RBX": "Europa Zachodnia (Francja)", + "region_location_DE": "Europa Środkowa (Niemcy)", + "region_location_UK": "Europa Zachodnia (Wielka Brytania)", + "region_location_SGP": "Azja Południowo-Wschodnia (Singapur)", + "region_location_SYD": "Oceania (Australia)", + "region_location_US": "Stany Zjednoczone", + "region_continent_SBG": "Europa Środkowa", + "region_continent_WAW": "Europa Środkowa", + "region_continent_BHS": "Ameryka Północna", + "region_continent_GRA": "Europa Zachodnia", + "region_continent_RBX": "Europa Zachodnia", + "region_continent_GS": "Europa Zachodnia", + "region_continent_MAD": "Europa Zachodnia", + "region_continent_BRU": "Europa Zachodnia", + "region_continent_DE": "Europa Środkowa", + "region_continent_UK": "Europa Zachodnia", + "region_continent_SGP": "Azja Południowo-Wschodnia", + "region_continent_SYD": "Oceania", + "region_continent_US": "Stany Zjednoczone", + "region_continent_SHA": "Europa Zachodnia", + "region_continent_all": "Wszystkie lokalizacje" +} diff --git a/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_pt_PT.json b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_pt_PT.json new file mode 100644 index 000000000000..365abdb1cdac --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/public/translations/regions/Messages_pt_PT.json @@ -0,0 +1,67 @@ +{ + "region_SBG1": "Estrasburgo (SBG1)", + "region_BHS1": "Beauharnois (BHS1)", + "region_GRA1": "Gravelines (GRA1)", + "region_SBG": "Estrasburgo", + "region_SBG_micro": "Estrasburgo ({{ micro }})", + "region_BHS": "Beauharnois", + "region_BHS_micro": "Beauharnois ({{ micro }})", + "region_ERI": "Londres", + "region_ERI_micro": "Londres ({{ micro }})", + "region_GRA": "Gravelines", + "region_GRA_micro": "Gravelines ({{ micro }})", + "region_LIM": "Limburgo", + "region_LIM_micro": "Limburgo ({{ micro }})", + "region_RBX": "Roubaix", + "region_RBX_micro": "Roubaix ({{ micro }})", + "region_WAW": "Varsóvia", + "region_WAW_micro": "Varsóvia ({{ micro }})", + "region_DE": "Frankfurt ", + "region_DE_micro": "Frankfurt ({{ micro }})", + "region_UK": "Londres", + "region_UK_micro": "Londres ({{ micro }})", + "region_SGP": "Singapura", + "region_SGP_micro": "Singapura ({{ micro }})", + "region_SYD": "Sydney", + "region_SYD_micro": "Sydney ({{ micro }})", + "region_US": "Estados Unidos", + "region_US_micro": "Estados Unidos ({{ micro }})", + "region_GS": "GS", + "region_MAD": "Madrid", + "region_BRU": "Bruxelas", + "region_SHA_micro": "Gravelines (SHADOW-EU-1)", + "region_GS_micro": "Gridscale ({{ micro }})", + "region_MAD_micro": "Madrid ({{ micro }})", + "region_BRU_micro": "Bruxelas ({{ micro }})", + "region_localize": "Localizar", + "region_location_SBG": "Europa Central (França)", + "region_location_WAW": "Europa Central (Polónia)", + "region_location_BHS": "América do Norte (Canadá)", + "region_location_ERI": "Europa Ocidental (Reino Unido)", + "region_location_GRA": "Europa Ocidental (França)", + "region_location_GS": "Western Europe", + "region_location_MAD": "Western Europe", + "region_location_BRU": "Western Europe", + "region_location_LIM": "Europa Central (Alemanha)", + "region_location_RBX": "Europa Ocidental (França)", + "region_location_DE": "Europa Central (Alemanha)", + "region_location_UK": "Europa Ocidental (Reino Unido)", + "region_location_SGP": "Sudeste Asiático (Singapura)", + "region_location_SYD": "Oceânia (Austrália)", + "region_location_US": "Estados Unidos", + "region_continent_SBG": "Europa Central", + "region_continent_WAW": "Europa Central", + "region_continent_BHS": "América do Norte", + "region_continent_GRA": "Europa Ocidental", + "region_continent_RBX": "Europa Ocidental", + "region_continent_GS": "Western Europe", + "region_continent_MAD": "Western Europe", + "region_continent_BRU": "Western Europe", + "region_continent_DE": "Europa Central", + "region_continent_UK": "Europa Ocidental", + "region_continent_SGP": "Sudeste Asiático", + "region_continent_SYD": "Oceânia", + "region_continent_US": "Estados Unidos", + "region_continent_SHA": "Europa Ocidental", + "region_continent_all": "Todas as localizações" +} diff --git a/packages/manager/apps/pci-ai-notebooks/src/App.tsx b/packages/manager/apps/pci-ai-notebooks/src/App.tsx new file mode 100644 index 000000000000..8b9a4cd48b07 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/App.tsx @@ -0,0 +1,34 @@ +import React, { useEffect } from 'react'; +import { QueryClientProvider } from '@tanstack/react-query'; +import { odsSetup } from '@ovhcloud/ods-common-core'; +import { useShell } from '@ovh-ux/manager-react-shell-client'; + +import '@ovhcloud/ods-theme-blue-jeans'; +import './global.css'; + +import queryClient from './query.client'; +import Router from '@/routes/Router'; +import Loading from './components/Loading/Loading'; +import { useLoadingIndicatorContext } from './contexts/loadingIndicatorContext'; +import ProgressLoader from './components/Loading/ProgressLoader'; + +odsSetup(); + +function App() { + const { loading } = useLoadingIndicatorContext(); + const shell = useShell(); + useEffect(() => { + shell.ux.hidePreloader(); + }, []); + + return ( + + {loading && } + }> + + + + ); +} + +export default App; diff --git a/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/pointerEvent.ts b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/pointerEvent.ts new file mode 100644 index 000000000000..a27898956a04 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/pointerEvent.ts @@ -0,0 +1,19 @@ +interface PointerEventProps extends EventInit { + button: number; + ctrlKey: boolean; +} +export class PointerEvent extends Event { + button: number; + + ctrlKey: boolean; + + constructor(type: string, props: PointerEventProps) { + super(type, props); + if (props.button != null) { + this.button = props.button; + } + if (props.ctrlKey != null) { + this.ctrlKey = props.ctrlKey; + } + } +} diff --git a/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/selectHelper.ts b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/selectHelper.ts new file mode 100644 index 000000000000..9941ec5ee168 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/selectHelper.ts @@ -0,0 +1,39 @@ +import { act, fireEvent, screen, waitFor } from '@testing-library/react'; + +/** + * Open a Select component and select the correct option + * @param triggerId testId of the trigger select + * @param options labels of the options that should be in the select + * @param optionToSelect the option to select + */ +export async function handleSelectOption( + triggerId: string, + options: string[], + optionToSelect: string, +) { + // Open select + act(() => { + const trigger = screen.getByTestId(triggerId); + fireEvent.focus(trigger); + fireEvent.keyDown(trigger, { key: 'Enter', code: 13 }); + }); + + // Check if select has the options + await waitFor(() => { + expect(screen.getByTestId(triggerId)).not.toHaveAttribute( + 'data-state', + 'closed', + ); + options.forEach((optionLabel) => { + expect(screen.getByText(optionLabel)).toBeInTheDocument(); + }); + }); + // Select the option + act(() => { + const optionsElements = screen.getAllByRole('option'); + const elem = optionsElements.find((e) => + e.innerHTML.includes(optionToSelect), + ); + fireEvent.keyDown(elem, { key: 'Enter', code: 13 }); + }); +} diff --git a/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/wrappers/QueryClientWrapper.tsx b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/wrappers/QueryClientWrapper.tsx new file mode 100644 index 000000000000..1b9e387c9f5a --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/wrappers/QueryClientWrapper.tsx @@ -0,0 +1,10 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; + +const queryClient = new QueryClient(); +export const QueryClientWrapper = ({ + children, +}: { + children: React.ReactNode; +}) => ( + {children} +); diff --git a/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/wrappers/RouterWithLocationWrapper.tsx b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/wrappers/RouterWithLocationWrapper.tsx new file mode 100644 index 000000000000..0d310dca93fb --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/__tests__/helpers/wrappers/RouterWithLocationWrapper.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { + RouterProvider, + createHashRouter, + MemoryRouter, + useLocation, +} from 'react-router-dom'; +import * as useLoadingIndicator from '@/contexts/loadingIndicatorContext'; + +/** + * Displays the current location in order to test the syncing between the state of the hook and the url + */ +export const LocationDisplay = () => { + const location = useLocation(); + return ( +
{`${location.pathname}${location.search}`}
+ ); +}; + +export const RouterWithLocationWrapper = ({ + children, + initialEntries = ['/test'], +}: { + children: React.ReactNode; + initialEntries: string[]; +}) => { + return ( + + {children} + + + ); +}; + +export const HashRouterWithLocationWrapper = ({ + children, +}: { + children: React.ReactNode; +}) => { + const element = ( + <> + {children} + + + ); + const router = createHashRouter([ + { + path: '/', + element, + }, + ]); + return ( + + + + ); +}; diff --git a/packages/manager/apps/pci-ai-notebooks/src/__tests__/setupTest.ts b/packages/manager/apps/pci-ai-notebooks/src/__tests__/setupTest.ts new file mode 100644 index 000000000000..8cd2e90548c9 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/__tests__/setupTest.ts @@ -0,0 +1,7 @@ +import '@testing-library/jest-dom'; +import { PointerEvent } from './helpers/pointerEvent'; + +// use a custom pointerEvent as jest does not implement it. +// it is requiered for DropdownMenus +// source: https://github.com/radix-ui/primitives/issues/856#issuecomment-928704064 +window.PointerEvent = PointerEvent as any; diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/Breadcrumb/Breadcrumb.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/Breadcrumb/Breadcrumb.tsx new file mode 100644 index 000000000000..c790aff8ff7c --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/Breadcrumb/Breadcrumb.tsx @@ -0,0 +1,82 @@ +import React, { useState } from 'react'; +import { Params, useParams, useLocation, useMatches } from 'react-router-dom'; +import { useNavigation } from '@ovh-ux/manager-react-shell-client'; +import usePciProject from '@/data/hooks/pciProjects.api.hooks'; +import { Skeleton } from '../ui/skeleton'; +import { Link, A } from '../links'; + +export type BreadcrumbHandleParams = { + data: unknown; + params: Params; +}; + +export interface MatchWithBreadcrumb { + id: string; + pathname: string; + params: Params; + data: unknown; + handle: { + breadcrumb?: (breadcrumbParams: { + params: Params; + data: unknown; + }) => React.ReactElement | null; + }; +} + +function Breadcrumb(): JSX.Element { + const { projectId } = useParams(); + const { data: project } = usePciProject(); + + const location = useLocation(); + + const navigation = useNavigation(); + const [baseUrl, setBaseUrl] = useState(''); + const matches = useMatches() as MatchWithBreadcrumb[]; + const [breadcrumbData, setBreadcrumbData] = React.useState([]); + + React.useEffect(() => { + const breadcrumbArray = matches + .filter((match) => Boolean(match.handle?.breadcrumb)) + .map((match) => { + return { + path: match.pathname, + label: match.handle.breadcrumb(match), + }; + }); + setBreadcrumbData(breadcrumbArray); + }, [location.pathname]); + + React.useEffect(() => { + const updateNav = async () => { + const url = await navigation.getURL('public-cloud', ``, {}); + setBaseUrl(url as string); + }; + updateNav(); + }, [navigation]); + + return ( + <> + + {project?.description ?? ( + + )} + + {breadcrumbData.map((bc, index) => ( + + | + {index < breadcrumbData.length - 1 ? ( + + {bc.label} + + ) : ( + + {bc.label} + + )} + + ))} + + ); +} + +export default Breadcrumb; diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/Breadcrumb/BreadcrumbItem.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/Breadcrumb/BreadcrumbItem.tsx new file mode 100644 index 000000000000..9d7164f1b24d --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/Breadcrumb/BreadcrumbItem.tsx @@ -0,0 +1,12 @@ +import { useTranslation } from 'react-i18next'; + +interface BreadcrumbItemProps { + translationKey: string; + namespace: string; +} +const BreadcrumbItem = ({ translationKey, namespace }: BreadcrumbItemProps) => { + const { t } = useTranslation(namespace); + return t(translationKey); +}; + +export default BreadcrumbItem; diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/Loading/Loading.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/Loading/Loading.tsx new file mode 100644 index 000000000000..d534bff440af --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/Loading/Loading.tsx @@ -0,0 +1,11 @@ +import { OsdsSpinner } from '@ovhcloud/ods-components/react'; + +export default function Loading() { + return ( +
+
+ +
+
+ ); +} diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/Loading/ProgressLoader.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/Loading/ProgressLoader.tsx new file mode 100644 index 000000000000..7e0cca660f09 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/Loading/ProgressLoader.tsx @@ -0,0 +1,35 @@ +import { useEffect, useState } from 'react'; + +export default function ProgressLoader() { + const [progress, setProgress] = useState(0); + + useEffect(() => { + const interval = setInterval(() => { + setProgress((prevProgress) => { + // Increment progress in a non-linear fashion for a more dynamic feel + const increment = Math.random() * 20; + const nextProgress = prevProgress + increment; + if (nextProgress < 95) { + return nextProgress; + } + clearInterval(interval); // Stop incrementing near 100% to mimic NProgress behavior + return prevProgress; + }); + }, 500); // Increase progress more frequently + + return () => clearInterval(interval); // Cleanup on component unmount + }, []); + + return ( +
+
+
+ ); +} diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/PageLayout/PageLayout.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/PageLayout/PageLayout.tsx new file mode 100644 index 000000000000..df32d00961fc --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/PageLayout/PageLayout.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +const PageLayout: React.FC = ({ children }) => { + return ( +
+ {children} +
+ ); +}; + +export default PageLayout; diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/errorBoundary.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/errorBoundary.tsx new file mode 100644 index 000000000000..66832b01741f --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/errorBoundary.tsx @@ -0,0 +1,74 @@ +import { useNavigation } from '@ovh-ux/manager-react-shell-client'; +import { useRouteError } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import errorImgSrc from '@/public/assets/oops.png'; +import { Alert, AlertDescription } from './ui/alert'; +import { Button } from './ui/button'; + +const ErrorBoundary = () => { + const error = useRouteError(); + const { t } = useTranslation('error'); + const nav = useNavigation(); + + const navigateToHomepage = () => { + nav.navigateTo('public-cloud', '', {}); + }; + const reloadPage = () => { + nav.reload(); + }; + + if (error instanceof Error) { + return ( +
+
+
+ +
+

{t('manager_error_page_title')}

+ + +

{t('manager_error_page_default')}

+

{error.message}

+ {'xOvhQueryId' in error && ( +

+ {t('manager_error_page_detail_code', { + code: error.xOvhQueryId, + })} +

+ )} +
+
+ +
+ + +
+
+
+ ); + } + return <>; +}; + +export default ErrorBoundary; diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/links.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/links.tsx new file mode 100644 index 000000000000..edca85019d07 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/links.tsx @@ -0,0 +1,154 @@ +import * as React from 'react'; +import { useNavigation } from '@ovh-ux/manager-react-shell-client'; +import { + LinkProps, + NavLink as RouterNavLink, + Link as RouterLink, + NavLinkProps, +} from 'react-router-dom'; +import { cn } from '@/lib/utils'; +import { useLoadingIndicatorContext } from '@/contexts/loadingIndicatorContext'; + +function A({ + className, + children, + disabled, + ...props +}: React.AnchorHTMLAttributes & { disabled?: boolean }) { + const baseClassName = + 'text-primary-500 font-semibold outiline-none cursor-pointer no-underline hover:text-primary-700 hover:underline'; + const disabledClass = 'opacity-50 cursor-not-allowed hover:text-primary-500'; + const combinedClassName = cn( + baseClassName, + className, + disabled && disabledClass, + ); + if (disabled) { + // eslint-disable-next-line no-unused-vars + const { href, onClick, tabIndex, ...otherProps } = props; + return ( + + {children} + + ); + } + + return ( + + {children} + + ); +} + +function Link({ + className, + disabled, + children, + to, + ...props +}: LinkProps & { disabled?: boolean }) { + const { setLoading } = useLoadingIndicatorContext(); + const baseClassName = + 'text-primary-500 font-semibold outiline-none cursor-pointer no-underline hover:text-primary-700 hover:underline'; + const disabledClass = 'opacity-50 cursor-not-allowed hover:text-primary-500'; + const combinedClassName = cn( + baseClassName, + className, + disabled && disabledClass, + ); + return ( + { + if (disabled) { + e.preventDefault(); + } else { + setLoading(true); + } + }} + > + {children} + + ); +} + +function NavLink({ + className, + disabled, + children, + to, + end, + ...props +}: NavLinkProps & { disabled?: boolean }) { + const { setLoading } = useLoadingIndicatorContext(); + const baseClassName = + 'whitespace-nowrap w-fit text-primary-500 text-base font-semibold m-0 py-2 hover:text-primary-700'; + const activeClass = 'border-b-2 border-primary-500'; + const disabledClass = 'cursor-not-allowed opacity-50 hover:text-primary-500'; + + return ( + + cn( + baseClassName, + isActive && activeClass, + className, + disabled && disabledClass, + ) + } + onClick={(e) => { + if (disabled) { + e.preventDefault(); + } else { + setLoading(true); + } + }} + {...props} + > + {children} + + ); +} + +interface OvhLinkProps { + application: string; + path: string; + params?: Record; +} +function OvhLink({ + application, + path, + params = {}, + children, + ...props +}: React.AnchorHTMLAttributes & + OvhLinkProps & { disabled?: boolean }) { + const navigation = useNavigation(); + const [url, setUrl] = React.useState(''); + React.useEffect(() => { + const fetchUrl = async (urlParams: OvhLinkProps) => { + const goTo = (await navigation.getURL( + urlParams.application, + urlParams.path, + urlParams.params, + )) as string; + setUrl(goTo); + }; + fetchUrl({ application, path, params }); + }, [application, path, params, navigation]); + return ( + + {children} + + ); +} +export { A, Link, NavLink, OvhLink }; diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/table-date.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/table-date.tsx new file mode 100644 index 000000000000..703bd2fe396c --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/table-date.tsx @@ -0,0 +1,17 @@ +import { useMemo } from 'react'; +import { useLocale } from '@/hooks/useLocale'; + +interface TableDateCellProps { + date: Date; + options?: Intl.DateTimeFormatOptions; +} + +const FormattedDate = ({ date, options }: TableDateCellProps) => { + const locale = useLocale(); + const formater = useMemo( + () => new Intl.DateTimeFormat(locale.replace('_', '-'), options), + [locale], + ); + return formater.format(date); +}; +export default FormattedDate; diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/tabs-menu.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/tabs-menu.tsx new file mode 100644 index 000000000000..90ecea26b442 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/tabs-menu.tsx @@ -0,0 +1,85 @@ +import { useRef } from 'react'; +import { Skeleton } from './ui/skeleton'; +import { ScrollArea, ScrollBar } from './ui/scroll-area'; +import { NavLink } from './links'; +import { Badge } from './ui/badge'; + +export interface Tab { + href: string; + label: string; + count?: number; + end?: boolean; + disabled?: boolean; +} +interface TabsMenuProps { + tabs: Tab[]; +} +const TabsMenu = ({ tabs }: TabsMenuProps) => { + const s = useRef(null); + return ( + { + s.current.children[1].scrollLeft += e.deltaY; + }} + > +
+ {tabs.map((tab, index) => ( + + {({ isActive }) => ( + { + if (node && isActive) + node?.scrollIntoView({ + behavior: 'smooth', + block: 'center', + }); + }} + > + {tab.label} + {tab.count > 0 && ( + + {tab.count} + + )} + + )} + + ))} +
+ +
+ ); +}; + +TabsMenu.Skeleton = function TabsMenuSkeleton() { + return ( +
+
+ + + + + + + + + + + + +
+
+ ); +}; + +export default TabsMenu; diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/ui/accordion.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/ui/accordion.tsx new file mode 100644 index 000000000000..e6a723d06574 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/ui/accordion.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDown } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Accordion = AccordionPrimitive.Root + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AccordionItem.displayName = "AccordionItem" + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + +)) +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)) + +AccordionContent.displayName = AccordionPrimitive.Content.displayName + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/ui/alert-dialog.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/ui/alert-dialog.tsx new file mode 100644 index 000000000000..8722561cf6bd --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/ui/alert-dialog.tsx @@ -0,0 +1,139 @@ +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/ui/alert.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/ui/alert.tsx new file mode 100644 index 000000000000..924b130c73e3 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/ui/alert.tsx @@ -0,0 +1,69 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + info: + "border-transparent bg-blue-100 text-blue-700 [&>svg]:text-blue-700", + success: + "border-transparent bg-green-100 text-green-700 [&>svg]:text-green-700", + warning: + "border-transparent bg-yellow-200 text-yellow-800 [&>svg]:text-green-800", + error: + "border-transparent bg-red-100 text-red-700 [&>svg]:text-red-700", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, children, ...props }, ref) => ( +
+ {children} +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/ui/badge.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/ui/badge.tsx new file mode 100644 index 000000000000..c2ed15071965 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/ui/badge.tsx @@ -0,0 +1,44 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + success: + "border-transparent bg-green-100 text-green-700 hover:bg-green-100/80", + info: + "border-transparent bg-blue-100 text-blue-700 hover:bg-blue-100/80", + warning: + "border-transparent bg-yellow-300 text-yellow-800 hover:bg-yellow-100/80", + error: + "border-transparent bg-red-100 text-red-700 hover:bg-red-100/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/ui/button.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/ui/button.tsx new file mode 100644 index 000000000000..57f4a373a512 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/ui/button.tsx @@ -0,0 +1,64 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary-600", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border text-primary border-primary border-2 bg-background font-semibold hover:bg-primary-100", + menu: + "border text-primary border-primary border-2 bg-background font-semibold hover:bg-primary-100 rounded-full", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + table: "hover:bg-primary-100 hover:text-primary-700 hover:font-semibold", + link: "text-primary underline-offset-4 hover:underline", + input: "border border-input bg-background", + }, + size: { + default: "h-10 px-4 py-2 text-base", + sm: "h-9 rounded-md px-3 text-sm", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + link: "text-base", + table: "h-4 w-4 my-auto", + menu: 'size-8 p-0', + input: "h-10 w-full rounded-md px-3 py-2 text-sm" + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/ui/calendar.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/ui/calendar.tsx new file mode 100644 index 000000000000..b065f8e0cd2b --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/ui/calendar.tsx @@ -0,0 +1,64 @@ +import * as React from "react" +import { ChevronLeft, ChevronRight } from "lucide-react" +import { DayPicker } from "react-day-picker" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +export type CalendarProps = React.ComponentProps + +function Calendar({ + className, + classNames, + showOutsideDays = true, + ...props +}: CalendarProps) { + return ( + , + IconRight: ({ ...props }) => , + }} + {...props} + /> + ) +} +Calendar.displayName = "Calendar" + +export { Calendar } diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/ui/card.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/ui/card.tsx new file mode 100644 index 000000000000..60b8cbe52fa1 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/ui/card.tsx @@ -0,0 +1,79 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, children, ...props }, ref) => ( +

{children}

+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/ui/collapsible.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/ui/collapsible.tsx new file mode 100644 index 000000000000..a23e7a281287 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/ui/collapsible.tsx @@ -0,0 +1,9 @@ +import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" + +const Collapsible = CollapsiblePrimitive.Root + +const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger + +const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent + +export { Collapsible, CollapsibleTrigger, CollapsibleContent } diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/ui/command.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/ui/command.tsx new file mode 100644 index 000000000000..d4a5338a3d59 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/ui/command.tsx @@ -0,0 +1,154 @@ +/* eslint-disable react/no-unknown-property */ +import * as React from "react" +import { type DialogProps } from "@radix-ui/react-dialog" +import { Command as CommandPrimitive } from "cmdk" +import { Search } from "lucide-react" + +import { cn } from "@/lib/utils" +import { Dialog, DialogContent } from "@/components/ui/dialog" + +const Command = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Command.displayName = CommandPrimitive.displayName + +interface CommandDialogProps extends DialogProps {} + +const CommandDialog = ({ children, ...props }: CommandDialogProps) => { + return ( + + + + {children} + + + + ) +} + +const CommandInput = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( +
+ + +
+)) + +CommandInput.displayName = CommandPrimitive.Input.displayName + +const CommandList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandList.displayName = CommandPrimitive.List.displayName + +const CommandEmpty = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>((props, ref) => ( + +)) + +CommandEmpty.displayName = CommandPrimitive.Empty.displayName + +const CommandGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandGroup.displayName = CommandPrimitive.Group.displayName + +const CommandSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +CommandSeparator.displayName = CommandPrimitive.Separator.displayName + +const CommandItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandItem.displayName = CommandPrimitive.Item.displayName + +const CommandShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +CommandShortcut.displayName = "CommandShortcut" + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +} diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/ui/data-table.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/ui/data-table.tsx new file mode 100644 index 000000000000..a71a1c699516 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/ui/data-table.tsx @@ -0,0 +1,262 @@ +import { useState } from 'react'; +import { ChevronDown, ChevronUp, ChevronsUpDown, ChevronLeft, ChevronRight, ChevronFirst, ChevronLast } from 'lucide-react'; +import { + ColumnDef, + SortingColumn, + SortingState, + flexRender, + Table as TanStackTable, + getCoreRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from '@tanstack/react-table'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table'; +import { Button } from '@/components/ui/button'; +import { Skeleton } from './skeleton'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './select'; + +interface DataTablePaginationProps { + table: TanStackTable +} +export function DataTablePagination({ + table, +}: DataTablePaginationProps) { + return ( +
+
+
+ +
+
+ Page {table.getState().pagination.pageIndex + 1} of{" "} + {table.getPageCount()} +
+
+ + + + +
+
+
+ ); +} + +interface DataTableProps { + columns: ColumnDef[]; + data: TData[]; + pageSize?: number; +} + +export function DataTable({ + columns, + data, + pageSize, +}: DataTableProps) { + const [sorting, setSorting] = useState([]); + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + onSortingChange: setSorting, + getSortedRowModel: getSortedRowModel(), + state: { + sorting, + }, + initialState: { + pagination: { pageSize: pageSize ?? 5 }, + }, + }); + + return ( + <> +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+ + + ); +} + +interface SortableHeaderProps { + column: SortingColumn; + children: React.ReactNode; +} +export function SortableHeader({ + column, + children, +}: SortableHeaderProps) { + const sort = column.getIsSorted(); + let icon = ; + if (sort === 'asc') { + icon = ; + } else if (sort === 'desc') { + icon = ; + } + + const buttonClass = `px-0 font-bold hover:bg-primary-100 ${ + sort + ? 'text-primary-500 hover:text-primary-500' + : 'text-primary-700 hover:text-primary-500' + }`; + return ( + + ); +} + +interface DataTableSkeletonProps { + columns?: number; + rows?: number; + height?: number; + width?: number; +} +DataTable.Skeleton = function DataTableSkeleton({ + columns = 5, + rows = 5, + height = 16, + width = 80, +}: DataTableSkeletonProps) { + return ( + + + + {Array.from({ length: columns }).map((colHead, iColHead) => ( + + + + ))} + + + + {Array.from({ length: rows }).map((row, iRow) => ( + + {Array.from({ length: columns }).map((col, iCol) => ( + + + + ))} + + ))} + +
+ ); +}; \ No newline at end of file diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/ui/dialog.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/ui/dialog.tsx new file mode 100644 index 000000000000..c23630eb8415 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/ui/dialog.tsx @@ -0,0 +1,120 @@ +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Dialog = DialogPrimitive.Root + +const DialogTrigger = DialogPrimitive.Trigger + +const DialogPortal = DialogPrimitive.Portal + +const DialogClose = DialogPrimitive.Close + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +DialogContent.displayName = DialogPrimitive.Content.displayName + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogHeader.displayName = "DialogHeader" + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogFooter.displayName = "DialogFooter" + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogTitle.displayName = DialogPrimitive.Title.displayName + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogDescription.displayName = DialogPrimitive.Description.displayName + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogClose, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +} diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/ui/dropdown-menu.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/ui/dropdown-menu.tsx new file mode 100644 index 000000000000..f3b4e7e4aad4 --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/ui/dropdown-menu.tsx @@ -0,0 +1,218 @@ +import * as React from "react" +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" +import { Check, ChevronRight, Circle } from "lucide-react" + +import { cn } from "@/lib/utils" +import { VariantProps, cva } from "class-variance-authority" + +const DropdownMenu = DropdownMenuPrimitive.Root + +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger + +const DropdownMenuGroup = DropdownMenuPrimitive.Group + +const DropdownMenuPortal = DropdownMenuPrimitive.Portal + +const DropdownMenuSub = DropdownMenuPrimitive.Sub + +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup + +const DropdownMenuSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +DropdownMenuSubTrigger.displayName = + DropdownMenuPrimitive.SubTrigger.displayName + +const DropdownMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DropdownMenuSubContent.displayName = + DropdownMenuPrimitive.SubContent.displayName + +const DropdownMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + + + +)) +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName + +const dropdownMenuItemVariant = cva( + "relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50", + { + variants: { + variant: { + default: "focus:bg-accent focus:text-accent-foreground", + primary: "text-primary-500 focus:text-primary-500 focus:bg-primary-100 font-semibold", + destructive: + "text-red-500 focus:text-red-500 focus:bg-red-100 font-semibold", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) +export interface DropdownMenuItemProps + extends React.ComponentPropsWithoutRef, + VariantProps { + inset?: boolean; + } + +const DropdownMenuItem = React.forwardRef< + React.ElementRef, + DropdownMenuItemProps +>(({ className, variant, inset, ...props }, ref) => ( +
+ +
+)) +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName + +const DropdownMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)) +DropdownMenuCheckboxItem.displayName = + DropdownMenuPrimitive.CheckboxItem.displayName + +const DropdownMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName + +const DropdownMenuShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +DropdownMenuShortcut.displayName = "DropdownMenuShortcut" + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, + dropdownMenuItemVariant, +} diff --git a/packages/manager/apps/pci-ai-notebooks/src/components/ui/form.tsx b/packages/manager/apps/pci-ai-notebooks/src/components/ui/form.tsx new file mode 100644 index 000000000000..4603f8b3d5ee --- /dev/null +++ b/packages/manager/apps/pci-ai-notebooks/src/components/ui/form.tsx @@ -0,0 +1,176 @@ +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { Slot } from "@radix-ui/react-slot" +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, +} from "react-hook-form" + +import { cn } from "@/lib/utils" +import { Label } from "@/components/ui/label" + +const Form = FormProvider + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath +> = { + name: TName +} + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue +) + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath +>({ + ...props +}: ControllerProps) => { + return ( + + + + ) +} + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext) + const itemContext = React.useContext(FormItemContext) + const { getFieldState, formState } = useFormContext() + + const fieldState = getFieldState(fieldContext.name, formState) + + if (!fieldContext) { + throw new Error("useFormField should be used within ") + } + + const { id } = itemContext + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + } +} + +type FormItemContextValue = { + id: string +} + +const FormItemContext = React.createContext( + {} as FormItemContextValue +) + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId() + + return ( + +
+ + ) +}) +FormItem.displayName = "FormItem" + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField() + + return ( +