From efbdcc0e923909d0402e8129ed01da9988ee2d34 Mon Sep 17 00:00:00 2001 From: Bikash <31549600+tulsiojha@users.noreply.github.com> Date: Sun, 14 Jan 2024 22:45:29 +0530 Subject: [PATCH] Features/design (#67) * :art: Cli queries updated * :art: Cli queries updated * fixed issue with react-select dep * :art: Added cli queries * UI refactoring * :art: Cli queries updated * Managed service and resources wip * :art: Updated cli queries * Worked on managed-resources and services, sync-status, bugfixes * Fixed ts error * UI fixes * :sparkles: Logs component updated * UI Fixes * Deleted storybook-dist * WIP * nested yup validation error in progress * :bug: Fixed issue with validation in managed service * :bug: Fixed issue with validation of managed service creation * :bug: Fixed issue with managed service validation * :sparkles: Added log screen to app * :bug: Fixed type issue * UI Refactoring * :art: Logs improved * :art: Cli queries updated * :art: Updated cli queries * :art: Updated cli queries * UI fixes * Fixed merge conflits and ts errors * fixed types issue --------- Co-authored-by: Abdhesh Nayak --- .gitignore | 3 +- fake-data-generator/gen.ts | 1 + gql-queries-generator/doc/queries.graphql | 2211 ++++++++++++++--- lib/client/helpers/socket-context.tsx | 2 +- lib/client/hooks/use-unsaved-changes.tsx | 73 +- lib/configs/base-url.cjs | 2 +- .../helpers/execute-query-with-context.ts | 4 + package.json | 4 +- pnpm-lock.yaml | 39 +- src/apps/auth/routes/_main+/api.tsx | 8 +- src/apps/auth/routes/test/_index.tsx | 19 - src/apps/auth/server/gql/cli-queries.ts | 316 ++- .../auth/server/gql/queries/infra-queries.ts | 112 + .../components/console-list-components.tsx | 61 +- src/apps/console/components/id-selector.tsx | 9 +- src/apps/console/components/logger.tsx | 381 +-- src/apps/console/components/menu-select.tsx | 8 + src/apps/console/components/name-id-view.tsx | 261 ++ src/apps/console/components/pulsable.tsx | 31 +- .../console/components/sidebar-layout.tsx | 3 +- src/apps/console/components/sync-status.tsx | 216 ++ src/apps/console/dummy/data.js | 89 +- .../console/hooks/useProgressWrapperState.tsx | 91 + .../console/page-components/app-states.tsx | 2 +- .../handle-console-devices.tsx | 688 +++++ .../console/page-components/new-cluster.tsx | 267 +- .../console/page-components/new-project.tsx | 228 +- .../console/page-components/new-scope.tsx | 67 +- src/apps/console/page-components/util.tsx | 2 +- src/apps/console/routes/_a+/new-team.tsx | 3 +- src/apps/console/routes/_a+/teams.tsx | 2 +- .../$workspace+/app+/$app+/logs/route.tsx | 80 + .../$workspace+/app+/$app+/logs/tools.tsx | 30 + .../$project+/$environment+/_layout.tsx | 183 +- .../$environment+/app+/$app+/logs/route.tsx | 27 + .../$environment+/app+/$app+/route.tsx | 117 +- .../app+/$app+/settings+/_layout.tsx | 155 +- .../app+/$app+/settings+/advance/route.tsx | 164 +- .../app+/$app+/settings+/compute/route.tsx | 327 ++- .../$app+/settings+/environment/route.tsx | 61 +- .../app+/$app+/settings+/general/route.tsx | 92 +- .../app+/$app+/settings+/network/route.tsx | 41 +- .../$environment+/apps/apps-resources.tsx | 177 +- .../$project+/$environment+/apps/route.tsx | 6 +- .../cs+/configs._index/handle-config.tsx | 40 +- .../cs+/secrets._index/handle-secret.tsx | 45 +- .../handle-managed-resource.tsx | 347 +++ .../managed-resources-resource.tsx | 248 ++ .../{routers => managed-resources}/route.tsx | 62 +- .../$environment+/managed-resources/tools.tsx | 29 + .../$environment+/new-app/app-compute.tsx | 33 +- .../$environment+/new-app/app-detail.tsx | 48 +- .../$environment+/new-app/app-dialogs.tsx | 6 +- .../new-app/app-environment-mounts.tsx | 22 +- .../new-app/app-environment-variables.tsx | 61 +- .../$environment+/new-app/app-network.tsx | 18 +- .../$environment+/new-app/app-review.tsx | 8 +- .../$project+/$environment+/new-app/route.tsx | 32 +- .../new-managed-resource/_index.tsx | 709 ++++++ .../$environment+/router+/$router+/_index.tsx | 7 + .../router+/$router+/_layout.tsx | 87 + .../router+/$router+/routes/_index.tsx | 88 + .../router+/$router+/routes/handle-route.tsx | 250 ++ .../$router+/routes/route-resources.tsx | 367 +++ .../router+/$router+/routes/tools.tsx | 29 + .../router+/$router+/settings/_index.tsx | 6 + .../router+/$router+/settings/_layout.tsx | 17 + .../$router+/settings/general/route.tsx | 164 ++ .../$environment+/routers/_index.tsx | 96 + .../$environment+/routers/handle-router.tsx | 219 ++ .../routers/router-resources.tsx | 230 ++ .../$project+/$environment+/routers/tools.tsx | 2 - .../$environment+/settings+/general/route.tsx | 48 +- .../_main+/$account+/$project+/_index.tsx | 4 +- .../_main+/$account+/$project+/_layout.tsx | 184 +- .../environments/clone-environment.tsx | 121 + .../$project+/environments/resources.tsx | 105 +- .../$project+/environments/route.tsx | 1 + .../backend-services-resources.tsx | 47 +- .../handle-backend-service.tsx | 559 ++--- .../$project+/managed-services/route.tsx | 125 +- .../$project+/new-managed-service/_index.tsx | 645 +++++ .../$account+/$project+/settings+/_index.tsx | 2 +- .../$account+/$project+/settings+/_layout.tsx | 6 +- .../$project+/settings+/general/route.tsx | 215 +- .../routes/_main+/$account+/_layout.tsx | 298 ++- .../$account+/infra+/$cluster+/_layout.tsx | 2 +- .../helm-charts/handle-helm-chart.tsx | 140 +- .../helm-charts/helm-chart-resources.tsx | 10 +- .../$cluster+/network+/vpn/handle-devices.tsx | 82 +- .../$cluster+/nodepools/handle-nodepool.tsx | 50 +- .../nodepools/nodepool-resources.tsx | 31 +- .../infra+/$cluster+/nodepools/route.tsx | 8 +- .../infra+/$cluster+/np.$nodepool/route.tsx | 18 +- .../infra+/$cluster+/overview+/logs/route.tsx | 87 + .../infra+/$cluster+/settings+/_index.tsx | 5 + .../settings+/domain/domain-resources.tsx | 17 +- .../$cluster+/settings+/domain/route.tsx | 18 - .../$cluster+/settings+/general/route.tsx | 294 ++- .../infra+/$cluster+/storage/route.tsx | 2 +- .../$cluster+/storage/storage-resources.tsx | 170 +- .../infra+/clusters/cluster-resources.tsx | 15 +- .../packages+/repos/repo-resources.tsx | 10 +- .../$account+/packages+/repos/route.tsx | 2 +- .../$account+/projects/project-resources.tsx | 25 +- .../_main+/$account+/projects/route.tsx | 3 +- .../$account+/repo+/$repo+/builds/_index.tsx | 11 +- .../repo+/$repo+/builds/handle-builds.tsx | 16 +- .../cloud-providers/handle-provider.tsx | 48 +- .../_main+/$account+/settings+/general.tsx | 53 +- .../_main+/$account+/settings+/sample.tsx | 1 - .../vpn/console-devices-resources.tsx | 262 ++ .../routes/_main+/$account+/vpn/route.tsx | 110 + .../routes/_main+/$account+/vpn/tools.tsx | 10 + .../console/routes/_main+/_layout/_layout.tsx | 17 +- src/apps/console/routes/test2/_index.tsx | 27 + .../server/gql/queries/account-queries.ts | 25 +- .../console/server/gql/queries/app-queries.ts | 106 +- .../server/gql/queries/base-queries.ts | 17 +- .../cluster-managed-services-queries.ts | 32 +- .../server/gql/queries/cluster-queries.ts | 34 +- .../server/gql/queries/console-vpn-queries.ts | 235 ++ .../server/gql/queries/environment-queries.ts | 65 +- .../server/gql/queries/helm-chart-queries.ts | 32 +- .../gql/queries/managed-resources-queries.ts | 228 ++ .../gql/queries/managed-templates-queries.ts | 24 + .../server/gql/queries/namespace-queries.ts | 89 + .../server/gql/queries/nodepool-queries.ts | 26 +- .../project-managed-services-queries.ts | 239 ++ .../server/gql/queries/project-queries.ts | 148 +- .../console/server/gql/queries/pv-queries.ts | 519 ++++ .../server/gql/queries/router-queries.ts | 180 +- src/apps/console/server/gql/saved-queries.ts | 10 + .../console/server/r-utils/key-constants.js | 1 + src/apps/console/utils/commons.tsx | 189 +- src/design-system/components/atoms/button.tsx | 1 + src/design-system/components/atoms/input.tsx | 18 +- .../components/atoms/option-list.tsx | 10 +- src/design-system/components/atoms/select.tsx | 38 +- src/design-system/components/atoms/switch.tsx | 12 +- src/design-system/components/atoms/tabs.tsx | 43 +- .../components/atoms/tooltip.tsx | 8 +- .../components/molecule/profile.tsx | 2 +- src/design-system/index.css | 11 +- .../assets/ActionList.stories-22d68721.js | 3 - .../assets/AlertDialog.stories-c992ad58.js | 3 - .../assets/Avatar.stories-c59013f1.js | 10 - .../assets/Badge.stories-83f9cf20.js | 27 - .../assets/BrandLogo.stories-11d50ae6.js | 7 - .../assets/Button.stories-db7ed9f5.js | 18 - .../assets/ButtonGroup.stories-e0af8175.js | 7 - .../assets/CalendarCheckFill-682d21c0.js | 1 - .../assets/CaretDownFill-e9561289.js | 1 - .../assets/Checkbox.stories-28c69722.js | 31 - .../assets/ChipGroup.stories-d17a2102.js | 7 - .../assets/Color-6VNJS4EI-2e1024a8.js | 1 - .../assets/DocsRenderer-NNNQARDV-227c57ff.js | 364 --- .../assets/EmptyState.stories-f1dc426f.js | 3 - .../assets/IconButton.stories-a790cd98.js | 6 - .../storybook/assets/Info-593bb6af.js | 1 - .../assets/NumberInput.stories-ad7137a4.js | 7 - .../assets/OptionList.stories-ffb8d528.js | 5 - .../assets/Pagination.stories-ec3c439b.js | 3 - .../assets/Popup.stories-9409a9a7.js | 3 - .../assets/ProdLogo.stories-e48351af.js | 7 - .../assets/Profile.stories-236afae6.js | 7 - .../ProgressTracker.stories-d867bcf9.js | 3 - .../assets/RadioGroup.stories-571833ab.js | 20 - .../storybook/assets/Search-7d018a47.js | 1 - .../storybook/assets/Spinner-6e10423b.js | 1 - .../assets/Switch.stories-51cc71a2.js | 15 - .../storybook/assets/Tabs.stories-e3c129cb.js | 36 - .../assets/TextArea.stories-cebe3fdf.js | 23 - .../assets/TextInput.stories-c07bc7cc.js | 47 - .../assets/Thumbnail.stories-4f56a650.js | 12 - .../assets/Toolbar.stories-0951988f.js | 16 - .../assets/Tooltip.stories-ed0c139a.js | 6 - .../assets/WithTooltip-V3YHNWJZ-158339cc.js | 1 - .../assets/WsLogo.stories-b85f89b3.js | 7 - .../assets/_commonjsHelpers-de833af9.js | 1 - .../assets/_getPrototype-1e53b583.js | 3 - .../storybook/assets/avatar-7a638a71.js | 1 - .../storybook/assets/bounce-it-356267b3.js | 1 - .../storybook/assets/button-30fb7d7d.js | 1 - .../storybook/assets/context-494caab9.js | 1 - .../assets/entry-preview-1f5c28fc.js | 1 - .../assets/entry-preview-docs-c7c6facc.js | 54 - .../assets/floating-ui.react-dom-7fbf629f.js | 1 - .../assets/formatter-SWP5E3XI-f667b9ba.js | 156 -- .../storybook/assets/iframe-bab8cdcd.js | 1 - .../storybook/assets/index-11a3acc1.js | 1 - .../storybook/assets/index-11d98b33.js | 1 - .../storybook/assets/index-1415155a.js | 1 - .../storybook/assets/index-2a7aefac.js | 1 - .../storybook/assets/index-2c2e1a29.js | 1 - .../storybook/assets/index-356e4a49.js | 6 - .../storybook/assets/index-3fd0d1ea.js | 1 - .../storybook/assets/index-4f95d78e.js | 1 - .../storybook/assets/index-7437dbe9.js | 1 - .../storybook/assets/index-76fb7be0.js | 9 - .../storybook/assets/index-8271c066.js | 1 - .../storybook/assets/index-858efc6d.js | 1 - .../storybook/assets/index-9303ff2d.js | 1 - .../storybook/assets/index-add589df.js | 9 - .../storybook/assets/index-d3597871.js | 1 - .../storybook/assets/index-d3ea75b5.js | 24 - .../storybook/assets/index-e131923d.js | 5 - .../storybook/assets/index-f287081f.js | 41 - .../storybook/assets/input-ca5f4eed.js | 1 - .../storybook/assets/jsx-runtime-29545a09.js | 9 - .../storybook/assets/motion-e61dcc0d.js | 1 - .../storybook/assets/option-list-184dfe76.js | 1 - .../storybook/assets/preview-0ef86afd.js | 7 - .../storybook/assets/preview-21802b0a.js | 27 - .../storybook/assets/preview-30b54f76.js | 20 - .../storybook/assets/preview-3dbe9685.css | 1 - .../storybook/assets/preview-73104b77.js | 1 - .../storybook/assets/preview-8df49943.js | 1 - .../storybook/assets/preview-96f1eb3d.js | 1 - .../storybook/assets/preview-c56bf6ac.js | 1 - .../storybook/assets/preview-d01b88e8.js | 17 - .../storybook/assets/preview-da31036b.js | 396 --- .../storybook/assets/profile-16b174b1.js | 1 - .../storybook/assets/react-18-988a5df2.js | 1 - .../syntaxhighlighter-V7JZZA35-49a0d4fe.js | 1 - .../storybook/assets/tabs-cfc133aa.js | 1 - .../assets/use-force-update-d6d40804.js | 1 - .../storybook/assets/utils-e72801fa.js | 1 - .../storybook-dist/storybook/favicon.svg | 7 - .../storybook-dist/storybook/iframe.html | 460 ---- .../storybook-dist/storybook/index.html | 127 - .../storybook-dist/storybook/index.json | 1 - .../storybook-dist/storybook/project.json | 1 - .../essentials-actions-2/manager-bundle.js | 3 - .../manager-bundle.js.LEGAL.txt | 0 .../manager-bundle.js | 12 - .../manager-bundle.js.LEGAL.txt | 0 .../essentials-controls-1/manager-bundle.js | 63 - .../manager-bundle.js.LEGAL.txt | 18 - .../essentials-measure-6/manager-bundle.js | 3 - .../manager-bundle.js.LEGAL.txt | 0 .../essentials-outline-7/manager-bundle.js | 3 - .../manager-bundle.js.LEGAL.txt | 0 .../essentials-toolbars-5/manager-bundle.js | 3 - .../manager-bundle.js.LEGAL.txt | 0 .../essentials-viewport-4/manager-bundle.js | 3 - .../manager-bundle.js.LEGAL.txt | 0 .../interactions-8/manager-bundle.js | 12 - .../manager-bundle.js.LEGAL.txt | 0 .../sb-addons/links-0/manager-bundle.js | 3 - .../links-0/manager-bundle.js.LEGAL.txt | 0 .../storybook/sb-common-assets/fonts.css | 31 - .../nunito-sans-bold-italic.woff2 | Bin 49460 -> 0 bytes .../sb-common-assets/nunito-sans-bold.woff2 | Bin 47144 -> 0 bytes .../sb-common-assets/nunito-sans-italic.woff2 | Bin 49620 -> 0 bytes .../nunito-sans-regular.woff2 | Bin 47072 -> 0 bytes .../WithTooltip-V3YHNWJZ-LVYLGZW2.js | 1 - .../storybook/sb-manager/chunk-2IXBUOFS.js | 7 - .../storybook/sb-manager/chunk-6ZZ2M5VW.js | 406 --- .../storybook/sb-manager/chunk-INSKDKQB.js | 348 --- .../storybook/sb-manager/chunk-NGTUFCUO.js | 9 - .../storybook/sb-manager/chunk-ZEU7PDD3.js | 1 - .../sb-manager/formatter-SWP5E3XI-7BGIK6BL.js | 156 -- .../sb-manager/globals-module-info.js | 1 - .../storybook/sb-manager/globals.js | 1 - .../storybook/sb-manager/index.js | 1 - .../storybook/sb-manager/runtime.js | 1 - .../syntaxhighlighter-V7JZZA35-DXZCI2WR.js | 1 - .../storybook/sb-preview/globals.js | 1 - .../storybook/sb-preview/runtime.js | 112 - .../storybook-dist/storybook/stories.json | 1 - src/generated/gql/sdl.graphql | 305 ++- src/generated/gql/server.ts | 2015 ++++++++++++--- 273 files changed, 15039 insertions(+), 6784 deletions(-) delete mode 100644 src/apps/auth/routes/test/_index.tsx create mode 100644 src/apps/auth/server/gql/queries/infra-queries.ts create mode 100644 src/apps/console/components/name-id-view.tsx create mode 100644 src/apps/console/components/sync-status.tsx create mode 100644 src/apps/console/hooks/useProgressWrapperState.tsx create mode 100644 src/apps/console/page-components/handle-console-devices.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$cluster+/$project+/$scope+/$workspace+/app+/$app+/logs/route.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$cluster+/$project+/$scope+/$workspace+/app+/$app+/logs/tools.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/logs/route.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/managed-resources/handle-managed-resource.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/managed-resources/managed-resources-resource.tsx rename src/apps/console/routes/_main+/$account+/$project+/$environment+/{routers => managed-resources}/route.tsx (54%) create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/managed-resources/tools.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/new-managed-resource/_index.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/_index.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/_layout.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/routes/_index.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/routes/handle-route.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/routes/route-resources.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/routes/tools.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/settings/_index.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/settings/_layout.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/settings/general/route.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/_index.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/handle-router.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/router-resources.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/environments/clone-environment.tsx create mode 100644 src/apps/console/routes/_main+/$account+/$project+/new-managed-service/_index.tsx create mode 100644 src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/logs/route.tsx create mode 100644 src/apps/console/routes/_main+/$account+/infra+/$cluster+/settings+/_index.tsx create mode 100644 src/apps/console/routes/_main+/$account+/vpn/console-devices-resources.tsx create mode 100644 src/apps/console/routes/_main+/$account+/vpn/route.tsx create mode 100644 src/apps/console/routes/_main+/$account+/vpn/tools.tsx create mode 100644 src/apps/console/routes/test2/_index.tsx create mode 100644 src/apps/console/server/gql/queries/console-vpn-queries.ts create mode 100644 src/apps/console/server/gql/queries/managed-resources-queries.ts create mode 100644 src/apps/console/server/gql/queries/namespace-queries.ts create mode 100644 src/apps/console/server/gql/queries/project-managed-services-queries.ts create mode 100644 src/apps/console/server/gql/queries/pv-queries.ts delete mode 100644 src/design-system/storybook-dist/storybook/assets/ActionList.stories-22d68721.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/AlertDialog.stories-c992ad58.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Avatar.stories-c59013f1.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Badge.stories-83f9cf20.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/BrandLogo.stories-11d50ae6.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Button.stories-db7ed9f5.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/ButtonGroup.stories-e0af8175.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/CalendarCheckFill-682d21c0.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/CaretDownFill-e9561289.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Checkbox.stories-28c69722.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/ChipGroup.stories-d17a2102.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Color-6VNJS4EI-2e1024a8.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/DocsRenderer-NNNQARDV-227c57ff.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/EmptyState.stories-f1dc426f.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/IconButton.stories-a790cd98.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Info-593bb6af.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/NumberInput.stories-ad7137a4.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/OptionList.stories-ffb8d528.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Pagination.stories-ec3c439b.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Popup.stories-9409a9a7.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/ProdLogo.stories-e48351af.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Profile.stories-236afae6.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/ProgressTracker.stories-d867bcf9.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/RadioGroup.stories-571833ab.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Search-7d018a47.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Spinner-6e10423b.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Switch.stories-51cc71a2.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Tabs.stories-e3c129cb.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/TextArea.stories-cebe3fdf.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/TextInput.stories-c07bc7cc.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Thumbnail.stories-4f56a650.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Toolbar.stories-0951988f.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/Tooltip.stories-ed0c139a.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/WithTooltip-V3YHNWJZ-158339cc.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/WsLogo.stories-b85f89b3.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/_commonjsHelpers-de833af9.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/_getPrototype-1e53b583.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/avatar-7a638a71.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/bounce-it-356267b3.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/button-30fb7d7d.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/context-494caab9.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/entry-preview-1f5c28fc.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/entry-preview-docs-c7c6facc.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/floating-ui.react-dom-7fbf629f.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/formatter-SWP5E3XI-f667b9ba.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/iframe-bab8cdcd.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-11a3acc1.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-11d98b33.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-1415155a.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-2a7aefac.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-2c2e1a29.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-356e4a49.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-3fd0d1ea.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-4f95d78e.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-7437dbe9.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-76fb7be0.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-8271c066.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-858efc6d.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-9303ff2d.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-add589df.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-d3597871.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-d3ea75b5.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-e131923d.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/index-f287081f.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/input-ca5f4eed.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/jsx-runtime-29545a09.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/motion-e61dcc0d.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/option-list-184dfe76.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/preview-0ef86afd.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/preview-21802b0a.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/preview-30b54f76.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/preview-3dbe9685.css delete mode 100644 src/design-system/storybook-dist/storybook/assets/preview-73104b77.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/preview-8df49943.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/preview-96f1eb3d.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/preview-c56bf6ac.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/preview-d01b88e8.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/preview-da31036b.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/profile-16b174b1.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/react-18-988a5df2.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/syntaxhighlighter-V7JZZA35-49a0d4fe.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/tabs-cfc133aa.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/use-force-update-d6d40804.js delete mode 100644 src/design-system/storybook-dist/storybook/assets/utils-e72801fa.js delete mode 100644 src/design-system/storybook-dist/storybook/favicon.svg delete mode 100644 src/design-system/storybook-dist/storybook/iframe.html delete mode 100644 src/design-system/storybook-dist/storybook/index.html delete mode 100644 src/design-system/storybook-dist/storybook/index.json delete mode 100644 src/design-system/storybook-dist/storybook/project.json delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/essentials-actions-2/manager-bundle.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/essentials-actions-2/manager-bundle.js.LEGAL.txt delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/essentials-backgrounds-3/manager-bundle.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/essentials-backgrounds-3/manager-bundle.js.LEGAL.txt delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/essentials-controls-1/manager-bundle.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/essentials-controls-1/manager-bundle.js.LEGAL.txt delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/essentials-measure-6/manager-bundle.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/essentials-measure-6/manager-bundle.js.LEGAL.txt delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/essentials-outline-7/manager-bundle.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/essentials-outline-7/manager-bundle.js.LEGAL.txt delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/essentials-toolbars-5/manager-bundle.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/essentials-toolbars-5/manager-bundle.js.LEGAL.txt delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/essentials-viewport-4/manager-bundle.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/essentials-viewport-4/manager-bundle.js.LEGAL.txt delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/interactions-8/manager-bundle.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/interactions-8/manager-bundle.js.LEGAL.txt delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/links-0/manager-bundle.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-addons/links-0/manager-bundle.js.LEGAL.txt delete mode 100644 src/design-system/storybook-dist/storybook/sb-common-assets/fonts.css delete mode 100644 src/design-system/storybook-dist/storybook/sb-common-assets/nunito-sans-bold-italic.woff2 delete mode 100644 src/design-system/storybook-dist/storybook/sb-common-assets/nunito-sans-bold.woff2 delete mode 100644 src/design-system/storybook-dist/storybook/sb-common-assets/nunito-sans-italic.woff2 delete mode 100644 src/design-system/storybook-dist/storybook/sb-common-assets/nunito-sans-regular.woff2 delete mode 100644 src/design-system/storybook-dist/storybook/sb-manager/WithTooltip-V3YHNWJZ-LVYLGZW2.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-manager/chunk-2IXBUOFS.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-manager/chunk-6ZZ2M5VW.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-manager/chunk-INSKDKQB.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-manager/chunk-NGTUFCUO.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-manager/chunk-ZEU7PDD3.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-manager/formatter-SWP5E3XI-7BGIK6BL.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-manager/globals-module-info.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-manager/globals.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-manager/index.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-manager/runtime.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-manager/syntaxhighlighter-V7JZZA35-DXZCI2WR.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-preview/globals.js delete mode 100644 src/design-system/storybook-dist/storybook/sb-preview/runtime.js delete mode 100644 src/design-system/storybook-dist/storybook/stories.json diff --git a/.gitignore b/.gitignore index c8afaf902..fb61cdf7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ # Logs -logs *.log npm-debug.log* yarn-debug.log* @@ -28,4 +27,6 @@ public/** !public/package.json .tmp +storybook-dist + tsconfig-compile.tsbuildinfo diff --git a/fake-data-generator/gen.ts b/fake-data-generator/gen.ts index 6ad903c25..b0629d3bb 100644 --- a/fake-data-generator/gen.ts +++ b/fake-data-generator/gen.ts @@ -21,6 +21,7 @@ const types: string[] = [ 'ConsoleListClusterMSvsQuery', 'ConsoleListManagedResourceQuery', 'ConsoleListHelmChartQuery', + 'ConsoleListConsoleVpnDevices', ]; async function fake(files: string[], types: string[] = []) { diff --git a/gql-queries-generator/doc/queries.graphql b/gql-queries-generator/doc/queries.graphql index 791c20c14..94690d833 100644 --- a/gql-queries-generator/doc/queries.graphql +++ b/gql-queries-generator/doc/queries.graphql @@ -26,14 +26,14 @@ query consoleInfraCheckNameAvailability($resType: ResType!, $name: String!, $clu } } -query consoleCoreCheckNameAvailability($resType: ConsoleResType!, $name: String!, $namespace: String) { +query consoleCoreCheckNameAvailability($resType: ConsoleResType!, $name: String!, $projectName: String, $envName: String) { core_checkNameAvailability( resType: $resType name: $name - namespace: $namespace + projectName: $projectName + envName: $envName ) { result - suggestedNames } } @@ -80,12 +80,17 @@ query consoleGetAccount($accountName: String!) { updateTime contactEmail displayName - spec { - targetNamespace - } } } +mutation consoleDeleteAccount($accountName: String!) { + accounts_deleteAccount(accountName: $accountName) +} + +mutation consoleDeleteProject($name: String!) { + core_deleteProject(name: $name) +} + mutation consoleCreateProject($project: ProjectIn!) { core_createProject(project: $project) { id @@ -100,118 +105,124 @@ mutation consoleUpdateProject($project: ProjectIn!) { query consoleGetProject($name: String!) { core_getProject(name: $name) { - id - displayName - creationTime clusterName - apiVersion - kind + displayName metadata { - namespace name - labels - deletionTimestamp - generation - creationTimestamp - annotations + namespace } - recordVersion spec { targetNamespace - logo - displayName - clusterName - accountName } + accountName + apiVersion + createdBy { + userEmail + userId + userName + } + creationTime + id + kind + lastUpdatedBy { + userEmail + userId + userName + } + markedForDeletion + recordVersion status { + checks + isReady + lastReadyGeneration + lastReconcileTime + message { + RawMessage + } resources { - name - kind apiVersion + kind + name namespace } - message { - RawMessage - } - lastReconcileTime - isReady - checks } syncStatus { - syncScheduledAt - state - recordVersion - lastSyncedAt - error action + error + lastSyncedAt + recordVersion + state + syncScheduledAt } updateTime - accountName } } -query consoleListProjects($clusterName: String, $pagination: CursorPaginationIn, $search: SearchProjects) { - core_listProjects(clusterName: $clusterName, pq: $pagination, search: $search) { - totalCount +query consoleListProjects($search: SearchProjects, $pq: CursorPaginationIn) { + core_listProjects(search: $search, pq: $pq) { edges { + cursor node { - id - displayName - creationTime clusterName - apiVersion - kind createdBy { - userName userEmail userId + userName } + creationTime + displayName lastUpdatedBy { - userName - userId userEmail + userId + userName } + markedForDeletion metadata { - namespace - name - labels + annotations + creationTimestamp deletionTimestamp generation - creationTimestamp - annotations + labels + name + namespace } recordVersion spec { targetNamespace - logo - displayName - clusterName - accountName } status { + checks + isReady + lastReadyGeneration + lastReconcileTime + message { + RawMessage + } resources { - name - kind apiVersion + kind + name namespace } - message { - RawMessage - } - lastReconcileTime - isReady - checks + } + syncStatus { + action + error + lastSyncedAt + recordVersion + state + syncScheduledAt } updateTime - accountName } } pageInfo { - startCursor - hasNextPage endCursor + hasNextPage hasPreviousPage + startCursor } + totalCount } } @@ -248,6 +259,7 @@ query consoleListClusters($search: SearchCluster, $pagination: CursorPaginationI metadata { name annotations + generation } creationTime lastUpdatedBy { @@ -261,25 +273,28 @@ query consoleListClusters($search: SearchCluster, $pagination: CursorPaginationI userName } updateTime - syncStatus { - syncScheduledAt - lastSyncedAt - recordVersion - error - } status { - resources { - namespace - name - kind - apiVersion - } + checks + isReady + lastReadyGeneration + lastReconcileTime message { RawMessage } - lastReconcileTime - isReady - checks + resources { + apiVersion + kind + name + namespace + } + } + syncStatus { + action + error + lastSyncedAt + recordVersion + state + syncScheduledAt } recordVersion spec { @@ -640,12 +655,6 @@ query consoleListNodePools($clusterName: String!, $search: SearchNodepool, $pagi search: $search pagination: $pagination ) { - totalCount - pageInfo { - endCursor - hasNextPage - startCursor - } edges { cursor node { @@ -664,8 +673,11 @@ query consoleListNodePools($clusterName: String!, $search: SearchNodepool, $pagi } markedForDeletion metadata { + generation name + namespace } + recordVersion spec { aws { availabilityZone @@ -690,6 +702,7 @@ query consoleListNodePools($clusterName: String!, $search: SearchNodepool, $pagi instanceTypes } nodes + spotFleetTaggingRoleName } } cloudProvider @@ -712,9 +725,24 @@ query consoleListNodePools($clusterName: String!, $search: SearchNodepool, $pagi namespace } } + syncStatus { + action + error + lastSyncedAt + recordVersion + state + syncScheduledAt + } updateTime } } + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + totalCount } } @@ -782,6 +810,10 @@ mutation consoleUpdateEnvironment($projectName: String!, $env: EnvironmentIn!) { } } +mutation consoleDeleteEnvironment($projectName: String!, $envName: String!) { + core_deleteEnvironment(projectName: $projectName, envName: $envName) +} + query consoleListEnvironments($projectName: String!, $search: SearchEnvironments, $pq: CursorPaginationIn) { core_listEnvironments(projectName: $projectName, search: $search, pq: $pq) { edges { @@ -801,17 +833,19 @@ query consoleListEnvironments($projectName: String!, $search: SearchEnvironments } markedForDeletion metadata { - annotations - creationTimestamp - deletionTimestamp generation - labels name namespace } projectName + recordVersion spec { projectName + routing { + mode + privateIngressClass + publicIngressClass + } targetNamespace } status { @@ -829,6 +863,14 @@ query consoleListEnvironments($projectName: String!, $search: SearchEnvironments namespace } } + syncStatus { + action + error + lastSyncedAt + recordVersion + state + syncScheduledAt + } updateTime } } @@ -842,6 +884,18 @@ query consoleListEnvironments($projectName: String!, $search: SearchEnvironments } } +mutation consoleCloneEnvironment($projectName: String!, $sourceEnvName: String!, $destinationEnvName: String!, $displayName: String!, $environmentRoutingMode: Github__com___kloudlite___operator___apis___crds___v1__EnvironmentRoutingMode!) { + core_cloneEnvironment( + projectName: $projectName + sourceEnvName: $sourceEnvName + destinationEnvName: $destinationEnvName + displayName: $displayName + environmentRoutingMode: $environmentRoutingMode + ) { + id + } +} + mutation consoleCreateApp($projectName: String!, $envName: String!, $app: AppIn!) { core_createApp(projectName: $projectName, envName: $envName, app: $app) { id @@ -854,12 +908,23 @@ mutation consoleUpdateApp($projectName: String!, $envName: String!, $app: AppIn! } } +mutation consoleInterceptApp($projectName: String!, $envName: String!, $appname: String!, $deviceName: String!, $intercept: Boolean!) { + core_interceptApp( + projectName: $projectName + envName: $envName + appname: $appname + deviceName: $deviceName + intercept: $intercept + ) +} + mutation consoleDeleteApp($projectName: String!, $envName: String!, $appName: String!) { core_deleteApp(projectName: $projectName, envName: $envName, appName: $appName) } query consoleGetApp($projectName: String!, $envName: String!, $name: String!) { core_getApp(projectName: $projectName, envName: $envName, name: $name) { + id createdBy { userEmail userId @@ -877,10 +942,6 @@ query consoleGetApp($projectName: String!, $envName: String!, $name: String!) { markedForDeletion metadata { annotations - creationTimestamp - deletionTimestamp - generation - labels name namespace } @@ -1014,7 +1075,6 @@ query consoleListApps($projectName: String!, $envName: String!, $search: SearchA displayName enabled environmentName - kind lastUpdatedBy { userEmail userId @@ -1022,47 +1082,133 @@ query consoleListApps($projectName: String!, $envName: String!, $search: SearchA } markedForDeletion metadata { - annotations - creationTimestamp - deletionTimestamp generation - labels name namespace } projectName + recordVersion spec { - displayName - freeze - } - status { - checks - isReady - lastReadyGeneration - lastReconcileTime - message { - RawMessage - } - resources { - apiVersion - kind - name - namespace - } - } - updateTime - } - } - pageInfo { - endCursor - hasNextPage - hasPreviousPage - startCursor - } - totalCount + containers { + args + command + env { + key + optional + refKey + refName + type + value + } + envFrom { + refName + type + } + image + imagePullPolicy + name + readinessProbe { + failureThreshold + initialDelay + interval + type + } + resourceCpu { + max + min + } + resourceMemory { + max + min + } + } + displayName + freeze + hpa { + enabled + maxReplicas + minReplicas + thresholdCpu + thresholdMemory + } + intercept { + enabled + toDevice + } + nodeSelector + region + replicas + serviceAccount + services { + name + port + targetPort + type + } + tolerations { + effect + key + operator + tolerationSeconds + value + } + } + status { + checks + isReady + lastReadyGeneration + lastReconcileTime + message { + RawMessage + } + resources { + apiVersion + kind + name + namespace + } + } + syncStatus { + action + error + lastSyncedAt + recordVersion + state + syncScheduledAt + } + updateTime + } + } + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + totalCount + } +} + +mutation consoleCreateRouter($projectName: String!, $envName: String!, $router: RouterIn!) { + core_createRouter(projectName: $projectName, envName: $envName, router: $router) { + id + } +} + +mutation consoleUpdateRouter($projectName: String!, $envName: String!, $router: RouterIn!) { + core_updateRouter(projectName: $projectName, envName: $envName, router: $router) { + id } } +mutation consoleDeleteRouter($projectName: String!, $envName: String!, $routerName: String!) { + core_deleteRouter( + projectName: $projectName + envName: $envName + routerName: $routerName + ) +} + query consoleListRouters($projectName: String!, $envName: String!, $search: SearchRouters, $pq: CursorPaginationIn) { core_listRouters( projectName: $projectName @@ -1089,16 +1235,14 @@ query consoleListRouters($projectName: String!, $envName: String!, $search: Sear } markedForDeletion metadata { - annotations - creationTimestamp - deletionTimestamp generation - labels name namespace } projectName + recordVersion spec { + domains routes { app lambda @@ -1122,6 +1266,14 @@ query consoleListRouters($projectName: String!, $envName: String!, $search: Sear namespace } } + syncStatus { + action + error + lastSyncedAt + recordVersion + state + syncScheduledAt + } updateTime } } @@ -1135,6 +1287,81 @@ query consoleListRouters($projectName: String!, $envName: String!, $search: Sear } } +query consoleGetRouter($projectName: String!, $envName: String!, $name: String!) { + core_getRouter(projectName: $projectName, envName: $envName, name: $name) { + createdBy { + userEmail + userId + userName + } + creationTime + displayName + enabled + environmentName + lastUpdatedBy { + userEmail + userId + userName + } + markedForDeletion + metadata { + name + namespace + } + projectName + spec { + backendProtocol + basicAuth { + enabled + secretName + username + } + cors { + allowCredentials + enabled + origins + } + domains + https { + clusterIssuer + enabled + forceRedirect + } + ingressClass + maxBodySizeInMB + rateLimit { + connections + enabled + rpm + rps + } + routes { + app + lambda + path + port + rewrite + } + } + status { + checks + isReady + lastReadyGeneration + lastReconcileTime + message { + RawMessage + } + resources { + apiVersion + kind + name + namespace + } + } + updateTime + } +} + mutation consoleUpdateConfig($projectName: String!, $envName: String!, $config: ConfigIn!) { core_updateConfig(projectName: $projectName, envName: $envName, config: $config) { id @@ -2098,75 +2325,559 @@ query consoleListPvcs($clusterName: String!, $search: SearchPersistentVolumeClai } } -query consoleListBuildRuns($repoName: String!, $search: SearchBuildRuns, $pq: CursorPaginationIn) { - cr_listBuildRuns(repoName: $repoName, search: $search, pq: $pq) { - edges { - cursor - node { - clusterName - creationTime - markedForDeletion - metadata { - annotations - creationTimestamp - deletionTimestamp - generation - labels - name - namespace - } - spec { - accountName - buildOptions { - buildArgs - buildContexts - contextDir - dockerfileContent - dockerfilePath - targetPlatforms - } - cacheKeyName - registry { - repo { - name - tags - } - } - resource { - cpu - memoryInMb - } - } - status { - checks - isReady - lastReadyGeneration - lastReconcileTime - message { - RawMessage - } - resources { - apiVersion - kind - name - namespace - } - } - updateTime - } - } - pageInfo { - endCursor - hasNextPage - hasPreviousPage - startCursor - } - totalCount - } -} - -query consoleGetBuildRun($repoName: String!, $buildRunName: String!) { - cr_getBuildRun(repoName: $repoName, buildRunName: $buildRunName) { +query consoleGetPv($clusterName: String!, $name: String!) { + infra_getPV(clusterName: $clusterName, name: $name) { + clusterName + createdBy { + userEmail + userId + userName + } + creationTime + displayName + lastUpdatedBy { + userEmail + userId + userName + } + markedForDeletion + metadata { + annotations + creationTimestamp + deletionTimestamp + generation + labels + name + namespace + } + recordVersion + spec { + accessModes + awsElasticBlockStore { + fsType + partition + readOnly + volumeID + } + azureDisk { + cachingMode + diskName + diskURI + fsType + kind + readOnly + } + azureFile { + readOnly + secretName + secretNamespace + shareName + } + capacity + cephfs { + monitors + path + readOnly + secretFile + secretRef { + name + namespace + } + user + } + cinder { + fsType + readOnly + volumeID + } + claimRef { + apiVersion + fieldPath + kind + name + namespace + resourceVersion + uid + } + csi { + controllerExpandSecretRef { + name + namespace + } + controllerPublishSecretRef { + name + namespace + } + driver + fsType + nodeExpandSecretRef { + name + namespace + } + nodePublishSecretRef { + name + namespace + } + nodeStageSecretRef { + name + namespace + } + readOnly + volumeAttributes + volumeHandle + } + fc { + fsType + lun + readOnly + targetWWNs + wwids + } + flexVolume { + driver + fsType + options + readOnly + } + flocker { + datasetName + datasetUUID + } + gcePersistentDisk { + fsType + partition + pdName + readOnly + } + glusterfs { + endpoints + endpointsNamespace + path + readOnly + } + hostPath { + path + type + } + iscsi { + chapAuthDiscovery + chapAuthSession + fsType + initiatorName + iqn + iscsiInterface + lun + portals + readOnly + targetPortal + } + local { + fsType + path + } + mountOptions + nfs { + path + readOnly + server + } + nodeAffinity { + required { + nodeSelectorTerms { + matchExpressions { + key + operator + values + } + matchFields { + key + operator + values + } + } + } + } + persistentVolumeReclaimPolicy + photonPersistentDisk { + fsType + pdID + } + portworxVolume { + fsType + readOnly + volumeID + } + quobyte { + group + readOnly + registry + tenant + user + volume + } + rbd { + fsType + image + keyring + monitors + pool + readOnly + user + } + scaleIO { + fsType + gateway + protectionDomain + readOnly + sslEnabled + storageMode + storagePool + system + volumeName + } + storageClassName + storageos { + fsType + readOnly + secretRef { + apiVersion + fieldPath + kind + name + namespace + resourceVersion + uid + } + volumeName + volumeNamespace + } + volumeMode + vsphereVolume { + fsType + storagePolicyID + storagePolicyName + volumePath + } + } + status { + lastPhaseTransitionTime + message + phase + reason + } + updateTime + } +} + +query consoleListPvs($clusterName: String!, $search: SearchPersistentVolumes, $pq: CursorPaginationIn) { + infra_listPVs(clusterName: $clusterName, search: $search, pq: $pq) { + edges { + cursor + node { + clusterName + createdBy { + userEmail + userId + userName + } + creationTime + displayName + lastUpdatedBy { + userEmail + userId + userName + } + markedForDeletion + metadata { + annotations + creationTimestamp + deletionTimestamp + generation + labels + name + namespace + } + recordVersion + spec { + accessModes + awsElasticBlockStore { + fsType + partition + readOnly + volumeID + } + azureDisk { + cachingMode + diskName + diskURI + fsType + kind + readOnly + } + azureFile { + readOnly + secretName + secretNamespace + shareName + } + capacity + cephfs { + monitors + path + readOnly + secretFile + secretRef { + name + namespace + } + user + } + cinder { + fsType + readOnly + volumeID + } + claimRef { + apiVersion + fieldPath + kind + name + namespace + resourceVersion + uid + } + csi { + controllerExpandSecretRef { + name + namespace + } + controllerPublishSecretRef { + name + namespace + } + driver + fsType + nodeExpandSecretRef { + name + namespace + } + nodePublishSecretRef { + name + namespace + } + nodeStageSecretRef { + name + namespace + } + readOnly + volumeAttributes + volumeHandle + } + fc { + fsType + lun + readOnly + targetWWNs + wwids + } + flexVolume { + driver + fsType + options + readOnly + } + flocker { + datasetName + datasetUUID + } + gcePersistentDisk { + fsType + partition + pdName + readOnly + } + glusterfs { + endpoints + endpointsNamespace + path + readOnly + } + hostPath { + path + type + } + iscsi { + chapAuthDiscovery + chapAuthSession + fsType + initiatorName + iqn + iscsiInterface + lun + portals + readOnly + targetPortal + } + local { + fsType + path + } + mountOptions + nfs { + path + readOnly + server + } + persistentVolumeReclaimPolicy + photonPersistentDisk { + fsType + pdID + } + portworxVolume { + fsType + readOnly + volumeID + } + quobyte { + group + readOnly + registry + tenant + user + volume + } + rbd { + fsType + image + keyring + monitors + pool + readOnly + user + } + scaleIO { + fsType + gateway + protectionDomain + readOnly + sslEnabled + storageMode + storagePool + system + volumeName + } + storageClassName + storageos { + fsType + readOnly + secretRef { + apiVersion + fieldPath + kind + name + namespace + resourceVersion + uid + } + volumeName + volumeNamespace + } + volumeMode + vsphereVolume { + fsType + storagePolicyID + storagePolicyName + volumePath + } + } + status { + lastPhaseTransitionTime + message + phase + reason + } + updateTime + } + } + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + totalCount + } +} + +query consoleListBuildRuns($repoName: String!, $search: SearchBuildRuns, $pq: CursorPaginationIn) { + cr_listBuildRuns(repoName: $repoName, search: $search, pq: $pq) { + edges { + cursor + node { + clusterName + creationTime + markedForDeletion + metadata { + annotations + creationTimestamp + deletionTimestamp + generation + labels + name + namespace + } + spec { + accountName + buildOptions { + buildArgs + buildContexts + contextDir + dockerfileContent + dockerfilePath + targetPlatforms + } + cacheKeyName + registry { + repo { + name + tags + } + } + resource { + cpu + memoryInMb + } + } + status { + checks + isReady + lastReadyGeneration + lastReconcileTime + message { + RawMessage + } + resources { + apiVersion + kind + name + namespace + } + } + updateTime + } + } + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + totalCount + } +} + +query consoleGetBuildRun($repoName: String!, $buildRunName: String!) { + cr_getBuildRun(repoName: $repoName, buildRunName: $buildRunName) { clusterName creationTime markedForDeletion @@ -2193,43 +2904,537 @@ query consoleGetBuildRun($repoName: String!, $buildRunName: String!) { registry { repo { name - tags + tags + } + } + resource { + cpu + memoryInMb + } + } + status { + checks + isReady + lastReadyGeneration + lastReconcileTime + message { + RawMessage + } + resources { + apiVersion + kind + name + namespace + } + } + updateTime + } +} + +query consoleGetClusterMSv($clusterName: String!, $name: String!) { + infra_getClusterManagedService(clusterName: $clusterName, name: $name) { + clusterName + displayName + metadata { + annotations + creationTimestamp + deletionTimestamp + generation + labels + name + namespace + } + spec { + msvcSpec { + serviceTemplate { + apiVersion + kind + spec + } + } + targetNamespace + } + } +} + +mutation consoleCreateClusterMSv($clusterName: String!, $service: ClusterManagedServiceIn!) { + infra_createClusterManagedService(clusterName: $clusterName, service: $service) { + id + } +} + +mutation consoleUpdateClusterMSv($clusterName: String!, $service: ClusterManagedServiceIn!) { + infra_updateClusterManagedService(clusterName: $clusterName, service: $service) { + id + } +} + +query consoleListClusterMSvs($clusterName: String!) { + infra_listClusterManagedServices(clusterName: $clusterName) { + edges { + cursor + node { + createdBy { + userEmail + userId + userName + } + creationTime + displayName + lastUpdatedBy { + userEmail + userId + userName + } + markedForDeletion + metadata { + name + } + spec { + msvcSpec { + serviceTemplate { + apiVersion + kind + spec + } + } + } + status { + checks + isReady + lastReadyGeneration + lastReconcileTime + message { + RawMessage + } + resources { + apiVersion + kind + name + namespace + } + } + updateTime + } + } + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + totalCount + } +} + +mutation consoleDeleteClusterMSv($clusterName: String!, $serviceName: String!) { + infra_deleteClusterManagedService( + clusterName: $clusterName + serviceName: $serviceName + ) +} + +query consoleGetProjectMSv($projectName: String!, $name: String!) { + core_getProjectManagedService(projectName: $projectName, name: $name) { + createdBy { + userEmail + userId + userName + } + creationTime + displayName + lastUpdatedBy { + userEmail + userId + userName + } + markedForDeletion + metadata { + annotations + creationTimestamp + deletionTimestamp + generation + labels + name + namespace + } + spec { + msvcSpec { + serviceTemplate { + apiVersion + kind + spec + } + } + targetNamespace + } + status { + checks + isReady + lastReadyGeneration + lastReconcileTime + message { + RawMessage + } + resources { + apiVersion + kind + name + namespace + } + } + updateTime + } +} + +mutation consoleCreateProjectMSv($projectName: String!, $pmsvc: ProjectManagedServiceIn!) { + core_createProjectManagedService(projectName: $projectName, pmsvc: $pmsvc) { + id + } +} + +mutation consoleUpdateProjectMSv($projectName: String!, $pmsvc: ProjectManagedServiceIn!) { + core_updateProjectManagedService(projectName: $projectName, pmsvc: $pmsvc) { + id + } +} + +query consoleListProjectMSvs($projectName: String!, $search: SearchProjectManagedService, $pq: CursorPaginationIn) { + core_listProjectManagedServices( + projectName: $projectName + search: $search + pq: $pq + ) { + edges { + cursor + node { + createdBy { + userEmail + userId + userName + } + creationTime + displayName + lastUpdatedBy { + userEmail + userId + userName + } + markedForDeletion + metadata { + annotations + creationTimestamp + deletionTimestamp + generation + labels + name + namespace + } + projectName + recordVersion + spec { + msvcSpec { + serviceTemplate { + apiVersion + kind + spec + } + } + targetNamespace + } + status { + checks + isReady + lastReadyGeneration + lastReconcileTime + message { + RawMessage + } + resources { + apiVersion + kind + name + namespace + } + } + syncStatus { + action + error + lastSyncedAt + recordVersion + state + syncScheduledAt } + updateTime } - resource { - cpu - memoryInMb + } + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + totalCount + } +} + +mutation consoleDeleteProjectMSv($projectName: String!, $pmsvcName: String!) { + core_deleteProjectManagedService( + projectName: $projectName + pmsvcName: $pmsvcName + ) +} + +query consoleGetMSvTemplate($category: String!, $name: String!) { + infra_getManagedServiceTemplate(category: $category, name: $name) { + active + apiVersion + description + displayName + fields { + defaultValue + inputType + label + max + min + name + required + unit + displayUnit + multiplier + } + kind + logoUrl + name + outputs { + description + label + name + } + resources { + apiVersion + description + displayName + kind + name + fields { + defaultValue + displayUnit + inputType + label + max + min + multiplier + name + required + unit } } - status { - checks - isReady - lastReadyGeneration - lastReconcileTime - message { - RawMessage + } +} + +query consoleListMSvTemplates { + infra_listManagedServiceTemplates { + category + displayName + items { + active + apiVersion + description + displayName + fields { + defaultValue + inputType + label + max + min + name + required + unit + displayUnit + multiplier + } + kind + logoUrl + name + outputs { + description + label + name } resources { apiVersion + description + displayName kind name - namespace + fields { + defaultValue + displayUnit + inputType + label + max + min + multiplier + name + required + unit + } + } + } + } +} + +query consoleGetManagedResource($projectName: String!, $envName: String!, $name: String!) { + core_getManagedResource( + projectName: $projectName + envName: $envName + name: $name + ) { + displayName + enabled + environmentName + markedForDeletion + metadata { + name + namespace + } + projectName + spec { + resourceTemplate { + apiVersion + kind + msvcRef { + apiVersion + kind + name + namespace + } + spec + } + } + updateTime + } +} + +mutation consoleCreateManagedResource($projectName: String!, $envName: String!, $mres: ManagedResourceIn!) { + core_createManagedResource( + projectName: $projectName + envName: $envName + mres: $mres + ) { + id + } +} + +mutation consoleUpdateManagedResource($projectName: String!, $envName: String!, $mres: ManagedResourceIn!) { + core_updateManagedResource( + projectName: $projectName + envName: $envName + mres: $mres + ) { + id + } +} + +query consoleListManagedResources($projectName: String!, $envName: String!, $search: SearchManagedResources, $pq: CursorPaginationIn) { + core_listManagedResources( + projectName: $projectName + envName: $envName + search: $search + pq: $pq + ) { + edges { + cursor + node { + createdBy { + userEmail + userId + userName + } + creationTime + displayName + enabled + environmentName + lastUpdatedBy { + userEmail + userId + userName + } + markedForDeletion + metadata { + generation + name + namespace + } + projectName + recordVersion + spec { + resourceTemplate { + apiVersion + kind + msvcRef { + apiVersion + kind + name + namespace + } + spec + } + } + status { + checks + isReady + lastReadyGeneration + lastReconcileTime + message { + RawMessage + } + resources { + apiVersion + kind + name + namespace + } + } + syncStatus { + action + error + lastSyncedAt + recordVersion + state + syncScheduledAt + } + updateTime } } - updateTime + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + totalCount } } -query consoleGetClusterMSv($clusterName: String!, $name: String!) { - infra_getClusterManagedService(clusterName: $clusterName, name: $name) { - displayName - creationTime +mutation consoleDeleteManagedResource($projectName: String!, $envName: String!, $mresName: String!) { + core_deleteManagedResource( + projectName: $projectName + envName: $envName + mresName: $mresName + ) +} + +query consoleGetHelmChart($clusterName: String!, $name: String!) { + infra_getHelmRelease(clusterName: $clusterName, name: $name) { createdBy { userEmail userId userName } - clusterName + creationTime + displayName lastUpdatedBy { userEmail userId @@ -2237,23 +3442,14 @@ query consoleGetClusterMSv($clusterName: String!, $name: String!) { } markedForDeletion metadata { - annotations - creationTimestamp - deletionTimestamp - generation - labels name namespace } spec { - msvcSpec { - serviceTemplate { - apiVersion - kind - spec - } - } - namespace + chartName + chartRepoURL + chartVersion + values } status { checks @@ -2263,6 +3459,8 @@ query consoleGetClusterMSv($clusterName: String!, $name: String!) { message { RawMessage } + releaseNotes + releaseStatus resources { apiVersion kind @@ -2274,23 +3472,16 @@ query consoleGetClusterMSv($clusterName: String!, $name: String!) { } } -mutation consoleCreateClusterMSv($clusterName: String!, $service: ClusterManagedServiceIn!) { - infra_createClusterManagedService(clusterName: $clusterName, service: $service) { - id - } -} - -mutation consoleUpdateClusterMSv($clusterName: String!, $service: ClusterManagedServiceIn!) { - infra_updateClusterManagedService(clusterName: $clusterName, service: $service) { - id - } -} - -query consoleListClusterMSvs($clusterName: String!) { - infra_listClusterManagedServices(clusterName: $clusterName) { +query consoleListHelmChart($clusterName: String!, $search: SearchHelmRelease, $pagination: CursorPaginationIn) { + infra_listHelmReleases( + clusterName: $clusterName + search: $search + pagination: $pagination + ) { edges { cursor node { + clusterName createdBy { userEmail userId @@ -2305,16 +3496,16 @@ query consoleListClusterMSvs($clusterName: String!) { } markedForDeletion metadata { + generation name + namespace } + recordVersion spec { - msvcSpec { - serviceTemplate { - apiVersion - kind - spec - } - } + chartName + chartRepoURL + chartVersion + values } status { checks @@ -2324,6 +3515,8 @@ query consoleListClusterMSvs($clusterName: String!) { message { RawMessage } + releaseNotes + releaseStatus resources { apiVersion kind @@ -2331,6 +3524,14 @@ query consoleListClusterMSvs($clusterName: String!) { namespace } } + syncStatus { + action + error + lastSyncedAt + recordVersion + state + syncScheduledAt + } updateTime } } @@ -2344,91 +3545,187 @@ query consoleListClusterMSvs($clusterName: String!) { } } -mutation consoleDeleteClusterMSv($clusterName: String!, $serviceName: String!) { - infra_deleteClusterManagedService( - clusterName: $clusterName - serviceName: $serviceName - ) +mutation consoleCreateHelmChart($clusterName: String!, $release: HelmReleaseIn!) { + infra_createHelmRelease(clusterName: $clusterName, release: $release) { + id + } } -query consoleGetMSvTemplate($category: String!, $name: String!) { - infra_getManagedServiceTemplate(category: $category, name: $name) { - active - apiVersion - description - displayName - fields { - defaultValue - inputType - label - max - min - name - required - unit - displayUnit - multiplier - } - kind - logoUrl - name - outputs { - description - label - name +mutation consoleUpdateHelmChart($clusterName: String!, $release: HelmReleaseIn!) { + infra_updateHelmRelease(clusterName: $clusterName, release: $release) { + id + } +} + +mutation consoleDeleteHelmChart($clusterName: String!, $releaseName: String!) { + infra_deleteHelmRelease(clusterName: $clusterName, releaseName: $releaseName) +} + +query consoleListNamespaces($clusterName: String!) { + infra_listNamespaces(clusterName: $clusterName) { + edges { + cursor + node { + accountName + apiVersion + clusterName + createdBy { + userEmail + userId + userName + } + creationTime + displayName + id + kind + lastUpdatedBy { + userEmail + userId + userName + } + markedForDeletion + metadata { + annotations + creationTimestamp + deletionTimestamp + generation + labels + name + namespace + } + recordVersion + spec { + finalizers + } + status { + conditions { + lastTransitionTime + message + reason + status + type + } + phase + } + syncStatus { + action + error + lastSyncedAt + recordVersion + state + syncScheduledAt + } + updateTime + } } - resources { - apiVersion - description - displayName - kind - name + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor } + totalCount } } -query consoleListMSvTemplates { - infra_listManagedServiceTemplates { - category - displayName - items { - active - apiVersion - description - displayName - fields { - defaultValue - inputType - label - max - min - name - required - unit - displayUnit - multiplier +mutation consoleCreateConsoleVpnDevice($vpnDevice: ConsoleVPNDeviceIn!) { + core_createVPNDevice(vpnDevice: $vpnDevice) { + id + } +} + +mutation consoleUpdateConsoleVpnDevice($vpnDevice: ConsoleVPNDeviceIn!) { + core_updateVPNDevice(vpnDevice: $vpnDevice) { + id + } +} + +query consoleListConsoleVpnDevices($search: CoreSearchVPNDevices, $pq: CursorPaginationIn) { + core_listVPNDevices(search: $search, pq: $pq) { + edges { + cursor + node { + createdBy { + userEmail + userId + userName + } + creationTime + displayName + environmentName + lastUpdatedBy { + userEmail + userId + userName + } + markedForDeletion + metadata { + generation + name + namespace + } + projectName + recordVersion + spec { + cnameRecords { + host + target + } + deviceNamespace + disabled + nodeSelector + ports { + port + targetPort + } + } + updateTime } - kind - logoUrl + } + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + totalCount + } +} + +query consoleGetConsoleVpnDevice($name: String!) { + core_getVPNDevice(name: $name) { + displayName + environmentName + metadata { name - outputs { - description - label - name + namespace + } + projectName + recordVersion + spec { + cnameRecords { + host + target } - resources { - apiVersion - description - displayName - kind - name + deviceNamespace + disabled + nodeSelector + ports { + port + targetPort } } + wireguardConfig { + encoding + value + } } } -query consoleGetHelmChart($clusterName: String!, $name: String!) { - infra_getHelmRelease(clusterName: $clusterName, name: $name) { +query consoleListConsoleVpnDevicesForUser { + core_listVPNDevicesForUser { + accountName + apiVersion createdBy { userEmail userId @@ -2436,6 +3733,9 @@ query consoleGetHelmChart($clusterName: String!, $name: String!) { } creationTime displayName + environmentName + id + kind lastUpdatedBy { userEmail userId @@ -2443,101 +3743,207 @@ query consoleGetHelmChart($clusterName: String!, $name: String!) { } markedForDeletion metadata { + annotations + creationTimestamp + deletionTimestamp + generation + labels name namespace } + projectName + recordVersion spec { - chartName - chartRepoURL - chartVersion - values + cnameRecords { + host + target + } + deviceNamespace + disabled + nodeSelector + ports { + port + targetPort + } + } + updateTime + wireguardConfig { + encoding + value + } + } +} + +mutation consoleDeleteConsoleVpnDevice($deviceName: String!) { + core_deleteVPNDevice(deviceName: $deviceName) +} + +query authCli_CoreCheckNameAvailability($resType: ConsoleResType!, $name: String!) { + core_checkNameAvailability(resType: $resType, name: $name) { + result + suggestedNames + } +} + +query authCli_listCoreDevices { + core_listVPNDevicesForUser { + displayName + environmentName + metadata { + name } + projectName status { - checks isReady - lastReadyGeneration - lastReconcileTime message { RawMessage } - releaseNotes - releaseStatus - resources { - apiVersion - kind - name - namespace + } + spec { + cnameRecords { + host + target + } + deviceNamespace + disabled + ports { + port + targetPort } } - updateTime } } -query consoleListHelmChart($clusterName: String!) { - infra_listHelmReleases(clusterName: $clusterName) { - totalCount +query authCli_getCoreDevice($name: String!) { + core_getVPNDevice(name: $name) { + displayName + metadata { + name + } + projectName + spec { + deviceNamespace + disabled + ports { + port + targetPort + } + } + wireguardConfig { + encoding + value + } + } +} + +mutation authCli_createCoreDevice($vpnDevice: ConsoleVPNDeviceIn!) { + core_createVPNDevice(vpnDevice: $vpnDevice) { + id + } +} + +mutation authCli_updateCoreDevicePorts($deviceName: String!, $ports: [PortIn!]!) { + core_updateVPNDevicePorts(deviceName: $deviceName, ports: $ports) +} + +query authCli_getMresKeys($projectName: String!, $envName: String!, $name: String!) { + core_getManagedResouceOutputKeys( + projectName: $projectName + envName: $envName + name: $name + ) +} + +query authCli_listMreses($projectName: String!, $envName: String!, $pq: CursorPaginationIn) { + core_listManagedResources(projectName: $projectName, envName: $envName, pq: $pq) { edges { node { - createdBy { - userEmail - userId - userName - } - creationTime displayName - lastUpdatedBy { - userEmail - userId - userName - } - markedForDeletion metadata { name namespace } - spec { - chartName - chartRepoURL - chartVersion - values - } - status { - checks - isReady - lastReadyGeneration - lastReconcileTime - message { - RawMessage - } - releaseNotes - releaseStatus - resources { - apiVersion - kind - name - namespace - } - } - updateTime } } } } -mutation consoleCreateHelmChart($clusterName: String!, $release: HelmReleaseIn!) { - infra_createHelmRelease(clusterName: $clusterName, release: $release) { - id +query authCli_getMresConfigsValues($keyrefs: [ManagedResourceKeyRefIn], $envName: String!, $projectName: String!) { + core_getManagedResouceOutputKeyValues( + keyrefs: $keyrefs + envName: $envName + projectName: $projectName + ) { + key + mresName + value } } -mutation consoleUpdateHelmChart($clusterName: String!, $release: HelmReleaseIn!) { - infra_updateHelmRelease(clusterName: $clusterName, release: $release) { - id +query authCli_infraCheckNameAvailability($resType: ResType!, $name: String!, $clusterName: String) { + infra_checkNameAvailability( + resType: $resType + name: $name + clusterName: $clusterName + ) { + result + suggestedNames } } -mutation consoleDeleteHelmChart($clusterName: String!, $releaseName: String!) { - infra_deleteHelmRelease(clusterName: $clusterName, releaseName: $releaseName) +mutation authCli_createDevice($clusterName: String!, $vpnDevice: VPNDeviceIn!) { + infra_createVPNDevice(clusterName: $clusterName, vpnDevice: $vpnDevice) { + metadata { + name + } + } +} + +query authCli_getConfigSecretMap($projectName: String!, $envName: String!, $configQueries: [ConfigKeyRefIn], $secretQueries: [SecretKeyRefIn!]) { + configs: core_getConfigValues( + projectName: $projectName + envName: $envName + queries: $configQueries + ) { + configName + key + value + } + secrets: core_getSecretValues( + projectName: $projectName + envName: $envName + queries: $secretQueries + ) { + key + secretName + value + } +} + +mutation authCli_interceptApp($projectName: String!, $envName: String!, $appname: String!, $deviceName: String!, $intercept: Boolean!) { + core_interceptApp( + projectName: $projectName + envName: $envName + appname: $appname + deviceName: $deviceName + intercept: $intercept + ) +} + +query authCli_getEnvironment($projectName: String!, $name: String!) { + core_getEnvironment(projectName: $projectName, name: $name) { + spec { + targetNamespace + } + } +} + +mutation authCli_updateDeviceNs($clusterName: String!, $deviceName: String!, $namespace: String!) { + infra_updateVPNDeviceNs( + clusterName: $clusterName + deviceName: $deviceName + namespace: $namespace + ) } mutation authCli_updateDevicePort($clusterName: String!, $deviceName: String!, $ports: [PortIn!]!) { @@ -2736,13 +4142,8 @@ query authCli_listConfigs($projectName: String!, $envName: String!) { } } -query authCli_listSecrets($projectName: String!, $envName: String!, $search: SearchSecrets, $pq: CursorPaginationIn) { - core_listSecrets( - projectName: $projectName - envName: $envName - search: $search - pq: $pq - ) { +query authCli_listSecrets($projectName: String!, $envName: String!, $pq: CursorPaginationIn) { + core_listSecrets(projectName: $projectName, envName: $envName, pq: $pq) { edges { cursor node { @@ -2783,22 +4184,18 @@ mutation authCli_updateDevice($clusterName: String!, $vpnDevice: VPNDeviceIn!) { } } -query authCli_listDevices($pq: CursorPaginationIn, $clusterName: String) { - infra_listVPNDevices(pq: $pq, clusterName: $clusterName) { +query authCli_listDevices($pq: CursorPaginationIn) { + infra_listVPNDevices(pq: $pq) { edges { node { displayName - markedForDeletion metadata { name - namespace } spec { - cnameRecords { - host - target - } deviceNamespace + disabled + nodeSelector ports { port targetPort @@ -2810,6 +4207,10 @@ query authCli_listDevices($pq: CursorPaginationIn, $clusterName: String) { RawMessage } } + wireguardConfig { + encoding + value + } } } } @@ -2848,8 +4249,8 @@ query authCli_getDevice($clusterName: String!, $name: String!) { } } -query authCli_listEnvironments($projectName: String!, $search: SearchEnvironments, $pq: CursorPaginationIn) { - core_listEnvironments(projectName: $projectName, search: $search, pq: $pq) { +query authCli_listEnvironments($projectName: String!, $pq: CursorPaginationIn) { + core_listEnvironments(projectName: $projectName, pq: $pq) { edges { cursor node { @@ -2881,8 +4282,8 @@ query authCli_listEnvironments($projectName: String!, $search: SearchEnvironment } } -query authCli_listProjects($clusterName: String, $pq: CursorPaginationIn) { - core_listProjects(clusterName: $clusterName, pq: $pq) { +query authCli_listProjects($pq: CursorPaginationIn) { + core_listProjects(pq: $pq) { edges { node { displayName diff --git a/lib/client/helpers/socket-context.tsx b/lib/client/helpers/socket-context.tsx index fbcb624f7..1ca6b9366 100644 --- a/lib/client/helpers/socket-context.tsx +++ b/lib/client/helpers/socket-context.tsx @@ -43,7 +43,7 @@ const createSocketContext = () => { const wsclient = new Promise((res, rej) => { try { // eslint-disable-next-line new-cap - const w = new sock.w3cwebsocket(socketUrl, '', '', {}); + const w = new sock.w3cwebsocket(`${socketUrl}/ws`, '', '', {}); w.onmessage = (msg) => { try { diff --git a/lib/client/hooks/use-unsaved-changes.tsx b/lib/client/hooks/use-unsaved-changes.tsx index 7822ae1cc..b411bbfe3 100644 --- a/lib/client/hooks/use-unsaved-changes.tsx +++ b/lib/client/hooks/use-unsaved-changes.tsx @@ -13,6 +13,7 @@ import { useState, } from 'react'; import { ChildrenProps } from '~/components/types'; +import Popup from '~/components/molecule/popup'; import { useReload } from '../helpers/reloader'; const UnsavedChanges = createContext<{ @@ -22,6 +23,11 @@ const UnsavedChanges = createContext<{ proceed: (() => void) | undefined; reset: (() => void) | undefined; resetAndReload: () => void; + setIgnorePaths: (path: string[]) => void; + performAction: string; + setPerformAction: (action: string) => void; + loading: boolean; + setLoading: (loading: boolean) => void; }>({ hasChanges: false, setHasChanges() {}, @@ -29,12 +35,27 @@ const UnsavedChanges = createContext<{ proceed() {}, reset() {}, resetAndReload() {}, + setIgnorePaths() {}, + performAction: '', + setPerformAction() {}, + loading: false, + setLoading() {}, }); export const UnsavedChangesProvider = ({ children }: ChildrenProps) => { const [hasChanges, setHasChanges] = useState(false); const [reload, setReload] = useState(false); - const { state, proceed, reset } = unstable_useBlocker(hasChanges); + const [ignorePaths, setIgnorePaths] = useState([]); + const [performAction, setPerformAction] = useState(''); + const [loading, setLoading] = useState(false); + const location = useLocation(); + const { state, proceed, reset } = unstable_useBlocker(({ nextLocation }) => { + if (hasChanges && !ignorePaths.includes(nextLocation.pathname)) { + return true; + } + return false; + }); + useBeforeUnload( useCallback( (e) => { @@ -46,10 +67,17 @@ export const UnsavedChangesProvider = ({ children }: ChildrenProps) => { [hasChanges] ) ); - const location = useLocation(); useEffect(() => { - setHasChanges(false); + if ( + !( + ignorePaths && + ignorePaths.length > 0 && + ignorePaths.includes(location.pathname) + ) + ) { + setHasChanges(false); + } }, [location]); const refresh = useReload(); @@ -76,11 +104,48 @@ export const UnsavedChangesProvider = ({ children }: ChildrenProps) => { proceed, reset, resetAndReload, + setIgnorePaths, + setPerformAction, + performAction, + loading, + setLoading, }), - [hasChanges, setHasChanges, state] + [ + hasChanges, + setHasChanges, + state, + ignorePaths, + performAction, + setPerformAction, + loading, + setLoading, + ] )} > {children} + { + reset?.(); + }} + > + Unsaved changes + + Are you sure you want to discard the changes? + + + reset?.()} + /> + proceed?.()} + /> + + ); }; diff --git a/lib/configs/base-url.cjs b/lib/configs/base-url.cjs index 84d3f85f6..eb8923696 100644 --- a/lib/configs/base-url.cjs +++ b/lib/configs/base-url.cjs @@ -86,7 +86,7 @@ const baseUrls = () => { cookieDomain, baseUrl: bUrl, githubAppName: 'kloudlite-dev', - socketUrl: `wss://socket${postFix}.${bUrl}/ws`, + socketUrl: `wss://websocket.${bUrl}`, }; }; diff --git a/lib/server/helpers/execute-query-with-context.ts b/lib/server/helpers/execute-query-with-context.ts index c95d26047..91f24e7da 100644 --- a/lib/server/helpers/execute-query-with-context.ts +++ b/lib/server/helpers/execute-query-with-context.ts @@ -113,15 +113,19 @@ export const ExecuteQueryWithContext = ( } catch (err) { if ((err as AxiosError).response) { console.trace('ErrorIn:', apiName, (err as Error).name); + return (err as AxiosError).response?.data; } console.trace('ErrorIn:', apiName, (err as Error).message); return { + data: null, errors: [ { message: (err as Error).message, + name: (err as Error).name, + stack: (err as Error).stack, }, ], }; diff --git a/package.json b/package.json index 416f599ad..9e44a406a 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "kloudlite.github.io", + "name": "kloudlite.github.io", "private": true, "version": "0.0.0", "homepage": "https://kloudlite.github.io", @@ -26,7 +26,7 @@ "@codemirror/legacy-modes": "^6.3.3", "@jengaicons/react": "^1.3.0", "@mdx-js/react": "^2.3.0", - "@oshq/react-select": "file:/Users/bikash/Documents/bikash/select/oshq-react-select-1.2.0.tgz", + "@oshq/react-select": "^1.3.0", "@radix-ui/primitive": "^1.0.1", "@radix-ui/react-alert-dialog": "1.0.4", "@radix-ui/react-checkbox": "^1.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 69cf50537..a6d305692 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,8 +18,8 @@ dependencies: specifier: ^2.3.0 version: 2.3.0(react@18.2.0) '@oshq/react-select': - specifier: file:/Users/bikash/Documents/bikash/select/oshq-react-select-1.2.0.tgz - version: file:../../bikash/select/oshq-react-select-1.2.0.tgz(@radix-ui/react-portal@1.0.4)(classnames@2.5.1)(framer-motion@10.17.8)(rc-virtual-list@3.11.3)(react@18.2.0) + specifier: ^1.3.0 + version: 1.3.0(@radix-ui/react-portal@1.0.4)(classnames@2.5.1)(framer-motion@10.17.8)(rc-virtual-list@3.11.3)(react@18.2.0) '@radix-ui/primitive': specifier: ^1.0.1 version: 1.0.1 @@ -2607,6 +2607,22 @@ packages: json-parse-even-better-errors: 2.3.1 dev: true + /@oshq/react-select@1.3.0(@radix-ui/react-portal@1.0.4)(classnames@2.5.1)(framer-motion@10.17.8)(rc-virtual-list@3.11.3)(react@18.2.0): + resolution: {integrity: sha512-Q9qZugOhG4Q8Cpt4U5lk4GuA8m2l4YED+fciVRVllsWy8H9If8zX6vo+pbSJhzd4YE5KwmjeKmt0Q7jHLS7Nzw==} + peerDependencies: + '@radix-ui/react-portal': ^1.0.4 + classnames: ^2.3.2 + framer-motion: ^10.16.4 + rc-virtual-list: ^3.11.2 + react: ^18.2.0 || ^17.0.0 || ^16.0.0 + dependencies: + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.46)(react-dom@18.2.0)(react@18.2.0) + classnames: 2.5.1 + framer-motion: 10.17.8(react-dom@18.2.0)(react@18.2.0) + rc-virtual-list: 3.11.3(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + dev: false + /@pkgjs/parseargs@0.11.0: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -10964,22 +10980,3 @@ packages: /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} dev: true - - file:../../bikash/select/oshq-react-select-1.2.0.tgz(@radix-ui/react-portal@1.0.4)(classnames@2.5.1)(framer-motion@10.17.8)(rc-virtual-list@3.11.3)(react@18.2.0): - resolution: {integrity: sha512-Vc4MiN1HyjcjqTHEmidK/wc9fTmsdA1d+vtLvzssmn0JPvQIDmvOYrLLKmv2jSN62cptW64gOjvjD2kqRonSXw==, tarball: file:../../bikash/select/oshq-react-select-1.2.0.tgz} - id: file:../../bikash/select/oshq-react-select-1.2.0.tgz - name: '@oshq/react-select' - version: 1.2.0 - peerDependencies: - '@radix-ui/react-portal': ^1.0.4 - classnames: ^2.3.2 - framer-motion: ^10.16.4 - rc-virtual-list: ^3.11.2 - react: ^18.2.0 || ^17.0.0 || ^16.0.0 - dependencies: - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.46)(react-dom@18.2.0)(react@18.2.0) - classnames: 2.5.1 - framer-motion: 10.17.8(react-dom@18.2.0)(react@18.2.0) - rc-virtual-list: 3.11.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - dev: false diff --git a/src/apps/auth/routes/_main+/api.tsx b/src/apps/auth/routes/_main+/api.tsx index 6fe2e80d2..1ef9c8d0b 100644 --- a/src/apps/auth/routes/_main+/api.tsx +++ b/src/apps/auth/routes/_main+/api.tsx @@ -8,6 +8,12 @@ export const action = async (ctx: IRemixCtx) => { const res = await RootAPIAction(GQLServerHandler)(ctx); return res; } catch (err) { - return json({ errors: [(err as Error).message] }, 500); + return json({ + errors: [ + { + message: (err as Error).message, + }, + ], + }); } }; diff --git a/src/apps/auth/routes/test/_index.tsx b/src/apps/auth/routes/test/_index.tsx deleted file mode 100644 index cc118e085..000000000 --- a/src/apps/auth/routes/test/_index.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import CodeMirrorClient from '~/root/lib/client/components/editor-client'; - -const Editor = () => { - return ( -
-

Editor

- -
- ); -}; - -export default Editor; diff --git a/src/apps/auth/server/gql/cli-queries.ts b/src/apps/auth/server/gql/cli-queries.ts index 34772ae44..f56cee3cb 100644 --- a/src/apps/auth/server/gql/cli-queries.ts +++ b/src/apps/auth/server/gql/cli-queries.ts @@ -1,22 +1,224 @@ /* eslint-disable camelcase */ import gql from 'graphql-tag'; import { IExecutor } from '~/root/lib/server/helpers/execute-query-with-context'; -import { - AuthCli_CreateRemoteLoginMutationVariables, - AuthCli_CreateRemoteLoginMutation, - AuthCli_GetRemoteLoginQueryVariables, - AuthCli_GetRemoteLoginQuery, - AuthCli_GetCurrentUserQuery, - AuthCli_GetCurrentUserQueryVariables, - AuthCli_ListAccountsQuery, - AuthCli_ListAccountsQueryVariables, - AuthCli_ListClustersQuery, - AuthCli_ListClustersQueryVariables, - AuthCli_GetKubeConfigQuery, - AuthCli_GetKubeConfigQueryVariables, -} from '~/root/src/generated/gql/server'; +import { infraQueries } from './queries/infra-queries'; export const cliQueries = (executor: IExecutor) => ({ + ...infraQueries(executor), + cli_getMresKeys: executor( + gql` + query Core_getManagedResouceOutputKeyValues( + $projectName: String! + $envName: String! + $name: String! + ) { + core_getManagedResouceOutputKeys( + projectName: $projectName + envName: $envName + name: $name + ) + } + `, + { + transformer: (data: any) => data.core_getManagedResouceOutputKeys, + vars: (_: any) => {}, + } + ), + + cli_listMreses: executor( + gql` + query Core_listManagedResources( + $projectName: String! + $envName: String! + $pq: CursorPaginationIn + ) { + core_listManagedResources( + projectName: $projectName + envName: $envName + pq: $pq + ) { + edges { + node { + displayName + metadata { + name + namespace + } + } + } + } + } + `, + { + transformer: (data: any) => data.core_listManagedResources, + vars: (_: any) => {}, + } + ), + + cli_getMresConfigsValues: executor( + gql` + query Core_getManagedResouceOutputKeyValues( + $keyrefs: [ManagedResourceKeyRefIn] + $envName: String! + $projectName: String! + ) { + core_getManagedResouceOutputKeyValues( + keyrefs: $keyrefs + envName: $envName + projectName: $projectName + ) { + key + mresName + value + } + } + `, + { + transformer: (data: any) => data, + vars: (_: any) => {}, + } + ), + + cli_infraCheckNameAvailability: executor( + gql` + query Infra_checkNameAvailability( + $resType: ResType! + $name: String! + $clusterName: String + ) { + infra_checkNameAvailability( + resType: $resType + name: $name + clusterName: $clusterName + ) { + result + suggestedNames + } + } + `, + { + transformer: (data: any) => data.infra_checkNameAvailability, + vars: (_: any) => {}, + } + ), + cli_createDevice: executor( + gql` + mutation Infra_createVPNDevice( + $clusterName: String! + $vpnDevice: VPNDeviceIn! + ) { + infra_createVPNDevice( + clusterName: $clusterName + vpnDevice: $vpnDevice + ) { + metadata { + name + } + } + } + `, + { + transformer: (data: any) => data.infra_createVPNDevice, + vars: (_: any) => {}, + } + ), + + cli_getConfigSecretMap: executor( + gql` + query Core_getConfigValues( + $projectName: String! + $envName: String! + $configQueries: [ConfigKeyRefIn] + $secretQueries: [SecretKeyRefIn!] + ) { + configs: core_getConfigValues( + projectName: $projectName + envName: $envName + queries: $configQueries + ) { + configName + key + value + } + secrets: core_getSecretValues( + projectName: $projectName + envName: $envName + queries: $secretQueries + ) { + key + secretName + value + } + } + `, + { + transformer: (data: any) => { + return { + configs: data.configs, + secrets: data.secrets, + }; + }, + + vars: (_: any) => {}, + } + ), + cli_interceptApp: executor( + gql` + mutation Core_interceptApp( + $projectName: String! + $envName: String! + $appname: String! + $deviceName: String! + $intercept: Boolean! + ) { + core_interceptApp( + projectName: $projectName + envName: $envName + appname: $appname + deviceName: $deviceName + intercept: $intercept + ) + } + `, + { + transformer: (data: any) => data.core_interceptApp, + vars: (_: any) => {}, + } + ), + cli_getEnvironment: executor( + gql` + query Core_getEnvironment($projectName: String!, $name: String!) { + core_getEnvironment(projectName: $projectName, name: $name) { + spec { + targetNamespace + } + } + } + `, + { + transformer: (data: any) => data.core_getEnvironment, + vars: (_: any) => {}, + } + ), + cli_updateDeviceNs: executor( + gql` + mutation Infra_updateVPNDeviceNs( + $clusterName: String! + $deviceName: String! + $namespace: String! + ) { + infra_updateVPNDeviceNs( + clusterName: $clusterName + deviceName: $deviceName + namespace: $namespace + ) + } + `, + { + transformer: (data: any) => data.infra_updateVPNDeviceNs, + vars: (_: any) => {}, + } + ), cli_updateDevicePort: executor( gql` mutation Mutation( @@ -33,7 +235,7 @@ export const cliQueries = (executor: IExecutor) => ({ `, { transformer: (data: any) => data.infra_updateVPNDevicePorts, - vars: (_: any) => { }, + vars: (_: any) => {}, } ), cli_getSecret: executor( @@ -59,7 +261,7 @@ export const cliQueries = (executor: IExecutor) => ({ `, { transformer: (data: any) => data.core_getSecret, - vars: (_: any) => { }, + vars: (_: any) => {}, } ), cli_getConfig: executor( @@ -85,7 +287,7 @@ export const cliQueries = (executor: IExecutor) => ({ `, { transformer: (data: any) => data.core_getConfig, - vars: (_: any) => { }, + vars: (_: any) => {}, } ), @@ -243,7 +445,7 @@ export const cliQueries = (executor: IExecutor) => ({ `, { transformer: (data: any) => data.core_listApps, - vars: (_: any) => { }, + vars: (_: any) => {}, } ), cli_listConfigs: executor( @@ -266,7 +468,7 @@ export const cliQueries = (executor: IExecutor) => ({ `, { transformer: (data: any) => data.core_listConfigs, - vars: (_: any) => { }, + vars: (_: any) => {}, } ), cli_listSecrets: executor( @@ -274,13 +476,11 @@ export const cliQueries = (executor: IExecutor) => ({ query Core_listSecrets( $projectName: String! $envName: String! - $search: SearchSecrets $pq: CursorPaginationIn ) { core_listSecrets( projectName: $projectName envName: $envName - search: $search pq: $pq ) { edges { @@ -300,7 +500,7 @@ export const cliQueries = (executor: IExecutor) => ({ `, { transformer: (data: any) => data.core_listSecrets, - vars: (_: any) => { }, + vars: (_: any) => {}, } ), cli_updateDevice: executor( @@ -335,30 +535,23 @@ export const cliQueries = (executor: IExecutor) => ({ `, { transformer: (data: any) => data.infra_updateVPNDevice, - vars: (_: any) => { }, + vars: (_: any) => {}, } ), cli_listDevices: executor( gql` - query Infra_listVPNDevices( - $pq: CursorPaginationIn - $clusterName: String - ) { - infra_listVPNDevices(pq: $pq, clusterName: $clusterName) { + query Infra_listVPNDevices($pq: CursorPaginationIn) { + infra_listVPNDevices(pq: $pq) { edges { node { displayName - markedForDeletion metadata { name - namespace } spec { - cnameRecords { - host - target - } deviceNamespace + disabled + nodeSelector ports { port targetPort @@ -370,6 +563,10 @@ export const cliQueries = (executor: IExecutor) => ({ RawMessage } } + wireguardConfig { + encoding + value + } } } } @@ -377,7 +574,7 @@ export const cliQueries = (executor: IExecutor) => ({ `, { transformer: (data: any) => data.infra_listVPNDevices, - vars: (_: any) => { }, + vars: (_: any) => {}, } ), @@ -418,7 +615,7 @@ export const cliQueries = (executor: IExecutor) => ({ `, { transformer: (data: any) => data.infra_getVPNDevice, - vars: (_: any) => { }, + vars: (_: any) => {}, } ), @@ -426,14 +623,9 @@ export const cliQueries = (executor: IExecutor) => ({ gql` query Core_listEnvironments( $projectName: String! - $search: SearchEnvironments $pq: CursorPaginationIn ) { - core_listEnvironments( - projectName: $projectName - search: $search - pq: $pq - ) { + core_listEnvironments(projectName: $projectName, pq: $pq) { edges { cursor node { @@ -467,14 +659,14 @@ export const cliQueries = (executor: IExecutor) => ({ `, { transformer: (data: any) => data.core_listEnvironments, - vars: (_: any) => { }, + vars: (_: any) => {}, } ), cli_listProjects: executor( gql` - query Core_listProjects($clusterName: String, $pq: CursorPaginationIn) { - core_listProjects(clusterName: $clusterName, pq: $pq) { + query Core_listProjects($pq: CursorPaginationIn) { + core_listProjects(pq: $pq) { edges { node { displayName @@ -496,7 +688,7 @@ export const cliQueries = (executor: IExecutor) => ({ `, { transformer: (data: any) => data.core_listProjects, - vars: (_: any) => { }, + vars: (_: any) => {}, } ), @@ -515,8 +707,8 @@ export const cliQueries = (executor: IExecutor) => ({ } `, { - transformer: (data: AuthCli_GetKubeConfigQuery) => data.infra_getCluster, - vars(_: AuthCli_GetKubeConfigQueryVariables) { }, + transformer: (data: any) => data.infra_getCluster, + vars: (_: any) => {}, } ), cli_listClusters: executor( @@ -538,10 +730,8 @@ export const cliQueries = (executor: IExecutor) => ({ } `, { - transformer(data: AuthCli_ListClustersQuery) { - return data.infra_listClusters; - }, - vars(_: AuthCli_ListClustersQueryVariables) { }, + transformer: (data: any) => data.infra_listClusters, + vars: (_: any) => {}, } ), cli_listAccounts: executor( @@ -556,10 +746,8 @@ export const cliQueries = (executor: IExecutor) => ({ } `, { - transformer(data: AuthCli_ListAccountsQuery) { - return data.accounts_listAccounts; - }, - vars(_: AuthCli_ListAccountsQueryVariables) { }, + transformer: (data: any) => data.accounts_listAccounts, + vars: (_: any) => {}, } ), cli_getCurrentUser: executor( @@ -573,10 +761,8 @@ export const cliQueries = (executor: IExecutor) => ({ } `, { - transformer(data: AuthCli_GetCurrentUserQuery) { - return data.auth_me; - }, - vars(_: AuthCli_GetCurrentUserQueryVariables) { }, + transformer: (data: any) => data.auth_me, + vars: (_: any) => {}, } ), @@ -587,9 +773,8 @@ export const cliQueries = (executor: IExecutor) => ({ } `, { - transformer: (data: AuthCli_CreateRemoteLoginMutation) => - data.auth_createRemoteLogin, - vars(_: AuthCli_CreateRemoteLoginMutationVariables) { }, + transformer: (data: any) => data.auth_createRemoteLogin, + vars: (_: any) => {}, } ), @@ -603,9 +788,8 @@ export const cliQueries = (executor: IExecutor) => ({ } `, { - transformer: (data: AuthCli_GetRemoteLoginQuery) => - data.auth_getRemoteLogin, - vars(_: AuthCli_GetRemoteLoginQueryVariables) { }, + transformer: (data: any) => data.auth_getRemoteLogin, + vars: (_: any) => {}, } ), }); diff --git a/src/apps/auth/server/gql/queries/infra-queries.ts b/src/apps/auth/server/gql/queries/infra-queries.ts new file mode 100644 index 000000000..9942877e1 --- /dev/null +++ b/src/apps/auth/server/gql/queries/infra-queries.ts @@ -0,0 +1,112 @@ +import gql from 'graphql-tag'; +import { IExecutor } from '~/root/lib/server/helpers/execute-query-with-context'; + +export const infraQueries = (executor: IExecutor) => ({ + cli_CoreCheckNameAvailability: executor( + gql` + query Core_checkNameAvailability( + $resType: ConsoleResType! + $name: String! + ) { + core_checkNameAvailability(resType: $resType, name: $name) { + result + suggestedNames + } + } + `, + { + transformer: (data: any) => data.core_checkNameAvailability, + vars: (_: any) => {}, + } + ), + cli_listCoreDevices: executor( + gql` + query Core_listVPNDevicesForUser { + core_listVPNDevicesForUser { + displayName + environmentName + metadata { + name + } + projectName + status { + isReady + message { + RawMessage + } + } + spec { + cnameRecords { + host + target + } + deviceNamespace + disabled + ports { + port + targetPort + } + } + } + } + `, + { + transformer: (data: any) => data.core_listVPNDevicesForUser, + vars: (_: any) => {}, + } + ), + cli_getCoreDevice: executor( + gql` + query Core_getVPNDevice($name: String!) { + core_getVPNDevice(name: $name) { + displayName + metadata { + name + } + projectName + spec { + deviceNamespace + disabled + ports { + port + targetPort + } + } + wireguardConfig { + encoding + value + } + } + } + `, + { + transformer: (data: any) => data.core_getVPNDevice, + vars: (_: any) => {}, + } + ), + cli_createCoreDevice: executor( + gql` + mutation Core_createVPNDevice($vpnDevice: ConsoleVPNDeviceIn!) { + core_createVPNDevice(vpnDevice: $vpnDevice) { + id + } + } + `, + { + transformer: (data: any) => data.core_createVPNDevice, + vars: (_: any) => {}, + } + ), + + cli_updateCoreDevicePorts: executor( + gql` + mutation Mutation($deviceName: String!, $ports: [PortIn!]!) { + core_updateVPNDevicePorts(deviceName: $deviceName, ports: $ports) + } + `, + { + transformer: (data: any) => data.core_updateVPNDevicePorts, + vars: (_: any) => {}, + } + ), +}); diff --git a/src/apps/console/components/console-list-components.tsx b/src/apps/console/components/console-list-components.tsx index b00e0e80a..464f75cb7 100644 --- a/src/apps/console/components/console-list-components.tsx +++ b/src/apps/console/components/console-list-components.tsx @@ -1,4 +1,5 @@ import { ReactNode } from 'react'; +import Tooltip from '~/components/atoms/tooltip'; import { cn } from '~/components/utils'; interface IBase { @@ -8,6 +9,40 @@ interface IBase { const BaseStyle = 'flex flex-row items-center gap-xl'; +const ListSecondary = ({ + className, + action, + title, + avatar, + subtitle, +}: { + className?: string; + action?: ReactNode; + title?: ReactNode; + subtitle?: ReactNode; + avatar?: ReactNode; +}) => { + return ( +
+
+ {avatar} +
+ {title && ( +
+ {title} +
+ )} + + {subtitle && ( +
{subtitle}
+ )} +
+
+ {action} +
+ ); +}; + const ListBody = ({ data, className = '', @@ -68,12 +103,17 @@ const ListTitle = ({ }) => { return (
-
+
{avatar}
{title && (
- {title} + {title}
} + > + {title} +
)} @@ -89,4 +129,19 @@ const ListTitle = ({ ); }; -export { ListBody, ListItem, ListTitle }; +const listFlex = ({ key }: { key: string }) => ({ + key, + className: 'basis-full', + render: () =>
, +}); + +const listTitleClass = 'w-[180px] min-w-[180px] max-w-[180px] mr-2xl'; + +export { + ListBody, + ListItem, + ListTitle, + ListSecondary, + listFlex, + listTitleClass, +}; diff --git a/src/apps/console/components/id-selector.tsx b/src/apps/console/components/id-selector.tsx index 2a085b77f..015ba7aba 100644 --- a/src/apps/console/components/id-selector.tsx +++ b/src/apps/console/components/id-selector.tsx @@ -15,6 +15,7 @@ import { ensureClusterClientSide, } from '../server/utils/auth-utils'; import { IEnvironmentContext } from '../routes/_main+/$account+/$project+/$environment+/_layout'; +import { parseName } from '../server/r-utils/common'; interface IidSelector { name: string; @@ -107,13 +108,9 @@ export const IdSelector = ({ resType, name: `${name}`, // eslint-disable-next-line no-nested-ternary - ...(resType === 'environment' + ...(resType === 'environment' || resType === 'app' ? { - namespace: project.spec?.targetNamespace, - } - : environment - ? { - namespace: environment.spec?.targetNamespace, + projectName: parseName(project), } : {}), ...(resType === 'nodepool' || resType === 'vpn_device' diff --git a/src/apps/console/components/logger.tsx b/src/apps/console/components/logger.tsx index c5474ccde..ef363ace0 100644 --- a/src/apps/console/components/logger.tsx +++ b/src/apps/console/components/logger.tsx @@ -2,17 +2,10 @@ /* eslint-disable no-nested-ternary */ import { ArrowsIn, ArrowsOut, List } from '@jengaicons/react'; import Anser from 'anser'; -import axios from 'axios'; import classNames from 'classnames'; import Fuse from 'fuse.js'; import hljs from 'highlight.js'; -import React, { - ReactNode, - useCallback, - useEffect, - useRef, - useState, -} from 'react'; +import React, { ReactNode, useEffect, useRef, useState } from 'react'; import { ViewportList } from 'react-viewport-list'; import * as sock from 'websocket'; import { dayjs } from '~/components/molecule/dayjs'; @@ -21,6 +14,8 @@ import { useSearch, } from '~/root/lib/client/helpers/search-filter'; import useClass from '~/root/lib/client/hooks/use-class'; +import logger from '~/root/lib/client/helpers/log'; +import { socketUrl } from '~/root/lib/configs/base-url.cjs'; import generateColor from './color-generator'; import Pulsable from './pulsable'; import { logsMockData } from '../dummy/data'; @@ -28,13 +23,14 @@ import { logsMockData } from '../dummy/data'; const hoverClass = `hover:bg-[#ddd]`; const hoverClassDark = `hover:bg-[#333]`; -type ILog = { message: string; timestamp: string }; -type ILogWithPodName = ILog & { pod_name: string; lineNumber: number }; - -type ISocketMessage = { +type ILog = { pod_name: string; - logs: ILog[]; + message: string; + timestamp: string; }; +type ILogWithLineNumber = ILog & { lineNumber: number }; + +type ISocketMessage = ILog; const padLeadingZeros = (num: number, size: number) => { let s = `${num}`; @@ -51,6 +47,21 @@ interface IHighlightIt { enableHL?: boolean; } +const getHashId = (str: string) => { + let hash = 0; + let i; + let chr; + if (str.length === 0) return hash; + for (i = 0; i < str.length; i += 1) { + chr = str.charCodeAt(i); + // eslint-disable-next-line no-bitwise + hash = (hash << 5) - hash + chr; + // eslint-disable-next-line no-bitwise + hash |= 0; // Convert to 32bit integer + } + return hash; +}; + const HighlightIt = ({ language, inlineData = '', @@ -77,8 +88,6 @@ const HighlightIt = ({ // @ts-ignore ref.current.innerHTML = Anser.ansiToHtml(inlineData); } - - // @ts-ignore } })(); }, [inlineData, language]); @@ -278,7 +287,7 @@ interface ILogLine { language: string; lines: number; hideLines?: boolean; - log: ILogWithPodName & { + log: ILogWithLineNumber & { searchInf?: ISearchInfProps['searchInf']; }; dark: boolean; @@ -381,38 +390,9 @@ const LogBlock = ({ }: ILogBlock) => { const [searchText, setSearchText] = useState(''); - const temp: { res: ILogWithPodName[]; id: number } = { - res: [], - id: 1, - }; - - const flatLogs = useCallback( - () => - data.reduce((acc, curr) => { - let { id } = acc; - const tres = [ - ...acc.res, - ...curr.logs.map((log, index) => { - id = acc.id + index; - return { - ...log, - pod_name: curr.pod_name, - lineNumber: id, - }; - }), - ]; - - return { - id, - res: tres, - }; - }, temp).res, - [data] - )(); - const searchResult = useSearch( { - data: flatLogs, + data, keys: ['message'], searchText, threshold, @@ -493,18 +473,23 @@ const LogBlock = ({ style={{ lineHeight: `${fontSize * 1.5}px` }} ref={ref} > - - {(log) => { + + {(log, index) => { return ( @@ -518,8 +503,15 @@ const LogBlock = ({ ); }; -interface IHighlightJsLog { - websocket?: boolean; +interface IuseLog { + url?: string; + account: string; + cluster: string; + trackingId: string; +} + +export interface IHighlightJsLog { + websocket: IuseLog; follow?: boolean; url?: string; text?: string; @@ -540,10 +532,159 @@ interface IHighlightJsLog { dark?: boolean; } -const HighlightJsLog = ({ - websocket = false, +const useSocketLogs = ({ url, account, cluster, trackingId }: IuseLog) => { + const [logs, setLogs] = useState([]); + const [error, setError] = useState(''); + const [isLoading, setIsLoading] = useState(true); + + let wsclient: Promise; + + const [socState, setSocState] = useState(null); + + if (typeof window !== 'undefined') { + try { + wsclient = new Promise((res, rej) => { + try { + // eslint-disable-next-line new-cap + const w = new sock.w3cwebsocket( + url || `${socketUrl}/logs`, + '', + '', + {} + ); + + w.onmessage = (msg) => { + try { + const m: { + timestamp: string; + message: string; + type: 'update' | 'error' | 'info'; + } = JSON.parse(msg.data as string); + + if (m.type === 'error') { + setLogs([]); + console.error(m.message); + return; + } + + if (m.type === 'info') { + console.log(m.message); + return; + } + + if (m.type === 'update') { + console.log(m.message); + return; + } + + if (m.type === 'log') { + setIsLoading(false); + setLogs((s) => [ + ...s, + { + pod_name: 'main', + message: m.message, + timestamp: m.timestamp, + }, + ]); + return; + } + + console.log(m); + } catch (err) { + console.error(err); + } + }; + + w.onopen = () => { + res(w); + }; + + w.onerror = (e) => { + rej(e); + }; + + w.onclose = () => { + // wsclient.send(newMessage({ event: 'unsubscribe', data: 'test' })); + logger.log('socket disconnected'); + }; + } catch (e) { + rej(e); + } + }); + } catch (e) { + console.log(e); + } + } + + useEffect(() => { + (async () => { + try { + // const client = await wsclient.; + // 'wss://auth-vision.devc.kloudlite.io/logs' + + if (account === '' || cluster === '' || trackingId === '') { + return () => {}; + } + + if (logs.length) { + setLogs([]); + } + setIsLoading(true); + + const client = await wsclient; + + setSocState(client); + + client.send( + JSON.stringify({ + event: 'subscribe', + data: { + account, + cluster, + trackingId, + }, + }) + ); + } catch (e) { + console.error(e); + setLogs([]); + setError((e as Error).message); + } + + return () => { + (async () => { + if (!socState) return; + + socState.send( + JSON.stringify({ + event: 'unsubscribe', + data: { + account, + cluster, + trackingId, + }, + }) + ); + + // client.close(); + + setLogs([]); + })(); + }; + })(); + }, [account, cluster, trackingId, url]); + + return { + logs, + error, + isLoading, + }; +}; + +const LogComp = ({ + websocket, follow = true, - url = '', enableSearch = true, selectableLines = true, title = '', @@ -559,96 +700,12 @@ const HighlightJsLog = ({ className = '', dark = false, }: IHighlightJsLog) => { - const [messages, setMessages] = useState([]); - const tempMessage = useRef(''); - const [errors, setErrors] = useState(''); - const [isLoading, setIsLoading] = useState(true); const [fullScreen, setFullScreen] = useState(false); - useEffect(() => { - if (tempMessage.current) { - try { - const data = JSON.parse(tempMessage.current); - setMessages((s) => [...s, ...data]); - tempMessage.current = ''; - setErrors(''); - setIsLoading(false); - } catch (error) { - const e = error as Error; - console.log(error); - setErrors( - `'Something went wrong! Please try again.', ${e.name}: ${+e.message}` - ); - } - } - }, [tempMessage.current]); - const { setClassName, removeClassName } = useClass({ elementClass: 'loading-container', }); - useEffect(() => { - (async () => { - if (!url || websocket) return; - setIsLoading(true); - try { - const d = await axios({ - url, - method: 'GET', - withCredentials: true, - }); - setMessages((d.data || '').trim()); - } catch (err) { - setErrors( - `${(err as Error).message} -An error occurred attempting to load the provided log. -Please check the URL and ensure it is reachable. -${url}` - ); - } finally { - setIsLoading(false); - } - })(); - }, []); - - useEffect(() => { - if (!url || !websocket) return () => {}; - - // setMessages([]); - - let wsclient: sock.w3cwebsocket; - setIsLoading(true); - try { - // eslint-disable-next-line new-cap - wsclient = new sock.w3cwebsocket(url, '', '', {}); - } catch (err) { - setErrors( - `${(err as Error).message} -An error occurred attempting to load the provided log. -Please check the URL and ensure it is reachable. -${url}` - ); - return () => {}; - } finally { - setIsLoading(false); - } - // wsclient.onopen = logger.log; - // wsclient.onclose = logger.log; - // wsclient.onerror = logger.log; - - wsclient.onmessage = (msg: sock.IMessageEvent) => { - try { - const data: ISocketMessage[] = JSON.parse(msg.data.toString()); - setMessages((s) => [...s, ...data]); - } catch (err) { - tempMessage.current += msg.data.toString(); - } - }; - return () => { - wsclient.close(); - }; - }, []); - useEffect(() => { const keyDownListener = (e: any) => { if (e.code === 'Escape') { @@ -670,16 +727,17 @@ ${url}` } }, [fullScreen]); - const mockDataRef = useRef( - Array.from({ length: 15 }).map(() => { - return { - message: logsMockData[Math.floor(Math.random() * 10)], - timestamp: dayjs().toISOString(), - }; - }) - ); + const { logs, error, isLoading } = useSocketLogs(websocket); - return ( + const [isClientSide, setIsClientSide] = useState(false); + + useEffect(() => { + if (!isClientSide) { + setIsClientSide(true); + } + }, []); + + return isClientSide ? (
- {errors ? ( -
{errors}
+ {error ? ( +
{error}
) : ( + ) : ( +
); }; -export default HighlightJsLog; +export default LogComp; diff --git a/src/apps/console/components/menu-select.tsx b/src/apps/console/components/menu-select.tsx index dbdae0a2d..0804b8a3b 100644 --- a/src/apps/console/components/menu-select.tsx +++ b/src/apps/console/components/menu-select.tsx @@ -2,6 +2,7 @@ import React, { ReactNode, useState } from 'react'; import * as Select from '@radix-ui/react-select'; import { cn } from '~/components/utils'; import { AnimatePresence, motion } from 'framer-motion'; +import { ChevronDown, ChevronUp } from '@jengaicons/react'; interface ISelectItem { children: ReactNode; @@ -48,6 +49,7 @@ const MenuSelect = ({ onChange, }: IMenuSelect) => { const [open, setOpen] = useState(false); + const scrollButtonSize = 12; return ( + + + { console.log(e); @@ -90,6 +95,9 @@ const MenuSelect = ({
))} + + + diff --git a/src/apps/console/components/name-id-view.tsx b/src/apps/console/components/name-id-view.tsx new file mode 100644 index 000000000..dfa5b877c --- /dev/null +++ b/src/apps/console/components/name-id-view.tsx @@ -0,0 +1,261 @@ +/* eslint-disable no-nested-ternary */ +import { CircleNotch } from '@jengaicons/react'; +import { ReactNode, forwardRef, useEffect, useState } from 'react'; +import { TextInput } from '~/components/atoms/input'; +import useDebounce from '~/root/lib/client/hooks/use-debounce'; +import { NonNullableString } from '~/root/lib/types/common'; +import { handleError } from '~/root/lib/utils/common'; +import { ConsoleResType, ResType } from '~/root/src/generated/gql/server'; +import { useOutletContext, useParams } from '@remix-run/react'; +import { dummyEvent } from '~/root/lib/client/hooks/use-form'; +import { + ensureAccountClientSide, + ensureClusterClientSide, +} from '../server/utils/auth-utils'; +import { useConsoleApi } from '../server/gql/api-provider'; +import { IEnvironmentContext } from '../routes/_main+/$account+/$project+/$environment+/_layout'; +import { parseName } from '../server/r-utils/common'; + +interface INameIdView { + name: string; + displayName: string; + resType: + | ConsoleResType + | ResType + | 'account' + | 'username' + | 'console_vpn_device' + | NonNullableString; + onChange?: ({ name, id }: { name: string; id: string }) => void; + prefix?: ReactNode; + errors?: string; + label?: ReactNode; + placeholder?: string; + onCheckError?: (error: boolean) => void; + isUpdate?: boolean; + handleChange?: (key: string) => (e: { + target: { + value: string; + }; + }) => void; + nameErrorLabel: string; +} + +export const NameIdView = forwardRef( + ( + { + name, + onChange = (_) => {}, + resType, + errors, + prefix, + label, + displayName, + placeholder, + onCheckError, + isUpdate, + handleChange, + nameErrorLabel, + }, + ref + ) => { + const [nameValid, setNameValid] = useState(false); + const [nameLoading, setNameLoading] = useState(true); + + const api = useConsoleApi(); + const params = useParams(); + + const checkApi = (() => { + switch (resType) { + case 'app': + case 'project': + case 'config': + case 'environment': + case 'managed_service': + case 'project_managed_service': + case 'managed_resource': + case 'router': + case 'console_vpn_device': + case 'secret': + ensureAccountClientSide(params); + ensureClusterClientSide(params); + return api.coreCheckNameAvailability; + + case 'cluster': + case 'providersecret': + ensureAccountClientSide(params); + return api.infraCheckNameAvailability; + case 'helm_release': + case 'vpn_device': + case 'nodepool': + return api.infraCheckNameAvailability; + + case 'account': + return api.accountCheckNameAvailability; + + case 'username': + return api.crCheckNameAvailability; + + default: + return api.coreCheckNameAvailability; + } + })(); + + useEffect(() => { + if (displayName && name) { + setNameLoading(true); + } + }, [displayName, name]); + + const checkNameAvailable = () => { + if (errors) { + // onCheckError?.(true); + return errors; + } + if (!name) { + // onCheckError?.(true); + return null; + } + + if (isUpdate) { + return null; + } + + if (nameLoading) { + // handleChange?.(nameErrorLabel)(dummyEvent(true)); + // setNameCheckError(true); + return ( +
+ + + + Checking availability +
+ ); + } + if (nameValid) { + // handleChange?.(nameErrorLabel)(dummyEvent(false)); + // setNameCheckError(false); + return ( + + {name} is available. + + ); + } + const error = 'This name is not available. Please try different.'; + onCheckError?.(!!error); + // handleChange?.(nameErrorLabel)(dummyEvent(!!error)); + // setNameCheckError(!!error); + return error; + }; + + const { cluster, environment, project } = params; + useDebounce( + async () => { + let tempResType = resType; + if (resType === 'console_vpn_device') { + tempResType = 'vpn_device'; + } + if (!isUpdate) + if (displayName) { + setNameLoading(true); + handleChange?.(nameErrorLabel)(dummyEvent(true)); + try { + // @ts-ignore + const { data, errors } = await checkApi({ + // @ts-ignore + resType: tempResType, + name: `${name}`, + ...([ + 'project', + 'app', + 'environment', + 'config', + 'secret', + 'project_managed_service', + 'console_vpn_device', + 'router', + ].includes(tempResType) + ? { + projectName: project, + envName: environment, + } + : {}), + ...(['nodepool', 'vpn_device', 'helm_release'].includes( + tempResType + ) + ? { + clusterName: cluster, + } + : {}), + ...(tempResType === 'managed_resource' + ? { + namespace: '', + } + : {}), + }); + + if (errors) { + throw errors[0]; + } + if (data.result) { + setNameValid(true); + handleChange?.(nameErrorLabel)(dummyEvent(false)); + } else { + setNameValid(false); + handleChange?.(nameErrorLabel)(dummyEvent(true)); + } + } catch (err) { + handleError(err); + } finally { + setNameLoading(false); + } + } else { + setNameLoading(false); + } + }, + 500, + [displayName, name, isUpdate] + ); + + return ( + { + const v = e.target.value; + const id = v.trim().toLowerCase().replace(/ /g, '-'); + onChange?.({ + name: v, + id, + }); + handleChange?.('displayName')(dummyEvent(v)); + if (!isUpdate) { + handleChange?.('name')(dummyEvent(id)); + } + if (v) { + setNameLoading(true); + if (!isUpdate) { + handleChange?.(nameErrorLabel)(dummyEvent(true)); + } + } else { + setNameLoading(false); + handleChange?.(nameErrorLabel)(dummyEvent(false)); + } + }} + placeholder={placeholder} + size="lg" + error={ + (!nameLoading || !isUpdate) && + ((!nameValid && !!name && !nameLoading) || !!errors) + } + message={checkNameAvailable()} + prefix={ + prefix && {prefix} / + } + focusRing + /> + ); + } +); diff --git a/src/apps/console/components/pulsable.tsx b/src/apps/console/components/pulsable.tsx index 256c6d83c..c7c4494b8 100644 --- a/src/apps/console/components/pulsable.tsx +++ b/src/apps/console/components/pulsable.tsx @@ -1,22 +1,31 @@ +import { createContext, useContext, useMemo } from 'react'; import ReactPulsable from 'react-pulsable'; import { ChildrenProps } from '~/components/types'; +const pulsableContext = createContext(false); + +export const usePulsableLoading = () => { + return useContext(pulsableContext); +}; + const Pulsable = ({ children, isLoading, }: ChildrenProps & { isLoading: boolean }) => { return ( - - {children} - + isLoading, [isLoading])}> + + {children} + + ); }; diff --git a/src/apps/console/components/sidebar-layout.tsx b/src/apps/console/components/sidebar-layout.tsx index 17b02ab28..26ef418e8 100644 --- a/src/apps/console/components/sidebar-layout.tsx +++ b/src/apps/console/components/sidebar-layout.tsx @@ -77,7 +77,8 @@ const SidebarLayout = ({
-
+ {/* If overflow problem occurs in error page look here */} +
{children}
diff --git a/src/apps/console/components/sync-status.tsx b/src/apps/console/components/sync-status.tsx new file mode 100644 index 000000000..9e2dbd26c --- /dev/null +++ b/src/apps/console/components/sync-status.tsx @@ -0,0 +1,216 @@ +import { + ArrowsClockwise, + Trash, + Info, + Warning, + WarningCircle, +} from '@jengaicons/react'; +import Tooltip from '~/components/atoms/tooltip'; +import { titleCase } from '~/components/utils'; +import { + Github__Com___Kloudlite___Api___Pkg___Types__SyncState as SyncState, + Github__Com___Kloudlite___Api___Pkg___Types__SyncAction as SyncAction, +} from '~/root/src/generated/gql/server'; + +interface IStatusMeta { + metadata?: { generation: number }; + recordVersion: number; + markedForDeletion?: boolean; + status?: { + checks?: any; + isReady: boolean; + lastReadyGeneration?: number; + lastReconcileTime?: any; + message?: { RawMessage?: any }; + resources?: Array<{ + apiVersion: string; + kind: string; + name: string; + namespace: string; + }>; + }; + syncStatus: { + action: SyncAction; + error?: string; + lastSyncedAt?: any; + recordVersion: number; + state: SyncState; + syncScheduledAt?: any; + }; +} + +const SyncStatus = ({ item }: { item: IStatusMeta }) => { + const statusIconSize = 16; + + const getMessages = () => { + let errors: Array = []; + if (item.status?.checks) { + try { + const err = Object.entries(item.status?.checks) + .map(([_key, values]) => { + const val = values as unknown as Record; + if (val.generation === item.metadata?.generation && !val.status) { + return val.message; + } + return null; + }) + .filter((er) => !!er); + errors = [...err]; + } catch { + // + } + } + + if (errors.length > 0) { + return { + errors, + render: ( +
+ + {errors.map((err) => ( + {err} + ))} +
+ } + > + + + + +
+ ), + }; + } + + return null; + }; + + if (item.markedForDeletion) { + return ( +
+ + Deleting + {getMessages()?.errors.map((error) => ( + + {titleCase(error)} + + ))} +
+ } + > + + + + +
+ ); + } + + if (item.syncStatus.error) { + return ( +
+ + Error + {titleCase(item.syncStatus.error)} +
+ } + > + + + + + + ); + } + + if ( + item.recordVersion !== item.syncStatus.recordVersion || + !item.status?.isReady + ) { + return ( +
+ + Not ready + {getMessages()?.errors.map((error) => ( + + {titleCase(error)} + + ))} +
+ } + > + + + + + + {/* {getError()} */} + + ); + } + + return null; +}; + +export type IStatus = 'deleting' | 'notready' | 'syncing' | 'none'; +type IResourceType = 'nodepool'; + +export const parseStatus = ({ + item, + type, +}: { + item: IStatusMeta; + type?: IResourceType; +}) => { + let status: IStatus = 'none'; + + if (item.markedForDeletion) { + status = 'deleting'; + } else if (!item.status?.isReady) { + switch (type) { + case 'nodepool': + status = 'syncing'; + break; + default: + status = 'notready'; + } + } + + return status; +}; + +export const listStatus = ({ + key, + className, + item, + type, +}: { + key: string; + className?: string; + item: IStatusMeta; + type?: IResourceType; +}) => { + return { + key, + className, + render: () => ( +
+ +
+ ), + status: parseStatus({ item, type }), + }; +}; + +export default SyncStatus; diff --git a/src/apps/console/dummy/data.js b/src/apps/console/dummy/data.js index 911401a26..2abc1cb81 100644 --- a/src/apps/console/dummy/data.js +++ b/src/apps/console/dummy/data.js @@ -722,14 +722,83 @@ export const dummyData = { }; export const logsMockData = [ - 'nnQhCA0QTSuukrRA', - '0dlRACNaaPWkx3hBmZNksKoR', - '9BLDLfyAHeCTjoUgLshkJ9eWpqfUkLEf', - 'TaJLEEhzWZvd6wt74fWe9oH9G7rhuzRO0ZZDFH49', - 'o2Znr1VqxwlnFcyOe8S78z1HXrwIUcrnGdpXoEj2smsD6d', - '5EIOShSO5vqfRojDxuRzdr0S5ILjQ65TuMEm7o6Q75maqJqKCnAj9L', - 'wfS7vAAvVY5BGhku2HE9Xnc7tF8wjH913u0i0f2JbOvXuvibfsFtbYnzC9ghk', - 'eBfcNEUnIcTDceQGc3jAoZdOno977hMQ5Hfd8LNrrnm9ZVnZpQyMl5Hf3FbTqthhIMdvzs', - 'jInteYIHeEBT7ZMKnNUbACPPs7YAUdnzegotEOzWkexdT1reZbzASBkb0LCx4k048gj2kUznyRvea45', - 'SQpMBRSCEvC2EWIq2eQ21ljhQkx7hi3t9WNgMpLI0vuaeaUTTcIDXYPqorSJUiGmvQ9R1uQ0x9O0z0fVCNvGYehoaAEYp', + { + pod_name: 'main', + message: + 'eBfcNEUnIcTDceQGc3jAoZdOno977hMQ5Hfd8LNrrnm9ZVnZpQyMl5Hf3FbTqthhIMdvzs', + timestamp: '2024-01-14T07:04:20.612Z', + }, + { + pod_name: 'main', + message: 'wfS7vAAvVY5BGhku2HE9Xnc7tF8wjH913u0i0f2JbOvXuvibfsFtbYnzC9ghk', + timestamp: '2024-01-14T07:04:20.612Z', + }, + { + pod_name: 'main', + message: '9BLDLfyAHeCTjoUgLshkJ9eWpqfUkLEf', + timestamp: '2024-01-14T07:04:20.612Z', + }, + { + pod_name: 'main', + message: 'TaJLEEhzWZvd6wt74fWe9oH9G7rhuzRO0ZZDFH49', + timestamp: '2024-01-14T07:04:20.612Z', + }, + { + pod_name: 'main', + message: + 'jInteYIHeEBT7ZMKnNUbACPPs7YAUdnzegotEOzWkexdT1reZbzASBkb0LCx4k048gj2kUznyRvea45', + timestamp: '2024-01-14T07:04:20.612Z', + }, + { + pod_name: 'main', + message: 'TaJLEEhzWZvd6wt74fWe9oH9G7rhuzRO0ZZDFH49', + timestamp: '2024-01-14T07:04:20.612Z', + }, + { + pod_name: 'main', + message: '0dlRACNaaPWkx3hBmZNksKoR', + timestamp: '2024-01-14T07:04:20.612Z', + }, + { + pod_name: 'main', + message: + 'eBfcNEUnIcTDceQGc3jAoZdOno977hMQ5Hfd8LNrrnm9ZVnZpQyMl5Hf3FbTqthhIMdvzs', + timestamp: '2024-01-14T07:04:20.612Z', + }, + { + pod_name: 'main', + message: '5EIOShSO5vqfRojDxuRzdr0S5ILjQ65TuMEm7o6Q75maqJqKCnAj9L', + timestamp: '2024-01-14T07:04:20.612Z', + }, + { + pod_name: 'main', + message: 'nnQhCA0QTSuukrRA', + timestamp: '2024-01-14T07:04:20.612Z', + }, + { + pod_name: 'main', + message: + 'jInteYIHeEBT7ZMKnNUbACPPs7YAUdnzegotEOzWkexdT1reZbzASBkb0LCx4k048gj2kUznyRvea45', + timestamp: '2024-01-14T07:04:20.612Z', + }, + { + pod_name: 'main', + message: '5EIOShSO5vqfRojDxuRzdr0S5ILjQ65TuMEm7o6Q75maqJqKCnAj9L', + timestamp: '2024-01-14T07:04:20.612Z', + }, + { + pod_name: 'main', + message: 'TaJLEEhzWZvd6wt74fWe9oH9G7rhuzRO0ZZDFH49', + timestamp: '2024-01-14T07:04:20.612Z', + }, + { + pod_name: 'main', + message: 'nnQhCA0QTSuukrRA', + timestamp: '2024-01-14T07:04:20.612Z', + }, + { + pod_name: 'main', + message: '5EIOShSO5vqfRojDxuRzdr0S5ILjQ65TuMEm7o6Q75maqJqKCnAj9L', + timestamp: '2024-01-14T07:04:20.612Z', + }, ]; diff --git a/src/apps/console/hooks/useProgressWrapperState.tsx b/src/apps/console/hooks/useProgressWrapperState.tsx new file mode 100644 index 000000000..794565161 --- /dev/null +++ b/src/apps/console/hooks/useProgressWrapperState.tsx @@ -0,0 +1,91 @@ +import { + Dispatch, + ReactNode, + SetStateAction, + createContext, + useContext, + useEffect, + useMemo, + useState, +} from 'react'; + +type IStep = { + label: string; + children: ReactNode; +}; + +interface IModifiedStep extends IStep { + active: boolean; + completed: boolean; +} + +const ManagedServiceStateContext = createContext<{ + nextStep: () => void; + prevStep: () => void; + steps: IModifiedStep[]; + setSteps: Dispatch>; +}>({ + nextStep() {}, + prevStep() {}, + steps: [], + setSteps() {}, +}); + +export const ManagedServiceStateProvider = ({ + children, +}: { + children: ReactNode; +}) => { + const [currentStepNumber, setCurrentStepNumber] = useState(1); + const [orgSteps, setOrgSteps] = useState([]); + const [steps, setModifiedSteps] = useState([]); + + const nextStep = () => { + if (currentStepNumber < orgSteps.length - 1) { + setCurrentStepNumber((prev) => prev + 1); + } + }; + + const prevStep = () => { + if (currentStepNumber > 0) { + setCurrentStepNumber((prev) => prev - 1); + } + }; + + useEffect(() => { + setModifiedSteps([ + ...orgSteps.map((step, index) => ({ + ...step, + children: index + 1 === currentStepNumber ? step.children : null, + active: index + 1 === currentStepNumber, + completed: false, + })), + ]); + }, [currentStepNumber, orgSteps]); + + return ( + ({ + nextStep, + prevStep, + steps, + setSteps: setOrgSteps, + }), + [steps] + )} + > + {children} + + ); +}; +const useManagedServiceState = ({ steps = [] }: { steps: IStep[] }) => { + const context = useContext(ManagedServiceStateContext); + console.log(steps); + + useEffect(() => { + context.setSteps(steps); + }, []); + return context; +}; +export default useManagedServiceState; diff --git a/src/apps/console/page-components/app-states.tsx b/src/apps/console/page-components/app-states.tsx index 4deb1b1c4..3a0b9fee3 100644 --- a/src/apps/console/page-components/app-states.tsx +++ b/src/apps/console/page-components/app-states.tsx @@ -208,7 +208,7 @@ export const useAppState = () => { setState, getContainer, setContainer, - activeContIndex, + activeContIndex: activeContIndex || 0, services: app.spec.services || [], setServices, }; diff --git a/src/apps/console/page-components/handle-console-devices.tsx b/src/apps/console/page-components/handle-console-devices.tsx new file mode 100644 index 000000000..01b93890d --- /dev/null +++ b/src/apps/console/page-components/handle-console-devices.tsx @@ -0,0 +1,688 @@ +/* eslint-disable no-nested-ternary */ +/* eslint-disable react/destructuring-assignment */ +import { + ArrowLineDown, + ArrowRight, + ChevronLeft, + ChevronRight, + Plus, + SmileySad, + X, +} from '@jengaicons/react'; +import { useParams } from '@remix-run/react'; +import { useEffect, useState } from 'react'; +import { IconButton } from '~/components/atoms/button'; +import { NumberInput } from '~/components/atoms/input'; +import { usePagination } from '~/components/molecule/pagination'; +import Popup from '~/components/molecule/popup'; +import { toast } from '~/components/molecule/toast'; +import { cn, useMapper } from '~/components/utils'; +import List from '~/console/components/list'; +import NoResultsFound from '~/console/components/no-results-found'; +import QRCode from '~/console/components/qr-code'; +import { useConsoleApi } from '~/console/server/gql/api-provider'; +import { IDevices } from '~/console/server/gql/queries/vpn-queries'; +import { + ExtractNodeType, + ensureResource, + parseName, + parseNodes, +} from '~/console/server/r-utils/common'; +import { ensureClusterClientSide } from '~/console/server/utils/auth-utils'; +import { useReload } from '~/root/lib/client/helpers/reloader'; +import useForm, { dummyEvent } from '~/root/lib/client/hooks/use-form'; +import { ENV_NAMESPACE } from '~/root/lib/configs/env'; +import Yup from '~/root/lib/server/helpers/yup'; +import { handleError } from '~/root/lib/utils/common'; +import { IDialogBase } from '~/console/components/types.d'; +import CommonPopupHandle from '~/console/components/common-popup-handle'; +import { LoadingPlaceHolder } from '~/console/components/loading'; +import { downloadFile } from '~/console/utils/commons'; +import CodeView from '~/console/components/code-view'; +import { InfoLabel } from '~/console/components/commons'; +import { parseValue } from '~/console/page-components/util'; +import { NameIdView } from '~/console/components/name-id-view'; +import { + IConsoleDevices, + IConsoleDevicesForUser, +} from '~/console/server/gql/queries/console-vpn-queries'; +import useCustomSwr from '~/root/lib/client/hooks/use-custom-swr'; +import Select from '~/components/atoms/select'; +import { ConsoleApiType } from '../server/gql/saved-queries'; + +interface IExposedPorts { + targetPort?: number; + port?: number; +} + +interface IExposedPortList { + exposedPorts: IExposedPorts[]; + onDelete: (exposedPorts: IExposedPorts) => void; +} +const ExposedPortList = ({ + exposedPorts, + onDelete = (_) => _, +}: IExposedPortList) => { + const itemsPerPage = 4; + + const { page, hasNext, hasPrevious, onNext, onPrev, setItems } = + usePagination({ + items: exposedPorts, + itemsPerPage, + }); + + useEffect(() => { + setItems(exposedPorts); + }, [exposedPorts]); + return ( +
+ {exposedPorts.length > 0 && ( + +
+ Exposed ports +
+
+ } + size="xs" + variant="plain" + onClick={() => onPrev()} + disabled={!hasPrevious} + /> + } + size="xs" + variant="plain" + onClick={() => onNext()} + disabled={!hasNext} + /> +
+
+ } + > + {page.map((ep, index) => { + return ( + ( +
+ Exposed: + {ep.port} + + Target: + {ep.targetPort} +
+ ), + }, + { + key: `${index}-column-3`, + render: () => ( +
+ } + variant="plain" + size="sm" + onClick={() => { + onDelete(ep); + }} + /> +
+ ), + }, + ]} + /> + ); + })} + + )} + {exposedPorts.length === 0 && ( +
+ } + shadow={false} + border={false} + /> +
+ )} + + ); +}; + +export const ExposedPorts = ({ + ports, + onChange, +}: { + ports: IExposedPorts[]; + onChange: (ports: IExposedPorts[]) => void; +}) => { + const { errors, handleChange, submit, values, resetValues } = useForm({ + initialValues: { + port: '', + targetPort: '', + }, + validationSchema: Yup.object({ + port: Yup.number() + .required() + .test('is-valid', 'Port already exists.', (value) => { + return !ports.some((p) => p.port === value); + }), + targetPort: Yup.number().min(0).max(65535).required(), + }), + onSubmit: (val) => { + onChange?.([ + ...ports, + { + port: + typeof val.port === 'string' ? parseInt(val.port, 10) : val.port, + targetPort: + typeof val.targetPort === 'string' + ? parseInt(val.targetPort, 10) + : val.targetPort, + }, + ]); + resetValues(); + }, + }); + + return ( + <> +
+
+
+ + } + size="lg" + error={!!errors.port} + message={errors.port} + value={values.port} + onChange={({ target }) => { + handleChange('port')(dummyEvent(parseValue(target.value, 0))); + }} + /> +
+
+ + } + size="lg" + autoComplete="off" + value={values.targetPort} + onChange={({ target }) => { + handleChange('targetPort')( + dummyEvent(parseValue(target.value, 0)) + ); + }} + /> +
+
+ } + variant="basic" + disabled={!values.port || !values.targetPort} + onClick={submit} + /> +
+
+
+ { + onChange?.(ports.filter((v) => v.port !== ep.port)); + }} + /> + + ); +}; + +export const QRCodeView = ({ data }: { data: string }) => { + return ( +
+
+
+ Use WireGuard on your phone +
+
    +
  • Download the app from Google Play or Apple Store
  • +
  • Open the app on your Phone
  • +
  • Tab on the ➕ Plus icon
  • +
  • Point your phone to this screen to capture the QR code
  • +
+
+
+ +
+
+ ); +}; + +export const decodeConfig = ({ + encoding, + value, +}: { + encoding: string; + value: string; +}) => { + switch (encoding) { + case 'base64': + return atob(value); + default: + return value; + } +}; + +const downloadConfig = ({ + filename, + data, +}: { + filename: string; + data: string; +}) => { + downloadFile({ filename, data, format: 'text/plain' }); +}; + +export const ShowWireguardConfig = ({ + visible, + setVisible, + data, + mode = 'config', +}: { + visible: boolean; + setVisible: (visible: boolean) => void; + data: { device: string }; + mode: 'qr' | 'config'; +}) => { + const [loading, setLoading] = useState(false); + const [config, setConfig] = useState(undefined); + const api = useConsoleApi(); + + useEffect(() => { + if (visible) { + (async () => { + setLoading(true); + try { + const { errors, data: out } = await api.getConsoleVpnDevice({ + name: data.device, + }); + if (errors) { + throw errors[0]; + } + if (out.wireguardConfig) { + setConfig(decodeConfig(out.wireguardConfig)); + } else { + setConfig(undefined); + } + } catch (error) { + handleError(error); + } finally { + setLoading(false); + } + })(); + } + }, [visible]); + + const modeView = () => { + if (!config) { + return ( +
+ No wireguard config found. +
+ ); + } + switch (mode) { + case 'qr': + return ; + case 'config': + default: + return ( +
+
+ Please use the following configuration to set up your WireGuard + client. +
+ +
+ ); + } + }; + + return ( + + + {mode === 'config' ? 'Wireguard Config' : 'Wireguard Config QR Code'} + + + {loading ? ( + + ) : ( + modeView() + )} + + {!loading && config && ( + + { + downloadConfig({ + filename: `${data.device}-wireguardconfig.yaml`, + data: config, + }); + }} + content="Export" + prefix={} + variant="primary" + /> + + )} + + ); +}; + +export const switchEnvironment = async ({ + api, + device, + environment, + project, +}: { + api: ConsoleApiType; + device: IConsoleDevicesForUser[number]; + environment: string; + project: string; +}) => { + try { + const { errors } = await api.updateConsoleVpnDevice({ + vpnDevice: { + displayName: device.displayName, + metadata: { + name: parseName(device), + }, + environmentName: environment, + projectName: project, + spec: { + ports: device.spec?.ports, + }, + }, + }); + if (errors) { + throw errors[0]; + } + toast.success('Device switched successfully'); + } catch (err) { + handleError(err); + } +}; + +type IDialog = IDialogBase>; + +const Root = (props: IDialog) => { + const { isUpdate, setVisible } = props; + const api = useConsoleApi(); + const reloadPage = useReload(); + + const { values, errors, handleChange, handleSubmit, resetValues, isLoading } = + useForm({ + initialValues: isUpdate + ? { + displayName: props.data.displayName, + name: parseName(props.data), + ports: props.data.spec?.ports || [], + isNameError: false, + projectName: props.data.projectName, + environmentName: props.data.environmentName, + } + : { + displayName: '', + name: '', + ports: [], + isNameError: false, + projectName: '', + environmentName: '', + }, + validationSchema: isUpdate + ? Yup.object({ + name: Yup.string().required(), + displayName: Yup.string().required(), + projectName: Yup.string().required(), + environmentName: Yup.string().required(), + }) + : Yup.object({ + name: Yup.string().required(), + displayName: Yup.string().required(), + }), + onSubmit: async (val) => { + try { + if (!isUpdate) { + const { errors } = await api.createConsoleVpnDevice({ + vpnDevice: { + displayName: val.displayName, + metadata: { + name: val.name, + }, + spec: { + ports: val.ports, + }, + }, + }); + if (errors) { + throw errors[0]; + } + toast.success('Device created successfully'); + } else if (isUpdate && props.data) { + const { errors } = await api.updateConsoleVpnDevice({ + vpnDevice: { + displayName: val.displayName, + metadata: { + name: parseName(props.data), + }, + environmentName: val.environmentName, + projectName: val.projectName, + spec: { + ports: val.ports, + }, + }, + }); + if (errors) { + throw errors[0]; + } + toast.success('Device updated successfully'); + } + reloadPage(); + setVisible(false); + } catch (err) { + handleError(err); + } + }, + }); + + useEffect(() => { + if (!isUpdate) { + resetValues(); + } + }, []); + + const { + data: projectData, + error: projectError, + isLoading: projectIsLoading, + } = useCustomSwr('/projects', async () => { + return api.listProjects({}); + }); + + const { + data: envData, + error: envError, + isLoading: envLoading, + } = useCustomSwr( + () => (values.projectName ? `/environments-${values.projectName}` : null), + async () => { + if (!values.projectName) { + throw new Error('Project name is required!.'); + } + return api.listEnvironments({ + projectName: values.projectName, + }); + } + ); + + const projects = useMapper(parseNodes(projectData), (val) => ({ + label: val.displayName, + value: parseName(val), + project: val, + render: () => ( +
+
{val.displayName}
+
{parseName(val)}
+
+ ), + })); + + const environments = useMapper(parseNodes(envData), (val) => ({ + label: val.displayName, + value: parseName(val), + project: val, + render: () => ( +
+
{val.displayName}
+
{parseName(val)}
+
+ ), + })); + + useEffect(() => { + console.log('errors here', errors); + }, [errors]); + + return ( + { + console.log('name error', values.isNameError); + + if (!values.isNameError) { + handleSubmit(e); + } else { + e.preventDefault(); + } + }} + > + +
+ + {isUpdate && ( + <> +
+
+ [...environments]} + value={ + values.environmentName + ? { + label: values.environmentName, + value: values.environmentName, + } + : undefined + } + onChange={(val) => { + handleChange('environmentName')(dummyEvent(val.value)); + }} + /> +
+
+ { + handleChange('ports')(dummyEvent(ports)); + }} + /> + + )} +
+
+ + + + +
+ ); +}; + +const HandleConsoleDevices = (props: IDialog) => { + return ( + + ); +}; + +export default HandleConsoleDevices; diff --git a/src/apps/console/page-components/new-cluster.tsx b/src/apps/console/page-components/new-cluster.tsx index a7a8243ac..0eeb12a95 100644 --- a/src/apps/console/page-components/new-cluster.tsx +++ b/src/apps/console/page-components/new-cluster.tsx @@ -9,7 +9,6 @@ import { mapper, useMapper } from '~/components/utils'; import useForm, { dummyEvent } from '~/root/lib/client/hooks/use-form'; import Yup from '~/root/lib/server/helpers/yup'; import { handleError } from '~/root/lib/utils/common'; -import { IdSelector } from '../components/id-selector'; import { constDatas, awsRegions } from '../dummy/consts'; import { useConsoleApi } from '../server/gql/api-provider'; import { @@ -26,6 +25,8 @@ import { import { ensureAccountClientSide } from '../server/utils/auth-utils'; // import { IAccountContext } from '../routes/_main+/$account+/_layout'; import ProgressWrapper from '../components/progress-wrapper'; +import { NameIdView } from '../components/name-id-view'; +import { ReviewComponent } from '../routes/_main+/$account+/$project+/$environment+/new-app/app-review'; type props = | { @@ -37,6 +38,8 @@ type props = cloudProvider: IProviderSecret; }; +type steps = 'Configure cluster' | 'Review'; + export const NewCluster = ({ providerSecrets, cloudProvider }: props) => { const { cloudprovider: cp } = useParams(); const isOnboarding = !!cp; @@ -64,7 +67,8 @@ export const NewCluster = ({ providerSecrets, cloudProvider }: props) => { const { a: accountName } = useParams(); // const { account } = useOutletContext(); - + const [activeState, setActiveState] = useState('Configure cluster'); + const isActive = (step: steps) => step === activeState; const navigate = useNavigate(); const [selectedProvider, setSelectedProvider] = useState< @@ -96,96 +100,103 @@ export const NewCluster = ({ providerSecrets, cloudProvider }: props) => { credentialsRef: cp || parseName(selectedProvider?.provider) || '', availabilityMode: '', displayName: '', + isNameError: false, }, validationSchema: Yup.object({ vpc: Yup.string(), - region: Yup.string().trim().required('region is required'), - cloudProvider: Yup.string().trim().required('cloud provider is required'), - name: Yup.string().trim().required('id is required'), - displayName: Yup.string().trim().required('name is required'), + region: Yup.string().trim().required('Region is required'), + cloudProvider: Yup.string().trim().required('Cloud provider is required'), + name: Yup.string().trim().required('Name is required'), + displayName: Yup.string().trim().required('Name is required'), credentialsRef: Yup.string().required(), availabilityMode: Yup.string() .trim() - .required('availability is required') - .oneOf(['HA', 'dev']), - }), + .oneOf(['HA', 'dev']) + .required('Availability mode is required'), + }).required(), onSubmit: async (val) => { - // type Merge = Omit & M; - - // type nt = { availabilityMode: 'HA' | 'dev' | string }; - // const k: Merge = val; - - // console.log(k); - // val.availabilityMode - if (!accountName || !val.availabilityMode) { - return; - } - try { - ensureAccountClientSide({ account: accountName }); - const { errors: e } = await api.createCluster({ - cluster: { - displayName: val.displayName, - spec: { - cloudProvider: validateClusterCloudProvider(val.cloudProvider), - aws: { - region: selectedRegion.Name, - k3sMasters: { - nvidiaGpuEnabled: true, - instanceType: 'c6a.xlarge', + const submit = async () => { + if (!accountName || !val.availabilityMode) { + return; + } + try { + ensureAccountClientSide({ account: accountName }); + const { errors: e } = await api.createCluster({ + cluster: { + displayName: val.displayName, + spec: { + cloudProvider: validateClusterCloudProvider(val.cloudProvider), + aws: { + region: selectedRegion.Name, + k3sMasters: { + nvidiaGpuEnabled: true, + instanceType: 'c6a.xlarge', + }, }, + credentialsRef: { + name: val.credentialsRef, + }, + availabilityMode: validateAvailabilityMode( + val.availabilityMode + ), }, - credentialsRef: { - name: val.credentialsRef, + metadata: { + name: val.name, }, - availabilityMode: validateAvailabilityMode(val.availabilityMode), - }, - metadata: { - name: val.name, }, - }, - }); - if (e) { - throw e[0]; + }); + if (e) { + throw e[0]; + } + toast.success('Cluster created successfully'); + navigate(`/${accountName}/infra/clusters`); + } catch (err) { + handleError(err); } - toast.success('cluster created successfully'); - navigate(`/${accountName}/infra/clusters`); - } catch (err) { - handleError(err); + }; + + switch (activeState) { + case 'Configure cluster': + setActiveState('Review'); + break; + case 'Review': + await submit(); + break; + default: + break; } }, }); const getView = () => { return ( -
+ { + if (!values.isNameError) { + handleSubmit(e); + } else { + e.preventDefault(); + } + }} + >
A cluster is a group of interconnected elements working together as a single unit.
- {Object.keys(JSON.parse(JSON.stringify(errors || '{}')) || {}) - .length > 0 && ( -
-                {JSON.stringify(errors, null, 2)}
-              
- )} -
- { - handleChange('name')({ target: { value: v } }); - }} - />
{!isOnboarding && ( { ); }; + const getReviewView = () => { + return ( + + { + setActiveState('Configure project'); + }} + > +
+
+
+ Project name +
+
{values.name}
+
+
+
Cluster
+
{values.clusterName}
+
+
+
+
+
+ + ); + }; + const getItems = () => { return [ { label: 'Configure project', - active: true, + active: isActive('Configure project'), completed: false, - children: getView(), + children: isActive('Configure project') ? getView() : null, }, { label: 'Review', - active: false, + active: isActive('Review'), completed: false, + children: isActive('Review') ? getReviewView() : null, }, ]; }; @@ -237,11 +219,17 @@ const NewProject = () => { return ( { + if (!isOnboarding) { + if (isActive('Review')) { + setActiveState('Configure project'); + } + } + }} /> ); }; diff --git a/src/apps/console/page-components/new-scope.tsx b/src/apps/console/page-components/new-scope.tsx index 8ddeefead..a7acf6147 100644 --- a/src/apps/console/page-components/new-scope.tsx +++ b/src/apps/console/page-components/new-scope.tsx @@ -1,20 +1,19 @@ import { useParams } from '@remix-run/react'; import { useEffect, useState } from 'react'; -import * as Chips from '~/components/atoms/chips'; -import { TextInput } from '~/components/atoms/input'; import Popup from '~/components/molecule/popup'; import { toast } from '~/components/molecule/toast'; -import { IdSelector } from '~/console/components/id-selector'; import { parseName, parseTargetNs } from '~/console/server/r-utils/common'; import { useReload } from '~/root/lib/client/helpers/reloader'; import { useDataFromMatches } from '~/root/lib/client/hooks/use-custom-matches'; -import useForm from '~/root/lib/client/hooks/use-form'; +import useForm, { dummyEvent } from '~/root/lib/client/hooks/use-form'; import Yup from '~/root/lib/server/helpers/yup'; import { handleError } from '~/root/lib/utils/common'; +import { Switch } from '~/components/atoms/switch'; import { IDialog } from '../components/types.d'; import { useConsoleApi } from '../server/gql/api-provider'; import { DIALOG_TYPE } from '../utils/commons'; import { IEnvironment } from '../server/gql/queries/environment-queries'; +import { NameIdView } from '../components/name-id-view'; const HandleScope = ({ show, setShow }: IDialog & {}) => { const api = useConsoleApi(); @@ -42,6 +41,8 @@ const HandleScope = ({ show, setShow }: IDialog & {}) => { initialValues: { name: '', displayName: '', + environmentRoutingMode: false, + isNameError: false, }, validationSchema, @@ -62,6 +63,9 @@ const HandleScope = ({ show, setShow }: IDialog & {}) => { spec: { projectName: projectName || '', targetNamespace: `${projectName}-${val.name}`, + routing: { + mode: val.environmentRoutingMode ? 'public' : 'private', + }, }, }, }); @@ -126,40 +130,35 @@ const HandleScope = ({ show, setShow }: IDialog & {}) => { ? `Create new environment` : `Edit environment`} -
+ { + if (!values.isNameError) { + handleSubmit(e); + } else { + e.preventDefault(); + } + }} + > -
- {show?.type === DIALOG_TYPE.EDIT && ( - - )} - - + -
- {show?.type === DIALOG_TYPE.ADD && ( - { - handleChange('name')({ target: { value: id } }); + { + handleChange('environmentRoutingMode')(dummyEvent(val)); }} - className="pt-2xl" /> - )} +
@@ -170,7 +169,7 @@ const HandleScope = ({ show, setShow }: IDialog & {}) => { variant="primary" /> - + ); }; diff --git a/src/apps/console/page-components/util.tsx b/src/apps/console/page-components/util.tsx index 3841b0c28..bd09639ac 100644 --- a/src/apps/console/page-components/util.tsx +++ b/src/apps/console/page-components/util.tsx @@ -43,7 +43,7 @@ export function parseValue(v: any, def: T): T { try { switch (typeof def) { case 'number': - const res = parseInt(v, 10); + const res = parseFloat(v); if (Number.isNaN(res)) { return def; } diff --git a/src/apps/console/routes/_a+/new-team.tsx b/src/apps/console/routes/_a+/new-team.tsx index 9cca5175f..66c651f62 100644 --- a/src/apps/console/routes/_a+/new-team.tsx +++ b/src/apps/console/routes/_a+/new-team.tsx @@ -224,7 +224,6 @@ const NewAccount = () => { const { errors: _errors } = await api.createAccount({ account: { metadata: { name: v.name }, - spec: {}, displayName: v.displayName, contactEmail: user.email, }, @@ -268,7 +267,7 @@ const NewAccount = () => {
+ +
+ ); +}; + +export default ItemList; diff --git a/src/apps/console/routes/_main+/$account+/$cluster+/$project+/$scope+/$workspace+/app+/$app+/logs/tools.tsx b/src/apps/console/routes/_main+/$account+/$cluster+/$project+/$scope+/$workspace+/app+/$app+/logs/tools.tsx new file mode 100644 index 000000000..1e1db9c82 --- /dev/null +++ b/src/apps/console/routes/_main+/$account+/$cluster+/$project+/$scope+/$workspace+/app+/$app+/logs/tools.tsx @@ -0,0 +1,30 @@ +import { useSearchParams } from '@remix-run/react'; +import { useMemo } from 'react'; +import { toast } from 'react-toastify'; +import CommonTools from '~/console/components/common-tools'; + +const Tools = () => { + const [searchParams] = useSearchParams(); + + const options = useMemo( + () => [ + { + name: 'Status', + type: 'text', + search: false, + dataFetcher: async () => { + return [ + { content: 'Active', value: 'active' }, + { content: 'Freezed', value: 'freezed' }, + { content: 'Archived', value: 'archived' }, + ]; + }, + }, + ], + [searchParams] + ); + + return ; +}; + +export default Tools; diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/_layout.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/_layout.tsx index 7dd5462cf..f2d81e619 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/_layout.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/_layout.tsx @@ -1,15 +1,19 @@ import { + BackingServices, ChevronDown, + CirclesFour, + Database, + GearSix, Plus, Search, - VirtualMachine, - Database, + File, + TreeStructure, } from '@jengaicons/react'; import { redirect } from '@remix-run/node'; import { + Link, Outlet, useLoaderData, - useNavigate, useOutletContext, useParams, } from '@remix-run/react'; @@ -19,7 +23,11 @@ import Breadcrum from '~/console/components/breadcrum'; import { CommonTabs } from '~/console/components/common-navbar-tabs'; import HandleScope from '~/console/page-components/new-scope'; import { GQLServerHandler } from '~/console/server/gql/saved-queries'; -import { parseName, parseNodes } from '~/console/server/r-utils/common'; +import { + ExtractNodeType, + parseName, + parseNodes, +} from '~/console/server/r-utils/common'; import { ensureAccountClientSide, ensureAccountSet, @@ -34,10 +42,14 @@ import { useConsoleApi } from '~/console/server/gql/api-provider'; import { BreadcrumButtonContent, BreadcrumSlash, + tabIconSize, } from '~/console/utils/commons'; -import MenuSelect from '~/console/components/menu-select'; -import { IEnvironment } from '~/console/server/gql/queries/environment-queries'; -import { toast } from '~/components/molecule/toast'; +import { + IEnvironment, + IEnvironments, +} from '~/console/server/gql/queries/environment-queries'; +import { useActivePath } from '~/root/lib/client/hooks/use-active-path'; +import { cn } from '~/components/utils'; import { IProjectContext } from '../_layout'; export interface IEnvironmentContext extends IProjectContext { @@ -55,92 +67,83 @@ const Environment = () => { ); }; +const tabs = [ + { + label: ( + + + Apps + + ), + to: '/apps', + value: '/apps', + }, + { + label: ( + + + Router + + ), + to: '/routers', + value: '/routers', + }, + { + label: ( + + + Configs and Secrets + + ), + to: '/cs/configs', + value: '/cs', + }, + { + label: ( + + + Managed resources + + ), + to: '/managed-resources', + value: '/managed-resources', + }, + // { + // label: 'Jobs & Crons', + // to: '/jc/task', + // value: '/jc', + // }, + { + label: ( + + + Settings + + ), + to: '/settings/general', + value: '/settings', + }, +]; + const EnvironmentTabs = () => { const { account, project, environment } = useParams(); return ( - - ); -}; - -const EnvironmentDropdown = () => { - const navigate = useNavigate(); - const { account, project, cluster } = useParams(); - const iconSize = 14; - - const menuItems = [ - { - label: ( - - - Environments - - ), - value: `/${account}/${cluster}/${project}/environments`, - }, - { - label: ( - - - Managed Services - - ), - value: `/${account}/${cluster}/${project}/managed-services`, - }, - ]; - return ( - navigate(value)} - trigger={ - } - /> - } - /> + ); }; -// @ts-ignore const CurrentBreadcrum = ({ environment }: { environment: IEnvironment }) => { const params = useParams(); const [showPopup, setShowPopup] = useState(null); - const [environments, setEnvironments] = useState([]); + const [environments, setEnvironments] = useState< + ExtractNodeType[] + >([]); const api = useConsoleApi(); const [search, setSearch] = useState(''); - const { project } = params; + const { project, account } = params; useDebounce( async () => { @@ -156,8 +159,6 @@ const CurrentBreadcrum = ({ environment }: { environment: IEnvironment }) => { if (errors) { throw errors[0]; } - // console.log(data); - setEnvironments(parseNodes(data)); } catch (err) { handleError(err); @@ -167,13 +168,13 @@ const CurrentBreadcrum = ({ environment }: { environment: IEnvironment }) => { [search] ); - // const navigate = useNavigate(); + const { activePath } = useActivePath({ + parent: `/${account}/${project}/${parseName(environment)}`, + }); return ( <> - {/* */} - {/* */} @@ -183,7 +184,11 @@ const CurrentBreadcrum = ({ environment }: { environment: IEnvironment }) => { content={
tab.to === activePath) + ? 'bodyMd-semibold' + : '' + )} content={environment.displayName} /> @@ -197,6 +202,7 @@ const CurrentBreadcrum = ({ environment }: { environment: IEnvironment }) => { value={search} onChange={(e) => setSearch(e.target.value)} prefixIcon={} + focusRing={false} placeholder="Search" compact className="border-0 rounded-none" @@ -206,12 +212,13 @@ const CurrentBreadcrum = ({ environment }: { environment: IEnvironment }) => { {environments.map((item) => { return ( - toast.info('todo')} + {item.displayName} - + ); })} diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/logs/route.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/logs/route.tsx new file mode 100644 index 000000000..5369da90c --- /dev/null +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/logs/route.tsx @@ -0,0 +1,27 @@ +import { useOutletContext } from '@remix-run/react'; +import LogComp from '~/console/components/logger'; +import { parseName } from '~/console/server/r-utils/common'; +import { IAppContext } from '../route'; + +const Overview = () => { + const { app, project, account } = useOutletContext(); + return ( +
+ +
+ ); +}; + +export default Overview; diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/route.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/route.tsx index c017f8128..a7f24be71 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/route.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/route.tsx @@ -10,27 +10,9 @@ import { IApp } from '~/console/server/gql/queries/app-queries'; import { GQLServerHandler } from '~/console/server/gql/saved-queries'; import { ensureAccountSet } from '~/console/server/utils/auth-utils'; import logger from '~/root/lib/client/helpers/log'; -import { - SubNavDataProvider, - useSubNavData, -} from '~/root/lib/client/hooks/use-create-subnav-action'; import { IRemixCtx } from '~/root/lib/types/common'; -import { useEffect, useState } from 'react'; -import { toast } from 'react-toastify'; -import Popup from '~/components/molecule/popup'; -import { DiffViewer, yamlDump } from '~/console/components/diff-viewer'; import { LoadingComp, pWrapper } from '~/console/components/loading-component'; -import { - AppContextProvider, - useAppState, -} from '~/console/page-components/app-states'; -import { useConsoleApi } from '~/console/server/gql/api-provider'; -import { getAppIn } from '~/console/server/r-utils/resource-getter'; -import { useActivePath } from '~/root/lib/client/hooks/use-active-path'; -import useForm from '~/root/lib/client/hooks/use-form'; -import Yup from '~/root/lib/server/helpers/yup'; -import { handleError } from '~/root/lib/utils/common'; import { IEnvironmentContext } from '../../_layout'; const ProjectTabs = () => { @@ -76,97 +58,8 @@ export interface IAppContext extends IEnvironmentContext { const AppOutlet = ({ app: oApp }: { app: IApp }) => { const rootContext = useOutletContext(); - const { data: subNavData, setData: setSubNavAction } = useSubNavData(); - const { app, setApp, resetState } = useAppState(); - const [isOpen, setIsOpen] = useState(false); - - const { account, project, environment, app: appId } = useParams(); - const { activePath } = useActivePath({ - parent: `/${account}/${project}//${environment}/app/${appId}/settings`, - }); - useEffect(() => { - resetState(oApp); - }, [activePath]); - - const api = useConsoleApi(); - - const { isLoading, submit } = useForm({ - initialValues: {}, - validationSchema: Yup.object({}), - onSubmit: async () => { - if (!project || !environment) { - throw new Error('Project and Environment is required!.'); - } - try { - const { errors } = await api.updateApp({ - app: getAppIn(app), - envName: environment, - projectName: project, - }); - if (errors) { - throw errors[0]; - } - toast.success('app updated'); - // @ts-ignore - window.reload(); - } catch (err) { - handleError(err); - } - }, - }); - - // useEffect(() => { - // if (JSON.stringify(app) !== JSON.stringify(oApp)) { - // setSubNavAction({ - // ...(subNavData || {}), - // show: true, - // content: 'View Changes', - // action() { - // setIsOpen(true); - // }, - // subAction() { - // setApp(oApp); - // }, - // }); - // } else { - // setSubNavAction({ - // ...(subNavData || {}), - // show: false, - // }); - // } - // }, [app, oApp]); - return ( - <> - setIsOpen(v)} - > - Review Changes - - - - - { - submit(); - }} - content="Commit Changes" - /> - - - - - - ); + return ; }; export const loader = async (ctx: IRemixCtx) => { @@ -202,13 +95,7 @@ const App = () => { return ( {({ app }) => { - return ( - - - - - - ); + return ; }} ); diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/_layout.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/_layout.tsx index 2406fb2a3..5dd8beb07 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/_layout.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/_layout.tsx @@ -1,6 +1,24 @@ -import { Outlet, useOutletContext } from '@remix-run/react'; +import { Outlet, useOutletContext, useParams } from '@remix-run/react'; import SidebarLayout from '~/console/components/sidebar-layout'; -import { useSubNavData } from '~/root/lib/client/hooks/use-create-subnav-action'; +import { + UnsavedChangesProvider, + useUnsavedChanges, +} from '~/root/lib/client/hooks/use-unsaved-changes'; +import Popup from '~/components/molecule/popup'; +import { handleError } from '~/root/lib/utils/common'; +import { toast } from '~/components/molecule/toast'; +import { getAppIn } from '~/console/server/r-utils/resource-getter'; +import useForm from '~/root/lib/client/hooks/use-form'; +import { useConsoleApi } from '~/console/server/gql/api-provider'; +import { useEffect } from 'react'; +import { + AppContextProvider, + useAppState, +} from '~/console/page-components/app-states'; +import Yup from '~/root/lib/server/helpers/yup'; +import { DiffViewer, yamlDump } from '~/console/components/diff-viewer'; +import { useReload } from '~/root/lib/client/helpers/reloader'; +import { keyconstants } from '~/console/server/r-utils/key-constants'; import { IAppContext } from '../route'; const navItems = [ @@ -11,19 +29,136 @@ const navItems = [ { label: 'Advance', value: 'advance' }, ]; -const Settings = () => { +const Layout = () => { const rootContext = useOutletContext(); - const subNavAction = useSubNavData(); + const { + setHasChanges, + setIgnorePaths, + performAction, + setPerformAction, + setLoading, + } = useUnsavedChanges(); + const { app, setApp } = useAppState(); + + const { account, project, environment, app: appId } = useParams(); + + useEffect(() => { + setIgnorePaths( + navItems.map( + (ni) => + `/${account}/${project}/${environment}/app/${appId}/settings/${ni.value}` + ) + ); + }, []); + + const api = useConsoleApi(); + const reload = useReload(); + + const { isLoading, submit } = useForm({ + initialValues: {}, + validationSchema: Yup.object({}), + onSubmit: async () => { + if (!project || !environment) { + throw new Error('Project and Environment is required!.'); + } + try { + const { errors } = await api.updateApp({ + app: getAppIn(app), + envName: environment, + projectName: project, + }); + if (errors) { + throw errors[0]; + } + toast.success('App updated successfully'); + // @ts-ignore + setPerformAction(''); + reload(); + setHasChanges(false); + } catch (err) { + handleError(err); + } + }, + }); + + useEffect(() => { + const isNotSame = JSON.stringify(app) !== JSON.stringify(rootContext.app); + + if (isNotSame) { + setHasChanges(true); + } else { + setHasChanges(false); + } + }, [app, rootContext.app]); + + useEffect(() => { + setApp(rootContext.app); + setLoading(false); + }, [rootContext.app]); + + useEffect(() => { + if (performAction === 'discard-changes') { + setApp(rootContext.app); + setPerformAction(''); + } + }, [performAction]); + return ( - + + setPerformAction(v)} + > + Review Changes + + + + + { + submit(); + setLoading(true); + }} + content="Commit Changes" + /> + + ); }; +const Settings = () => { + const rootContext = useOutletContext(); + if (!rootContext.app.metadata?.annotations?.[keyconstants.description]) { + rootContext.app = { + ...rootContext.app, + // @ts-ignore + metadata: { + ...(rootContext.app.metadata || {}), + annotations: { + ...(rootContext.app.metadata?.annotations || {}), + [keyconstants.description]: '', + }, + }, + }; + } + + return ( + + + + + + ); +}; + export default Settings; diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/advance/route.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/advance/route.tsx index 2589a6852..b84298bfd 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/advance/route.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/advance/route.tsx @@ -1,112 +1,104 @@ -import { useNavigate, useOutletContext } from '@remix-run/react'; -import { useEffect, useState } from 'react'; +import { useNavigate, useOutletContext, useParams } from '@remix-run/react'; +import { useState } from 'react'; import { Button } from '~/components/atoms/button'; import { DeleteContainer } from '~/console/components/common-console-components'; import { useAppState } from '~/console/page-components/app-states'; -import { keyconstants } from '~/console/server/r-utils/key-constants'; -import useForm from '~/root/lib/client/hooks/use-form'; -import Yup from '~/root/lib/server/helpers/yup'; import { parseName } from '~/console/server/r-utils/common'; import DeleteDialog from '~/console/components/delete-dialog'; import { useReload } from '~/root/lib/client/helpers/reloader'; import { useConsoleApi } from '~/console/server/gql/api-provider'; import { toast } from '~/components/molecule/toast'; import { handleError } from '~/root/lib/utils/common'; +import Wrapper from '~/console/components/wrapper'; +import { useUnsavedChanges } from '~/root/lib/client/hooks/use-unsaved-changes'; import { IAppContext } from '../../route'; const SettingAdvance = () => { - const { app, setApp } = useAppState(); + const { app } = useAppState(); const { environment, project } = useOutletContext(); const [deleteApp, setDeleteApp] = useState(false); const reload = useReload(); const api = useConsoleApi(); const navigate = useNavigate(); - - const { values, submit } = useForm({ - initialValues: { - name: parseName(app), - displayName: app.displayName, - description: app.metadata?.annotations?.[keyconstants.description] || '', - }, - validationSchema: Yup.object({ - name: Yup.string().required(), - displayName: Yup.string().required(), - description: Yup.string(), - }), - - onSubmit: async (val) => { - setApp((a) => { - return { - ...a, - metadata: { - ...a.metadata, - name: val.name, - namespace: environment.spec?.targetNamespace, - annotations: { - ...(a.metadata?.annotations || {}), - [keyconstants.description]: val.description, - }, - }, - displayName: val.displayName, - }; - }); - }, - }); - - useEffect(() => { - submit(); - }, [values]); + const { setPerformAction, hasChanges, loading } = useUnsavedChanges(); + const { account } = useParams(); return ( - <> -
-
-
Transfer
-
- Move your app to a different environment seamlessly, avoiding any - downtime or disruptions to workflows. -
-
-
-
-
- { - setDeleteApp(true); +
+ +
+ ), }} > - Permanently remove your application and all of its contents from the - “Lobster Early” project. This action is not reversible, so please - continue with caution. -
- { - try { - const { errors } = await api.deleteApp({ - appName: parseName(app), - envName: parseName(environment), - projectName: parseName(project), - }); +
+
+
Transfer
+
+ Move your app to a different environment seamlessly, avoiding any + downtime or disruptions to workflows. +
+
+
+
+
+ { + setDeleteApp(true); + }} + > + Permanently remove your application and all of its contents from the + “Lobster Early” project. This action is not reversible, so please + continue with caution. + + { + try { + const { errors } = await api.deleteApp({ + appName: parseName(app), + envName: parseName(environment), + projectName: parseName(project), + }); - if (errors) { - throw errors[0]; + if (errors) { + throw errors[0]; + } + reload(); + toast.success(`App deleted successfully`); + setDeleteApp(false); + navigate( + `/${account}/${parseName(project)}/${parseName( + environment + )}/apps/` + ); + } catch (err) { + handleError(err); } - reload(); - toast.success(`App deleted successfully`); - setDeleteApp(false); - navigate(`../`); - } catch (err) { - handleError(err); - } - }} - /> - + }} + /> + +
); }; export default SettingAdvance; diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx index 1ebb607e0..507c7277f 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx @@ -1,8 +1,10 @@ -import { useCallback, useEffect } from 'react'; -import { PasswordInput, TextInput } from '~/components/atoms/input'; -import Radio from '~/components/atoms/radio'; +import { useEffect } from 'react'; +import { + NumberInput, + PasswordInput, + TextInput, +} from '~/components/atoms/input'; import Slider from '~/components/atoms/slider'; -import ExtendedFilledTab from '~/console/components/extended-filled-tab'; import { TitleBox } from '~/console/components/raw-wrapper'; import { useAppState } from '~/console/page-components/app-states'; import { keyconstants } from '~/console/server/r-utils/key-constants'; @@ -11,25 +13,76 @@ import Yup from '~/root/lib/server/helpers/yup'; import { InfoLabel } from '~/console/components/commons'; import { FadeIn, parseValue } from '~/console/page-components/util'; -import { IcpuMode, plans } from '../../../../new-app/datas'; +import Select from '~/components/atoms/select'; +import ExtendedFilledTab from '~/console/components/extended-filled-tab'; +import Wrapper from '~/console/components/wrapper'; +import { useUnsavedChanges } from '~/root/lib/client/hooks/use-unsaved-changes'; +import { Button } from '~/components/atoms/button'; +import { plans } from '../../../../new-app/datas'; + +const valueRender = ({ + label, + isShared, + memoryPerCpu, +}: { + label: string; + isShared: boolean; + memoryPerCpu: number; +}) => { + return ( +
+ + {label}-{isShared ? 'Shared' : 'Dedicated'} + + + {memoryPerCpu}GB/vCPU + +
+ ); +}; const SettingCompute = () => { - const { app, setApp, getContainer } = useAppState(); - const { values, errors, handleChange, submit } = useForm({ + const { app, setApp, getContainer, activeContIndex } = useAppState(); + const { setPerformAction, hasChanges, loading } = useUnsavedChanges(); + + const { values, errors, handleChange, submit, resetValues } = useForm({ initialValues: { imageUrl: getContainer(0)?.image || '', pullSecret: 'TODO', cpuMode: app.metadata?.annotations?.[keyconstants.cpuMode] || 'shared', + memPerCpu: app.metadata?.annotations?.[keyconstants.memPerCpu] || 1, + + cpu: parseValue( + app.spec.containers[activeContIndex]?.resourceCpu?.max, + 250 + ), + selectedPlan: - app.metadata?.annotations?.[keyconstants.selectedPlan] || '4', - cpu: parseValue(getContainer(0)?.resourceCpu?.max, 250), + app.metadata?.annotations[keyconstants.selectedPlan] || 'shared-1', + selectionMode: + app.metadata?.annotations[keyconstants.selectionModeKey] || 'quick', + manualCpuMin: parseValue( + app.spec.containers[activeContIndex].resourceCpu?.min, + 0 + ), + manualCpuMax: parseValue( + app.spec.containers[activeContIndex].resourceCpu?.max, + 0 + ), + manualMemMin: parseValue( + app.spec.containers[activeContIndex].resourceMemory?.min, + 0 + ), + manualMemMax: parseValue( + app.spec.containers[activeContIndex].resourceMemory?.max, + 0 + ), }, validationSchema: Yup.object({ imageUrl: Yup.string().required(), pullSecret: Yup.string(), cpuMode: Yup.string().required(), selectedPlan: Yup.string().required(), - cpu: Yup.number().required().min(100).max(8000), }), onSubmit: (val) => { setApp((s) => ({ @@ -49,16 +102,28 @@ const SettingCompute = () => { ...(s.spec.containers?.[0] || {}), image: val.imageUrl, name: 'container-0', - resourceCpu: { - min: `${val.cpu}m`, - max: `${val.cpu}m`, - }, - resourceMemory: { - min: `${val.cpu}Mi`, - max: `${( - (values.cpu || 1) * parseValue(values.selectedPlan, 4) - ).toFixed(2)}Mi`, - }, + resourceCpu: + val.selectionMode === 'quick' + ? { + max: `${val.cpu}m`, + min: `${val.cpu}m`, + } + : { + max: `${val.manualCpuMax}m`, + min: `${val.manualCpuMin}m`, + }, + resourceMemory: + val.selectionMode === 'quick' + ? { + max: `${( + (values.cpu || 1) * parseValue(values.memPerCpu, 4) + ).toFixed(2)}Mi`, + min: `${val.cpu}Mi`, + } + : { + max: `${val.manualMemMax}Mi`, + min: `${val.manualMemMin}Mi`, + }, }, ], }, @@ -76,40 +141,58 @@ const SettingCompute = () => { submit(); }, [values]); + useEffect(() => { + if (!hasChanges) { + resetValues(); + } + }, [hasChanges]); + return ( - { - e.preventDefault(); - }} - > - -
- - } - size="lg" - value={values.imageUrl} - onChange={handleChange('imageUrl')} - error={!!errors.imageUrl} - message={errors.imageUrl} - /> - - } - size="lg" - value={values.pullSecret} - // error={!!errors.pullSecret} - // message={errors.pullSecret} - // onChange={handleChange('pullSecret')} - /> -
- {/*
+
+ +
+ ), + }} + > +
+ + } + size="lg" + value={values.imageUrl} + onChange={handleChange('imageUrl')} + error={!!errors.imageUrl} + message={errors.imageUrl} + /> + {/* + } + size="lg" + value={values.pullSecret} + // error={!!errors.pullSecret} + // message={errors.pullSecret} + // onChange={handleChange('pullSecret')} + /> */} +
+ {/*
Select plan @@ -207,7 +290,138 @@ const SettingCompute = () => { )}
*/} -
+
+
+
+ { + handleChange('selectionMode')(dummyEvent(e)); + }} + items={[ + { label: 'Quick', value: 'quick' }, + { + label: 'Manual', + value: 'manual', + }, + ]} + /> +
+
+ {values.selectionMode === 'quick' ? ( +
+ { + handleChange('selectedService')(dummyEvent(val)); + handleChange('selectedResource')(dummyEvent(undefined)); + }} + options={async () => [ + ...services.map((mt) => ({ + label: mt.displayName, + value: parseName(mt), + service: mt, + })), + ]} + error={!!errors.selectedService} + message={errors.selectedService} + /> + [...apps]} + onChange={(val) => { + handleChange('app')(dummyEvent(val.value)); + setSelectedApp(val.app); + }} + error={!!errors.app || !!appLoadingError} + message={appLoadingError ? 'Error fetching apps.' : errors.app} + loading={appLoading} + /> + [...domains]} + onChange={(val) => { + setSelectedDomains(val); + handleChange('domains')(dummyEvent([...val.map((v) => v.value)])); + }} + error={!!errors.domains || !!domainLoadingError} + message={ + errors.domains || + (domainLoadingError ? 'Error fetching domains.' : '') + } + loading={domainLoading} + disableWhileLoading + /> + + + + + + + ); +}; + +const HandleRouter = (props: IDialog) => { + return ( + + ); +}; +export default HandleRouter; diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/router-resources.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/router-resources.tsx new file mode 100644 index 000000000..bdb4c66ce --- /dev/null +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/router-resources.tsx @@ -0,0 +1,230 @@ +import { Trash, PencilLine } from '@jengaicons/react'; +import { useState } from 'react'; +import { toast } from '~/components/molecule/toast'; +import { generateKey, titleCase } from '~/components/utils'; +import { + ListBody, + ListItem, + ListTitle, + listFlex, + listTitleClass, +} from '~/console/components/console-list-components'; +import DeleteDialog from '~/console/components/delete-dialog'; +import Grid from '~/console/components/grid'; +import List from '~/console/components/list'; +import ListGridView from '~/console/components/list-grid-view'; +import ResourceExtraAction from '~/console/components/resource-extra-action'; +import { useConsoleApi } from '~/console/server/gql/api-provider'; +import { + ExtractNodeType, + parseName, + parseUpdateOrCreatedBy, + parseUpdateOrCreatedOn, +} from '~/console/server/r-utils/common'; +import { useReload } from '~/root/lib/client/helpers/reloader'; +import { handleError } from '~/root/lib/utils/common'; +import { IRouters } from '~/console/server/gql/queries/router-queries'; +import { Link, useParams } from '@remix-run/react'; +import { listStatus } from '~/console/components/sync-status'; +import HandleRouter from './handle-router'; + +const RESOURCE_NAME = 'domain'; +type BaseType = ExtractNodeType; + +const parseItem = (item: BaseType) => { + return { + name: item.displayName, + id: parseName(item), + updateInfo: { + author: `Updated by ${parseUpdateOrCreatedBy(item)}`, + time: parseUpdateOrCreatedOn(item), + }, + }; +}; + +type OnAction = ({ + action, + item, +}: { + action: 'edit' | 'delete' | 'detail'; + item: BaseType; +}) => void; + +type IExtraButton = { + onAction: OnAction; + item: BaseType; +}; + +const ExtraButton = ({ onAction, item }: IExtraButton) => { + return ( + , + type: 'item', + onClick: () => onAction({ action: 'edit', item }), + key: 'edit', + }, + { + label: 'Delete', + icon: , + type: 'item', + onClick: () => onAction({ action: 'delete', item }), + key: 'delete', + className: '!text-text-critical', + }, + ]} + /> + ); +}; + +interface IResource { + items: BaseType[]; + onAction: OnAction; +} + +const GridView = ({ items, onAction }: IResource) => { + const { account, project, environment } = useParams(); + return ( + + {items.map((item, index) => { + const { name, id, updateInfo } = parseItem(item); + const keyPrefix = `${RESOURCE_NAME}-${id}-${index}`; + return ( + ( + } + /> + ), + }, + { + key: generateKey(keyPrefix, updateInfo.author), + render: () => ( + + ), + }, + ]} + /> + ); + })} + + ); +}; + +const ListView = ({ items, onAction }: IResource) => { + const { account, project, environment } = useParams(); + return ( + + {items.map((item, index) => { + const { name, id, updateInfo } = parseItem(item); + const keyPrefix = `${RESOURCE_NAME}-${id}-${index}`; + const status = listStatus({ key: `${keyPrefix}status`, item }); + return ( + , + }, + status, + listFlex({ key: 'flex-1' }), + { + key: generateKey(keyPrefix, updateInfo.author), + className: 'w-[180px]', + render: () => ( + + ), + }, + { + key: generateKey(keyPrefix, 'action'), + render: () => , + }, + ]} + /> + ); + })} + + ); +}; + +const RouterResources = ({ items = [] }: { items: BaseType[] }) => { + const [showDeleteDialog, setShowDeleteDialog] = useState( + null + ); + const [visible, setVisible] = useState(null); + const api = useConsoleApi(); + const reloadPage = useReload(); + + const props: IResource = { + items, + onAction: ({ action, item }) => { + switch (action) { + case 'edit': + setVisible(item); + break; + case 'delete': + setShowDeleteDialog(item); + break; + default: + } + }, + }; + return ( + <> + } + gridView={} + /> + { + try { + // const { errors } = await api.deleteDomain({ + // domainName: showDeleteDialog!.domainName, + // }); + + // if (errors) { + // throw errors[0]; + // } + // reloadPage(); + // toast.success(`${titleCase(RESOURCE_NAME)} deleted successfully`); + setShowDeleteDialog(null); + } catch (err) { + handleError(err); + } + }} + /> + setVisible(null), + }} + /> + + ); +}; + +export default RouterResources; diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/tools.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/tools.tsx index ff2066253..ec2a33565 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/tools.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/tools.tsx @@ -1,6 +1,5 @@ import { useSearchParams } from '@remix-run/react'; import { useMemo } from 'react'; -import { toast } from 'react-toastify'; import CommonTools from '~/console/components/common-tools'; const Tools = () => { @@ -13,7 +12,6 @@ const Tools = () => { type: 'text', search: false, dataFetcher: async () => { - toast.info(`todo status`); return [ { content: 'Active', value: 'active' }, { content: 'Freezed', value: 'freezed' }, diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/settings+/general/route.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/settings+/general/route.tsx index 69b9b80a4..9e36fe076 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/settings+/general/route.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/settings+/general/route.tsx @@ -1,6 +1,6 @@ import { CopySimple } from '@jengaicons/react'; -import { useOutletContext } from '@remix-run/react'; -import { useEffect } from 'react'; +import { useNavigate, useOutletContext } from '@remix-run/react'; +import { useEffect, useState } from 'react'; import { Button } from '~/components/atoms/button'; import { TextInput } from '~/components/atoms/input'; import { toast } from '~/components/molecule/toast'; @@ -19,6 +19,8 @@ import { consoleBaseUrl } from '~/root/lib/configs/base-url.cjs'; import Yup from '~/root/lib/server/helpers/yup'; import { handleError } from '~/root/lib/utils/common'; import { IEnvironment } from '~/console/server/gql/queries/environment-queries'; +import DeleteDialog from '~/console/components/delete-dialog'; +import { useReload } from '~/root/lib/client/helpers/reloader'; import { IEnvironmentContext } from '../../_layout'; export const updateEnvironment = async ({ @@ -51,8 +53,11 @@ const EnvironmentSettingsGeneral = () => { useOutletContext(); const { setHasChanges, resetAndReload } = useUnsavedChanges(); + const [deleteEnvironment, setDeleteEnvironment] = useState(false); const api = useConsoleApi(); + const reload = useReload(); + const navigate = useNavigate(); const { copy } = useClipboard({ onSuccess() { @@ -166,11 +171,42 @@ const EnvironmentSettingsGeneral = () => {
- {}}> - Permanently remove your Environment and all of its contents from the - Kloudlite platform. This action is not reversible — please continue with - caution. + { + setDeleteEnvironment(true); + }} + > + Permanently remove your environment and all of its contents from the “ + {environment.displayName}” environment. This action is not reversible, + so please continue with caution. + { + try { + const { errors } = await api.deleteEnvironment({ + envName: parseName(environment), + projectName: parseName(project), + }); + + if (errors) { + throw errors[0]; + } + reload(); + toast.success(`Environment deleted successfully`); + setDeleteEnvironment(false); + navigate( + `/${parseName(account)}/${parseName(project)}/environments/` + ); + } catch (err) { + handleError(err); + } + }} + /> ); }; diff --git a/src/apps/console/routes/_main+/$account+/$project+/_index.tsx b/src/apps/console/routes/_main+/$account+/$project+/_index.tsx index 3f1d02b16..d28076b69 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/_index.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/_index.tsx @@ -2,6 +2,6 @@ import { redirect } from '@remix-run/node'; import { IRemixCtx } from '~/root/lib/types/common'; export const loader = async (ctx: IRemixCtx) => { - const { account, project, cluster } = ctx.params; - return redirect(`/${account}/${cluster}/${project}/environments`); + const { account, project } = ctx.params; + return redirect(`/${account}/${project}/environments`); }; diff --git a/src/apps/console/routes/_main+/$account+/$project+/_layout.tsx b/src/apps/console/routes/_main+/$account+/$project+/_layout.tsx index d8625b0f5..bc1e8c447 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/_layout.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/_layout.tsx @@ -3,7 +3,6 @@ import { Link, Outlet, useLoaderData, - useNavigate, useOutletContext, useParams, } from '@remix-run/react'; @@ -21,104 +20,93 @@ import { } from '~/console/server/utils/auth-utils'; import { GQLServerHandler } from '~/console/server/gql/saved-queries'; import Breadcrum from '~/console/components/breadcrum'; -import { - Database, - GearSix, - VirtualMachine, - InfraAsCode, - Container as ContainerIcon, - Project as ProjectIcon, -} from '@jengaicons/react'; +import { Database, GearSix, VirtualMachine } from '@jengaicons/react'; import { ExtractNodeType, parseName } from '~/console/server/r-utils/common'; -import MenuSelect from '~/console/components/menu-select'; import LogoWrapper from '~/console/components/logo-wrapper'; import { BrandLogo } from '~/components/branding/brand-logo'; import { BreadcrumButtonContent, BreadcrumSlash, + tabIconSize, } from '~/console/utils/commons'; -import { IClusterContext } from '../infra+/$cluster+/_layout'; +import { useActivePath } from '~/root/lib/client/hooks/use-active-path'; +import { cn } from '~/components/utils'; +import { IMSvTemplates } from '~/console/server/gql/queries/managed-templates-queries'; +import { IAccountContext } from '../_layout'; -export interface IProjectContext extends IClusterContext { +export interface IProjectContext extends IAccountContext { project: IProject; + msvtemplates: IMSvTemplates; } +const iconSize = tabIconSize; +const tabs = [ + { + label: ( + + + Environments + + ), + to: '/environments', + value: '/environments', + }, + { + label: ( + + + Managed services + + ), + to: '/managed-services', + value: '/managed-services', + }, + + { + label: ( + + + Settings + + ), + to: '/settings/general', + value: '/settings', + }, +]; const Project = () => { - const rootContext = useOutletContext(); - const { project } = useLoaderData(); + const rootContext = useOutletContext(); + const { project, msvtemplates } = useLoaderData(); return ( - + ); }; -const ProjectsDropdown = () => { - const navigate = useNavigate(); - const { account } = useParams(); - const iconSize = 14; - - const menuItems = [ - { - label: ( - - - Projects - - ), - value: `/${account}/projects`, - }, - { - label: ( - - - Infrastructure - - ), - value: `/${account}/infra`, - }, - { - label: ( - - - Packages - - ), - value: `/${account}/packages`, - }, - ]; - return ( - navigate(value)} - trigger={ - } - /> - } - /> - ); -}; - const LocalBreadcrum = ({ project, }: { project: ExtractNodeType; }) => { - const iconSize = 14; - const { account, cluster } = useParams(); + const { account } = useParams(); + const { activePath } = useActivePath({ + parent: `/${account}/${parseName(project)}`, + }); + return (
- {/* */} +
tab.to === activePath) ? 'bodyMd-semibold' : '' + )} + > - {/* */}
} /> @@ -126,47 +114,10 @@ const LocalBreadcrum = ({ ); }; -const ProjectTabs = () => { +const Tabs = () => { const { account, project } = useParams(); - const iconSize = 16; - return ( - - - Environments - - ), - to: '/environments', - value: '/environments', - }, - { - label: ( - - - Managed services - - ), - to: '/managed-services', - value: '/managed-services', - }, - { - label: ( - - - Settings - - ), - to: '/settings/general', - value: '/settings', - }, - ]} - /> - ); + return ; }; const Logo = () => { @@ -184,7 +135,7 @@ export const handle = ({ project: ExtractNodeType; }) => { return { - navbar: , + navbar: , breadcrum: () => , logo: , }; @@ -193,20 +144,31 @@ export const handle = ({ export const loader = async (ctx: IRemixCtx) => { ensureAccountSet(ctx); ensureClusterSet(ctx); - const { account, project, cluster } = ctx.params; + const { account, project } = ctx.params; try { const { data, errors } = await GQLServerHandler(ctx.request).getProject({ name: project, }); + + const { data: msvTemplates, errors: msvError } = await GQLServerHandler( + ctx.request + ).listMSvTemplates({}); + if (errors) { throw errors[0]; } + + if (msvError) { + throw msvError[0]; + } + return { project: data || {}, + msvtemplates: msvTemplates || {}, }; } catch (err) { logger.log(err); - return redirect(`/${account}/${cluster}/projects`); + return redirect(`/${account}/projects`); } }; diff --git a/src/apps/console/routes/_main+/$account+/$project+/environments/clone-environment.tsx b/src/apps/console/routes/_main+/$account+/$project+/environments/clone-environment.tsx new file mode 100644 index 000000000..4045c417b --- /dev/null +++ b/src/apps/console/routes/_main+/$account+/$project+/environments/clone-environment.tsx @@ -0,0 +1,121 @@ +/* eslint-disable react/destructuring-assignment */ +import { useParams } from '@remix-run/react'; +import { Switch } from '~/components/atoms/switch'; +import Popup from '~/components/molecule/popup'; +import { toast } from '~/components/molecule/toast'; +import CommonPopupHandle from '~/console/components/common-popup-handle'; +import { NameIdView } from '~/console/components/name-id-view'; +import { IDialogBase } from '~/console/components/types.d'; +import { useConsoleApi } from '~/console/server/gql/api-provider'; +import { IEnvironments } from '~/console/server/gql/queries/environment-queries'; +import { ExtractNodeType, parseName } from '~/console/server/r-utils/common'; +import { useReload } from '~/root/lib/client/helpers/reloader'; +import useForm, { dummyEvent } from '~/root/lib/client/hooks/use-form'; +import Yup from '~/root/lib/server/helpers/yup'; +import { handleError } from '~/root/lib/utils/common'; + +type IDialog = IDialogBase>; + +const Root = (props: IDialog) => { + const { isUpdate, setVisible } = props; + const api = useConsoleApi(); + const reloadPage = useReload(); + + const { project } = useParams(); + + const { values, errors, handleChange, handleSubmit, resetValues, isLoading } = + useForm({ + initialValues: { + name: '', + displayName: '', + environmentRoutingMode: false, + isNameError: false, + }, + validationSchema: Yup.object({ + name: Yup.string().required('Name is required.'), + displayName: Yup.string().required(), + }), + onSubmit: async (val) => { + if (isUpdate) { + if (!project) { + throw new Error('Project is required!.'); + } + try { + const { errors: e } = await api.cloneEnvironment({ + displayName: val.displayName, + environmentRoutingMode: val.environmentRoutingMode + ? 'public' + : 'private', + destinationEnvName: val.name, + projectName: project, + sourceEnvName: parseName(props.data), + }); + if (e) { + throw e[0]; + } + resetValues(); + toast.success('Environment cloned successfully'); + setVisible(false); + reloadPage(); + } catch (err) { + handleError(err); + } + } + }, + }); + return ( + { + if (!values.isNameError) { + handleSubmit(e); + } else { + e.preventDefault(); + } + }} + > + +
+ + { + handleChange('environmentRoutingMode')(dummyEvent(val)); + }} + /> +
+
+ + + + +
+ ); +}; + +const CloneEnvironment = (props: IDialog) => { + return ( + + ); +}; + +export default CloneEnvironment; diff --git a/src/apps/console/routes/_main+/$account+/$project+/environments/resources.tsx b/src/apps/console/routes/_main+/$account+/$project+/environments/resources.tsx index a72e82413..e3eb871b1 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/environments/resources.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/environments/resources.tsx @@ -1,16 +1,18 @@ -import { DotsThreeVerticalFill } from '@jengaicons/react'; +import { Copy, GearSix } from '@jengaicons/react'; import { Link, useParams } from '@remix-run/react'; -import { IconButton } from '~/components/atoms/button'; +import { useState } from 'react'; import { generateKey, titleCase } from '~/components/utils'; import ConsoleAvatar from '~/console/components/console-avatar'; import { - ListBody, ListItem, ListTitle, + listFlex, + listTitleClass, } from '~/console/components/console-list-components'; import Grid from '~/console/components/grid'; import List from '~/console/components/list'; import ListGridView from '~/console/components/list-grid-view'; +import ResourceExtraAction from '~/console/components/resource-extra-action'; import { IEnvironments } from '~/console/server/gql/queries/environment-queries'; import { ExtractNodeType, @@ -18,6 +20,8 @@ import { parseUpdateOrCreatedBy, parseUpdateOrCreatedOn, } from '~/console/server/r-utils/common'; +import { listStatus } from '~/console/components/sync-status'; +import CloneEnvironment from './clone-environment'; const RESOURCE_NAME = 'workspace'; type BaseType = ExtractNodeType; @@ -33,7 +37,43 @@ const parseItem = (item: BaseType) => { }; }; -const GridView = ({ items = [] }: { items: BaseType[] }) => { +type OnAction = ({ action, item }: { action: 'clone'; item: BaseType }) => void; + +type IExtraButton = { + onAction: OnAction; + item: BaseType; +}; + +const ExtraButton = ({ item, onAction }: IExtraButton) => { + const { account, project } = useParams(); + return ( + , + type: 'item', + key: 'clone', + onClick: () => onAction({ action: 'clone', item }), + }, + { + label: 'Settings', + icon: , + type: 'item', + to: `/${account}/${project}/${parseName(item)}/settings/general`, + key: 'settings', + }, + ]} + /> + ); +}; + +interface IResource { + items: BaseType[]; + onAction: OnAction; +} + +const GridView = ({ items = [], onAction }: IResource) => { const { account, project } = useParams(); return ( @@ -51,13 +91,7 @@ const GridView = ({ items = [] }: { items: BaseType[] }) => { } - variant="plain" - onClick={(e) => e.stopPropagation()} - /> - } + action={} avatar={} /> ), @@ -79,7 +113,7 @@ const GridView = ({ items = [] }: { items: BaseType[] }) => { ); }; -const ListView = ({ items }: { items: BaseType[] }) => { +const ListView = ({ items, onAction }: IResource) => { const { account, project } = useParams(); return ( @@ -87,6 +121,7 @@ const ListView = ({ items }: { items: BaseType[] }) => { {items.map((item, index) => { const { name, id, updateInfo } = parseItem(item); const keyPrefix = `${RESOURCE_NAME}-${id}-${index}`; + const status = listStatus({ key: `${keyPrefix}status`, item }); return ( { columns={[ { key: generateKey(keyPrefix, name + id), - className: 'flex-1', + className: listTitleClass, render: () => ( { /> ), }, + status, + listFlex({ key: `${keyPrefix}flex-1` }), { key: generateKey(keyPrefix, updateInfo.author), className: 'w-[180px]', @@ -116,13 +153,7 @@ const ListView = ({ items }: { items: BaseType[] }) => { }, { key: generateKey(keyPrefix, 'action'), - render: () => ( - } - variant="plain" - onClick={(e) => e.stopPropagation()} - /> - ), + render: () => , }, ]} /> @@ -133,11 +164,37 @@ const ListView = ({ items }: { items: BaseType[] }) => { }; const Resources = ({ items = [] }: { items: BaseType[] }) => { + const [visible, setVisible] = useState(null); + const props: IResource = { + items, + onAction: ({ action, item }) => { + switch (action) { + case 'clone': + setVisible(item); + break; + default: + break; + } + }, + }; + return ( - } - gridView={} - /> + <> + } + gridView={} + /> + { + setVisible(null); + }, + data: visible!, + }} + /> + ); }; diff --git a/src/apps/console/routes/_main+/$account+/$project+/environments/route.tsx b/src/apps/console/routes/_main+/$account+/$project+/environments/route.tsx index 718af6de1..2859256fc 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/environments/route.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/environments/route.tsx @@ -58,6 +58,7 @@ const Workspaces = () => { > {({ environmentData }) => { // console.log('environment: ', environmentData); + console.log(environmentData); const environments = parseNodes(environmentData); diff --git a/src/apps/console/routes/_main+/$account+/$project+/managed-services/backend-services-resources.tsx b/src/apps/console/routes/_main+/$account+/$project+/managed-services/backend-services-resources.tsx index 1fd3bf941..1e684decf 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/managed-services/backend-services-resources.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/managed-services/backend-services-resources.tsx @@ -1,13 +1,14 @@ -import { Trash } from '@jengaicons/react'; +import { PencilSimple, Trash } from '@jengaicons/react'; import { generateKey, titleCase } from '~/components/utils'; import { ListItem, ListTitle, + listFlex, + listTitleClass, } from '~/console/components/console-list-components'; import Grid from '~/console/components/grid'; import List from '~/console/components/list'; import ListGridView from '~/console/components/list-grid-view'; -import { IClusterMSvs } from '~/console/server/gql/queries/cluster-managed-services-queries'; import { ExtractNodeType, parseName, @@ -24,9 +25,12 @@ import { useState } from 'react'; import { handleError } from '~/root/lib/utils/common'; import { toast } from '~/components/molecule/toast'; import { useParams } from '@remix-run/react'; +import { IProjectMSvs } from '~/console/server/gql/queries/project-managed-services-queries'; +import { listStatus } from '~/console/components/sync-status'; +import HandleBackendService from './handle-backend-service'; const RESOURCE_NAME = 'managed service'; -type BaseType = ExtractNodeType; +type BaseType = ExtractNodeType; const parseItem = (item: BaseType, templates: IMSvTemplates) => { const template = getManagedTemplate({ @@ -49,7 +53,7 @@ type OnAction = ({ action, item, }: { - action: 'delete'; + action: 'delete' | 'edit'; item: BaseType; }) => void; @@ -62,6 +66,13 @@ const ExtraButton = ({ onAction, item }: IExtraButton) => { return ( , + type: 'item', + onClick: () => onAction({ action: 'edit', item }), + key: 'edit', + }, { label: 'Delete', icon: , @@ -127,7 +138,7 @@ const ListView = ({ items = [], templates = [], onAction }: IResource) => { {items.map((item, index) => { const { name, id, logo, updateInfo } = parseItem(item, templates); const keyPrefix = `${RESOURCE_NAME}-${id}-${index}`; - + const status = listStatus({ key: `${keyPrefix}status`, item }); return ( { columns={[ { key: generateKey(keyPrefix, name), - className: 'flex-1', + className: listTitleClass, render: () => ( { /> ), }, + status, + listFlex({ key: `${keyPrefix}flex-1` }), { key: generateKey(keyPrefix, 'author'), className: 'w-[180px]', @@ -180,6 +193,7 @@ const BackendServicesResources = ({ const [showDeleteDialog, setShowDeleteDialog] = useState( null ); + const [visible, setVisible] = useState(null); const api = useConsoleApi(); const reloadPage = useReload(); const params = useParams(); @@ -192,6 +206,9 @@ const BackendServicesResources = ({ case 'delete': setShowDeleteDialog(item); break; + case 'edit': + setVisible(item); + break; default: break; } @@ -209,10 +226,13 @@ const BackendServicesResources = ({ show={showDeleteDialog} setShow={setShowDeleteDialog} onSubmit={async () => { + if (!params.project) { + throw new Error('Project is required!.'); + } try { - const { errors } = await api.deleteClusterMSv({ - serviceName: parseName(showDeleteDialog), - clusterName: params.cluster || '', + const { errors } = await api.deleteProjectMSv({ + pmsvcName: parseName(showDeleteDialog), + projectName: params.project, }); if (errors) { @@ -226,6 +246,15 @@ const BackendServicesResources = ({ } }} /> + setVisible(null), + data: visible!, + templates: templates || [], + }} + /> ); }; diff --git a/src/apps/console/routes/_main+/$account+/$project+/managed-services/handle-backend-service.tsx b/src/apps/console/routes/_main+/$account+/$project+/managed-services/handle-backend-service.tsx index 9a3425cbc..e35a21563 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/managed-services/handle-backend-service.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/managed-services/handle-backend-service.tsx @@ -2,7 +2,7 @@ /* eslint-disable react/destructuring-assignment */ import { ArrowRight, Search, Check } from '@jengaicons/react'; import { useParams } from '@remix-run/react'; -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import ActionList from '~/components/atoms/action-list'; import { NumberInput, TextInput } from '~/components/atoms/input'; import Popup from '~/components/molecule/popup'; @@ -13,8 +13,7 @@ import List from '~/console/components/list'; import NoResultsFound from '~/console/components/no-results-found'; import { IDialogBase } from '~/console/components/types.d'; import { useConsoleApi } from '~/console/server/gql/api-provider'; -import { IClusterMSvs } from '~/console/server/gql/queries/cluster-managed-services-queries'; -import { ExtractNodeType } from '~/console/server/r-utils/common'; +import { ExtractNodeType, parseName } from '~/console/server/r-utils/common'; import { useReload } from '~/root/lib/client/helpers/reloader'; import { useInputSearch } from '~/root/lib/client/helpers/search-filter'; import useForm, { dummyEvent } from '~/root/lib/client/hooks/use-form'; @@ -24,240 +23,56 @@ import { handleError } from '~/root/lib/utils/common'; import { IMSvTemplates } from '~/console/server/gql/queries/managed-templates-queries'; import { Switch } from '~/components/atoms/switch'; import { cn } from '~/components/utils'; +import { IProjectMSvs } from '~/console/server/gql/queries/project-managed-services-queries'; +import { getManagedTemplate } from '~/console/utils/commons'; +import { NameIdView } from '~/console/components/name-id-view'; -type IDialog = IDialogBase> & { +type IDialog = IDialogBase> & { templates: IMSvTemplates; }; -type IActiveCategory = { - name: string; - displayName: string; -} | null; - type ISelectedService = { category: { name: string; displayName: string; }; - service: NN[number]['items'][number]; + service?: NN[number]['items'][number]; } | null; -const ServicePicker = ({ - activeCategory, - setActiveCategory, - templates, - selectedService, - setSelectedService, -}: { - activeCategory: IActiveCategory; - setActiveCategory: React.Dispatch; - templates: IMSvTemplates; - selectedService: ISelectedService; - setSelectedService: React.Dispatch; -}) => { - const [searchProps, searchResults] = useInputSearch( - { - data: - templates?.find((t) => t.category === activeCategory?.name)?.items || - [], - keys: ['name'], - reverse: false, - threshold: 0.4, - }, - [activeCategory] - ); - return ( -
-
- { - setActiveCategory({ - name: v, - displayName: - templates?.find((t) => t.category === v)?.displayName || '', - }); - }} - > - {templates?.map((t, index) => { - if (!activeCategory && index === 0) { - setActiveCategory({ - name: t.category, - displayName: t.displayName, - }); - } - return ( - - {t.displayName} - - ); - })} - -
-
- } - placeholder="Search" - /> - {templates?.find((t) => t.category === activeCategory?.name)?.items - .length === 0 ? ( - } - subtitle="No Services Available now" - /> - ) : ( - - {searchResults.map((item) => { - return ( - { - if (!item.apiVersion) { - toast.error('not available now'); - return; - } - if (activeCategory) { - setSelectedService({ - category: activeCategory, - service: item, - }); - } - }} - columns={[ - { - key: item.name, - className: 'flex-grow', - render: () => ( - - } - title={ -
{item.displayName}
- } - /> - ), - }, - { - key: `${item.name}-arrow`, - render: () => - selectedService?.service.name === item.name ? ( - - ) : ( -
- -
- ), - }, - ]} - /> - ); - })} -
- )} - - {/* - {searchResults.map((item) => { - return ( - { - if (!item.apiVersion) { - toast.error('not available now'); - return; - } - if (activeCategory) { - setSelectedService({ - category: activeCategory, - service: item, - }); - } - }} - key={item.name} - className={cn({ - '!bg-surface-basic-active': - item.name === selectedService?.service.name, - 'opacity-50 cursor-not-allowed': !item.apiVersion, - })} - rows={[ - { - key: `${item.name}first`, - render() { - return ( -
- {item.displayName} - -
-
- {item.displayName} -
-
-
- ); - }, - }, - ]} - /> - ); - })} -
*/} -
-
- ); -}; - const RenderField = ({ field, value, onChange, - error, - message, + errors, + fieldKey, }: { - field: NN['service']['fields'][number]; + field: NN['service']>['fields'][number]; onChange: (e: string) => (e: { target: { value: any } }) => void; value: any; - error: boolean; - message?: string; + errors: { + [key: string]: string | undefined; + }; + fieldKey: string; }) => { - console.log('value', value); - const [qos, setQos] = useState(false); + + useEffect(() => { + if (field.inputType === 'Resource' && value.max === value.min) { + setQos(true); + } + }, []); + if (field.inputType === 'Number') { return ( { - onChange(`${field.name}`)( + onChange(`res.${field.name}`)( dummyEvent( `${parseFloat(target.value) * (field.multiplier || 1)}${ field.unit @@ -274,9 +89,11 @@ const RenderField = ({ return ( ); } @@ -290,14 +107,12 @@ const RenderField = ({
{ - onChange(`${field.name}.min`)( + onChange(`res.${field.name}.min`)( dummyEvent( `${parseFloat(target.value) * (field.multiplier || 1)}${ field.unit @@ -305,7 +120,7 @@ const RenderField = ({ ) ); if (qos) { - onChange(`${field.name}.max`)( + onChange(`res.${field.name}.max`)( dummyEvent( `${parseFloat(target.value) * (field.multiplier || 1)}${ field.unit @@ -320,14 +135,12 @@ const RenderField = ({ {!qos && (
{ - onChange(`${field.name}.max`)( + onChange(`res.${field.name}.max`)( dummyEvent( `${parseFloat(target.value) * (field.multiplier || 1)}${ field.unit @@ -347,7 +160,7 @@ const RenderField = ({ onChange={(_value) => { setQos(_value); if (_value) { - onChange(`${field.name}.max`)(dummyEvent(`${value.min}`)); + onChange(`res.${field.name}.max`)(dummyEvent(`${value.min}`)); } }} /> @@ -372,41 +185,47 @@ const Fill = ({ [key: string]: string | undefined; }; }) => { - console.log('values: ', values); - + const nameRef = useRef(null); + useEffect(() => { + nameRef.current?.focus(); + }, [nameRef.current]); return (
- - { - handleChange('name')(dummyEvent(v)); - }} + resType="project_managed_service" + name={values.name} + displayName={values.displayName} + errors={errors.name} + handleChange={handleChange} + nameErrorLabel="isNameError" /> - {selectedService?.service.fields.map((field) => { + + {selectedService?.service?.fields.map((field) => { const k = field.name; const x = k.split('.').reduce((acc, curr) => { + console.log(acc, curr, values); + if (!acc) { - return values[curr]; + return values.res[curr]; } + return acc[curr]; }, null); + console.log('x', x); + return ( ); })} @@ -414,196 +233,124 @@ const Fill = ({ ); }; -const flatM = (obj: Record) => { - const flatJson = {}; - for (const key in obj) { - const parts = key.split('.'); - let temp: Record = flatJson; - - parts.forEach((part, index) => { - if (index === parts.length - 1) { - temp[part] = { - min: null, - max: null, - }; - } else { - temp[part] = temp[part] || {}; - } - temp = temp[part]; - }); - } - console.log('flatjson ', flatJson); - - return flatJson; -}; - const Root = (props: IDialog) => { const { isUpdate, setVisible, templates } = props; - const [activeCategory, setActiveCategory] = useState(null); const [selectedService, setSelectedService] = useState(null); const api = useConsoleApi(); const reload = useReload(); - const { cluster } = useParams(); + const { project } = useParams(); const [step, setStep] = useState<'choose' | 'fill'>('choose'); - console.log(selectedService); - - const { - values, - errors, - handleChange, - handleSubmit, - resetValues, - isLoading, - setValues, - } = useForm({ - initialValues: { - name: '', - displayName: '', - }, - validationSchema: Yup.object({}), - onSubmit: async (val) => { - const tempVal = { ...val }; - // @ts-ignore - delete tempVal.name; - // @ts-ignore - delete tempVal.displayName; - - try { - if (!cluster) { - throw new Error('Cluster not found.'); - } - if ( - !selectedService?.service.apiVersion || - !selectedService.service.kind - ) { - throw new Error('Service apiversion or kind error.'); - } - const { errors: e } = await api.createClusterMSv({ - clusterName: cluster, - service: { - displayName: val.displayName, - metadata: { - name: val.name, + const { values, errors, handleChange, handleSubmit, resetValues, isLoading } = + useForm({ + initialValues: isUpdate + ? { + name: parseName(props.data), + displayName: props.data.displayName, + isNameError: false, + res: { + ...props.data.spec?.msvcSpec.serviceTemplate.spec, }, + } + : { + name: '', + displayName: '', + res: {}, + isNameError: false, + }, + validationSchema: Yup.object({}), + onSubmit: async (val) => { + if (isUpdate) { + try { + if (!project) { + throw new Error('Project is required!.'); + } + const { errors: e } = await api.updateProjectMSv({ + projectName: project, + pmsvc: { + displayName: val.displayName, + metadata: { + name: val.name, + }, - spec: { - msvcSpec: { - serviceTemplate: { - apiVersion: selectedService.service.apiVersion, - kind: selectedService.service.kind, - spec: { - ...tempVal, + spec: { + msvcSpec: { + serviceTemplate: { + apiVersion: + props.data.spec?.msvcSpec.serviceTemplate.apiVersion || + '', + kind: + props.data.spec?.msvcSpec.serviceTemplate.kind || '', + spec: { + ...val.res, + }, + }, }, + targetNamespace: '', }, }, - namespace: '', - }, - }, - }); - if (e) { - throw e[0]; + }); + if (e) { + throw e[0]; + } + setVisible(false); + reload(); + } catch (err) { + handleError(err); + } } - setVisible(false); - reload(); - } catch (err) { - handleError(err); - } - }, - }); + }, + }); - useEffect(() => { - if (selectedService?.service.fields) { - setValues({ - name: '', - displayName: '', - ...flatM( - selectedService?.service.fields.reduce((acc, curr) => { - return { ...acc, [curr.name]: curr.defaultValue }; - }, {}) - ), + const getService = () => { + if (isUpdate) + return getManagedTemplate({ + templates, + apiVersion: props.data.spec?.msvcSpec.serviceTemplate.apiVersion || '', + kind: props.data.spec?.msvcSpec.serviceTemplate.kind || '', }); - } - }, [selectedService]); + return undefined; + }; + if (!isUpdate) { + return null; + } return ( - // - // {step === 'choose' ? ( - //
Choose a service
- // ) : ( - //
- //
- // } - // size="xs" - // variant="plain" - // onClick={() => { - // resetValues({}); - // setStep('choose'); - // }} - // /> - // {selectedService?.service.displayName} - //
- //
{selectedService?.service.displayName}
- //
- // )} - //
{ - if (step === 'choose') { - setStep('fill'); - e.preventDefault(); - } else handleSubmit(e); + console.log('name error..', values.isNameError); + + handleSubmit(e); + // if (!values.isNameError) { + // handleSubmit(e); + // } else { + // e.preventDefault(); + // } }} > - {step === 'choose' ? ( - - ) : ( - - )} + - {step === 'fill' ? ( - { - resetValues({}); - setSelectedService(null); - setStep('choose'); - }} - content="Back" - /> - ) : null} + @@ -614,13 +361,9 @@ const Root = (props: IDialog) => { const HandleBackendService = (props: IDialog) => { const { isUpdate, setVisible, visible } = props; return ( - setVisible(v)} - > + setVisible(v)}> - {isUpdate ? 'Edit cloud provider' : 'Add new cloud provider'} + {isUpdate ? 'Edit managed service' : 'Add managed service'} {(!isUpdate || (isUpdate && props.data)) && } diff --git a/src/apps/console/routes/_main+/$account+/$project+/managed-services/route.tsx b/src/apps/console/routes/_main+/$account+/$project+/managed-services/route.tsx index 360133b11..2c578b1dd 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/managed-services/route.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/managed-services/route.tsx @@ -1,25 +1,22 @@ import { Plus, PlusFill } from '@jengaicons/react'; import { defer } from '@remix-run/node'; -import { useLoaderData } from '@remix-run/react'; -import { Dispatch, SetStateAction, useEffect, useState } from 'react'; +import { Link, useLoaderData } from '@remix-run/react'; import { Button } from '~/components/atoms/button.jsx'; import { LoadingComp, pWrapper } from '~/console/components/loading-component'; import Wrapper from '~/console/components/wrapper'; import { GQLServerHandler } from '~/console/server/gql/saved-queries'; import { parseNodes } from '~/console/server/r-utils/common'; import { IRemixCtx } from '~/root/lib/types/common'; -import { IMSvTemplates } from '~/console/server/gql/queries/managed-templates-queries'; import Tools from './tools'; -import HandleBackendService from './handle-backend-service'; import BackendServicesResources from './backend-services-resources'; export const loader = (ctx: IRemixCtx) => { - const { cluster } = ctx.params; + const { project } = ctx.params; const promise = pWrapper(async () => { const { data: mData, errors: mErrors } = await GQLServerHandler( ctx.request - ).listClusterMSvs({ - clusterName: cluster, + ).listProjectMSvs({ + projectName: project, }); const { data: msvTemplates, errors: msvError } = await GQLServerHandler( @@ -39,84 +36,54 @@ export const loader = (ctx: IRemixCtx) => { return defer({ promise }); }; -const SetValue = ({ - templates, - setTemplate, -}: { - templates: IMSvTemplates; - setTemplate: Dispatch>; -}) => { - useEffect(() => { - setTemplate(templates); - }, []); - return null; -}; - const KlOperatorServices = () => { - const [visible, setVisible] = useState(false); const { promise } = useLoaderData(); - const [templates, setTemplates] = useState(null); return ( - <> - - {({ managedServices, templates: templatesData }) => { - const backendServices = parseNodes(managedServices); + + {({ managedServices, templates: templatesData }) => { + const backendServices = parseNodes(managedServices); - return ( - <> - - 0 && ( -