├── .env.example
├── .eslintignore
├── .eslintrc.cjs
├── .github
└── workflows
│ ├── check-links.yml
│ ├── playwright.yml
│ ├── release.yml
│ └── unit-tests.yml
├── .gitignore
├── .husky
└── pre-commit
├── .prettierignore
├── .prettierrc
├── README.md
├── e2e-tests
└── example.spec.ts
├── index.html
├── package.json
├── playwright.config.ts
├── pnpm-lock.yaml
├── postcss.config.js
├── public
└── favicon.ico
├── scripts
├── check-links.js
├── check-links.sh
└── types.js
├── src
├── App.tsx
├── app
│ ├── 404.tsx
│ ├── activate-server
│ │ ├── PasswordStep.tsx
│ │ ├── ServerActivationContext.tsx
│ │ ├── ServerNameStep.tsx
│ │ ├── Wizard.tsx
│ │ └── page.tsx
│ ├── activate-user
│ │ ├── AccountDetailsStep.tsx
│ │ ├── ActivationContext.tsx
│ │ ├── InfrastructureStep.tsx
│ │ ├── PasswordStep.tsx
│ │ ├── PrimaryUseStep.tsx
│ │ ├── UsageReasonStep.tsx
│ │ ├── Wizard.tsx
│ │ └── page.tsx
│ ├── artifacts
│ │ ├── Fragments.tsx
│ │ └── page.tsx
│ ├── components
│ │ ├── StackComponentList.tsx
│ │ ├── [componentId]
│ │ │ ├── edit
│ │ │ │ ├── form-step.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
│ │ ├── button-group.tsx
│ │ ├── columns.tsx
│ │ ├── component-dropdown.tsx
│ │ ├── create
│ │ │ ├── breadcrumb.ts
│ │ │ ├── config-step.tsx
│ │ │ ├── flavor-step.tsx
│ │ │ ├── header.tsx
│ │ │ ├── page.tsx
│ │ │ ├── type-step.tsx
│ │ │ └── wizard.tsx
│ │ ├── page.tsx
│ │ ├── selector-context.tsx
│ │ └── service.ts
│ ├── devices
│ │ └── verify
│ │ │ ├── ConfirmationForm.tsx
│ │ │ ├── DeviceInfo.tsx
│ │ │ ├── Success.tsx
│ │ │ ├── page.tsx
│ │ │ └── service.ts
│ ├── login
│ │ ├── LoginForm.tsx
│ │ └── page.tsx
│ ├── models
│ │ ├── Fragments.tsx
│ │ └── page.tsx
│ ├── onboarding
│ │ ├── Header.tsx
│ │ ├── Setup
│ │ │ ├── Items.tsx
│ │ │ ├── constant.ts
│ │ │ ├── index.tsx
│ │ │ ├── pipeline-snippet.tsx
│ │ │ ├── provider-step.tsx
│ │ │ ├── run-script.tsx
│ │ │ └── set-project.tsx
│ │ ├── page.tsx
│ │ └── progress-indicator.tsx
│ ├── pipelines
│ │ ├── PipelinesTab
│ │ │ ├── ButtonGroup.tsx
│ │ │ ├── DeletePipelineAlert.tsx
│ │ │ ├── PipelineDropdown.tsx
│ │ │ ├── PipelineSelectorContext.tsx
│ │ │ ├── PipelinesBody.tsx
│ │ │ ├── columns.tsx
│ │ │ └── service.ts
│ │ ├── [namespace]
│ │ │ ├── Header.tsx
│ │ │ ├── RunsTable.tsx
│ │ │ ├── breadcrumb.ts
│ │ │ ├── columns.tsx
│ │ │ ├── page.tsx
│ │ │ └── service.ts
│ │ └── page.tsx
│ ├── projects
│ │ ├── page.tsx
│ │ ├── project-item.tsx
│ │ ├── project-list.tsx
│ │ ├── project-menu.tsx
│ │ ├── searchbar.tsx
│ │ └── set-default-project.tsx
│ ├── run-templates
│ │ ├── TemplateBody.tsx
│ │ └── page.tsx
│ ├── runs
│ │ ├── ButtonGroup.tsx
│ │ ├── DeleteRunAlert.tsx
│ │ ├── RunDropdown.tsx
│ │ ├── RunsBody.tsx
│ │ ├── RunsSelectorContext.tsx
│ │ ├── [id]
│ │ │ ├── Dag.tsx
│ │ │ ├── DeleteRunAlert.tsx
│ │ │ ├── Header.tsx
│ │ │ ├── RunActionMenu.tsx
│ │ │ ├── _Tabs
│ │ │ │ ├── Configuration
│ │ │ │ │ ├── CodeCollapsible.tsx
│ │ │ │ │ ├── DockerImageCollapsible.tsx
│ │ │ │ │ ├── EnvironmentCollapsible.tsx
│ │ │ │ │ ├── ParameterCollapsible.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── Metadata
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── Overview
│ │ │ │ │ ├── AlertPanels.tsx
│ │ │ │ │ ├── Details.tsx
│ │ │ │ │ ├── Orchestrator.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── Stack
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ └── service.ts
│ │ │ ├── breadcrumbs.ts
│ │ │ ├── not-found.tsx
│ │ │ ├── page.tsx
│ │ │ └── useDag.tsx
│ │ ├── columns.tsx
│ │ ├── page.tsx
│ │ └── service.ts
│ ├── settings
│ │ ├── api-tokens
│ │ │ ├── APITokenModal.tsx
│ │ │ ├── CreateTokenButton.tsx
│ │ │ └── page.tsx
│ │ ├── connectors
│ │ │ ├── [id]
│ │ │ │ ├── components
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── configuration
│ │ │ │ │ ├── configuration.tsx
│ │ │ │ │ ├── page.tsx
│ │ │ │ │ └── params.tsx
│ │ │ │ └── resources
│ │ │ │ │ ├── loading-dialog.tsx
│ │ │ │ │ ├── page.tsx
│ │ │ │ │ ├── resources-list.tsx
│ │ │ │ │ ├── verify-box.tsx
│ │ │ │ │ └── verify-button.tsx
│ │ │ ├── button-group.tsx
│ │ │ ├── columns.tsx
│ │ │ ├── connector-dropdown.tsx
│ │ │ ├── connector-list.tsx
│ │ │ ├── create
│ │ │ │ ├── breadcrumbs.ts
│ │ │ │ ├── common
│ │ │ │ │ └── footer-buttons.tsx
│ │ │ │ ├── configuration
│ │ │ │ │ ├── auth-method-section.tsx
│ │ │ │ │ ├── config-section.tsx
│ │ │ │ │ ├── debounced-check.ts
│ │ │ │ │ ├── helper.ts
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── name-schema.ts
│ │ │ │ │ ├── name-section.tsx
│ │ │ │ │ ├── resource-type-section.tsx
│ │ │ │ │ ├── schema.ts
│ │ │ │ │ ├── skip-verify.tsx
│ │ │ │ │ ├── use-verify-button.ts
│ │ │ │ │ └── verify-connector-button.tsx
│ │ │ │ ├── connector-type
│ │ │ │ │ ├── description.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── list.tsx
│ │ │ │ │ └── schema.ts
│ │ │ │ ├── create-context.tsx
│ │ │ │ ├── header.tsx
│ │ │ │ ├── page.tsx
│ │ │ │ ├── success
│ │ │ │ │ ├── columns.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── success-box.tsx
│ │ │ │ │ └── table.tsx
│ │ │ │ └── wizard.tsx
│ │ │ ├── header.tsx
│ │ │ ├── list-content.tsx
│ │ │ ├── new-connector-button.tsx
│ │ │ ├── page.tsx
│ │ │ ├── searchbar.tsx
│ │ │ ├── selector-context.ts
│ │ │ └── use-connector-params.ts
│ │ ├── general
│ │ │ ├── GeneralForm.tsx
│ │ │ ├── GeneralFormSchema.ts
│ │ │ └── page.tsx
│ │ ├── members
│ │ │ ├── AddMemberDialog.tsx
│ │ │ ├── DeleteMemberAlert.tsx
│ │ │ ├── MemberDropdown.tsx
│ │ │ ├── MembersTable.tsx
│ │ │ ├── columns.tsx
│ │ │ ├── page.tsx
│ │ │ └── service.ts
│ │ ├── notifications
│ │ │ ├── NotificationsForm.tsx
│ │ │ └── page.tsx
│ │ ├── profile
│ │ │ ├── UpdatePasswordDialog.tsx
│ │ │ ├── UpdateProfileForm.tsx
│ │ │ ├── UpdateProfileSchema.ts
│ │ │ └── page.tsx
│ │ ├── repositories
│ │ │ ├── Fragments.tsx
│ │ │ └── page.tsx
│ │ ├── secrets
│ │ │ ├── AddSecretDialog.tsx
│ │ │ ├── DeleteSecretAlert.tsx
│ │ │ ├── EditSecretDialog.tsx
│ │ │ ├── SecretTooltip.tsx
│ │ │ ├── SecretsDropdown.tsx
│ │ │ ├── SecretsTable.tsx
│ │ │ ├── [id]
│ │ │ │ ├── DeleteKeyAlert.tsx
│ │ │ │ ├── SecretDetailTable.tsx
│ │ │ │ ├── SecretTableDropDown.tsx
│ │ │ │ ├── breadcrumbs.ts
│ │ │ │ ├── columns.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── columns.tsx
│ │ │ ├── form-schema.ts
│ │ │ ├── page.tsx
│ │ │ └── service.ts
│ │ └── service-accounts
│ │ │ ├── AddServiceAccount.tsx
│ │ │ ├── ButtonGroup.tsx
│ │ │ ├── DeleteAlert.tsx
│ │ │ ├── Dropdown.tsx
│ │ │ ├── Fallback.tsx
│ │ │ ├── SelectorContext.tsx
│ │ │ ├── Table.tsx
│ │ │ ├── ToggleServiceAccount.tsx
│ │ │ ├── [service-account-id]
│ │ │ ├── AddApiKeyDialog.tsx
│ │ │ ├── ButtonGroup.tsx
│ │ │ ├── DeleteApiKeyDialog.tsx
│ │ │ ├── Dropdown.tsx
│ │ │ ├── Fallback.tsx
│ │ │ ├── RotateKeyDialog.tsx
│ │ │ ├── SelectorContext.tsx
│ │ │ ├── Success.tsx
│ │ │ ├── Table.tsx
│ │ │ ├── ToggleApiKey.tsx
│ │ │ ├── breadcrumb.ts
│ │ │ ├── columns.tsx
│ │ │ └── page.tsx
│ │ │ ├── columns.tsx
│ │ │ ├── form-schema.ts
│ │ │ ├── page.tsx
│ │ │ └── service.ts
│ ├── stacks
│ │ ├── ActionsDropdown.tsx
│ │ ├── DeleteStackModal.tsx
│ │ ├── DialogItems.tsx
│ │ ├── ResumeBanner.tsx
│ │ ├── ResumeStackBanner.tsx
│ │ ├── ResumeTerraformBanner.tsx
│ │ ├── StackList.tsx
│ │ ├── columns.tsx
│ │ ├── create
│ │ │ ├── ExistingInfra.tsx
│ │ │ ├── LocalOverlay.tsx
│ │ │ ├── NewInfra.tsx
│ │ │ ├── OptionCard.tsx
│ │ │ ├── breadcrumb.ts
│ │ │ ├── components
│ │ │ │ ├── CancelButton.tsx
│ │ │ │ ├── ProviderRadio.tsx
│ │ │ │ ├── RadioItem.tsx
│ │ │ │ ├── StackName.tsx
│ │ │ │ ├── WizardFooter.tsx
│ │ │ │ └── sharedSchema.ts
│ │ │ ├── existing-infrastructure
│ │ │ │ ├── ExistingInfraContext.tsx
│ │ │ │ ├── FlavorIcon.tsx
│ │ │ │ ├── Wizard.tsx
│ │ │ │ ├── page.tsx
│ │ │ │ └── steps
│ │ │ │ │ ├── artifact_store
│ │ │ │ │ ├── Form.tsx
│ │ │ │ │ ├── Header.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── schema.ts
│ │ │ │ │ └── useArtifactStore.ts
│ │ │ │ │ ├── connect
│ │ │ │ │ ├── AuthMethodSelect.tsx
│ │ │ │ │ ├── AuthenticationMethod.tsx
│ │ │ │ │ ├── Configuration.tsx
│ │ │ │ │ ├── LoadingModal.tsx
│ │ │ │ │ ├── NewConnectorForm.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── schema.ts
│ │ │ │ │ └── useNewConnector.ts
│ │ │ │ │ ├── container_registry
│ │ │ │ │ ├── Form.tsx
│ │ │ │ │ ├── Header.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── schema.ts
│ │ │ │ │ └── useContainerRegistry.tsx
│ │ │ │ │ ├── final
│ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── orchestrator
│ │ │ │ │ ├── Form.tsx
│ │ │ │ │ ├── Header.tsx
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── schema.ts
│ │ │ │ │ └── useOrchestrator.ts
│ │ │ │ │ └── provider
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── schema.ts
│ │ │ ├── layout.tsx
│ │ │ ├── manual
│ │ │ │ ├── ComponentSelection.tsx
│ │ │ │ ├── ComponentsTooltip.tsx
│ │ │ │ ├── TypeOverview.tsx
│ │ │ │ ├── TypeOverviewItem.tsx
│ │ │ │ ├── create-component-modal
│ │ │ │ │ ├── configuration-step.tsx
│ │ │ │ │ ├── flavor-select.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── page.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── useManualStack.tsx
│ │ │ ├── new-infrastructure
│ │ │ │ ├── CloudProvider.tsx
│ │ │ │ ├── NewInfraFormContext.tsx
│ │ │ │ ├── Providers
│ │ │ │ │ ├── AWS.tsx
│ │ │ │ │ ├── Azure.tsx
│ │ │ │ │ ├── GCP.tsx
│ │ │ │ │ ├── PermissionsCard.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── Steps
│ │ │ │ │ ├── Configuration
│ │ │ │ │ │ ├── LocationSelect.tsx
│ │ │ │ │ │ ├── Partials.tsx
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Deploy
│ │ │ │ │ │ ├── ButtonStep.tsx
│ │ │ │ │ │ ├── ProvisioningStep.tsx
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── Provider.tsx
│ │ │ │ │ ├── Success
│ │ │ │ │ │ ├── SuccessList.tsx
│ │ │ │ │ │ └── SuccessStep.tsx
│ │ │ │ │ └── schemas.ts
│ │ │ │ ├── Wizard.tsx
│ │ │ │ ├── page.tsx
│ │ │ │ └── persist.ts
│ │ │ ├── page.tsx
│ │ │ └── terraform
│ │ │ │ ├── TerraformContext.tsx
│ │ │ │ ├── TerraformWizard.tsx
│ │ │ │ ├── page.tsx
│ │ │ │ ├── persist.ts
│ │ │ │ └── steps
│ │ │ │ ├── configuration
│ │ │ │ └── index.tsx
│ │ │ │ ├── deploy
│ │ │ │ ├── Pollingstep.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ └── usePollinglist.ts
│ │ │ │ ├── provider
│ │ │ │ ├── index.tsx
│ │ │ │ └── schema.ts
│ │ │ │ └── success
│ │ │ │ └── index.tsx
│ │ ├── page.tsx
│ │ ├── service.ts
│ │ └── useDeleteStack.tsx
│ ├── survey
│ │ ├── AccountDetailsStep.tsx
│ │ ├── InfrastructureStep.tsx
│ │ ├── PrimaryUseStep.tsx
│ │ ├── SurveyUserContext.tsx
│ │ ├── UsageReasonStep.tsx
│ │ ├── Wizard.tsx
│ │ ├── loader.ts
│ │ └── page.tsx
│ └── upgrade
│ │ ├── components
│ │ ├── Context.tsx
│ │ ├── Success.tsx
│ │ └── form
│ │ │ ├── Form.tsx
│ │ │ ├── Steps.tsx
│ │ │ ├── Wrapper.tsx
│ │ │ ├── index.tsx
│ │ │ ├── schema.ts
│ │ │ └── useUpgradeForm.ts
│ │ └── page.tsx
├── assets
│ ├── icons
│ │ ├── Lock.svg
│ │ ├── alert-circle.svg
│ │ ├── alert-triangle.svg
│ │ ├── analysis.svg
│ │ ├── annotation-alert.svg
│ │ ├── announcement.svg
│ │ ├── arrow-left.svg
│ │ ├── arrow-right.svg
│ │ ├── bar-chart-square-check.svg
│ │ ├── bar-chart.svg
│ │ ├── building.svg
│ │ ├── cached.svg
│ │ ├── calendar.svg
│ │ ├── check-circle.svg
│ │ ├── check.svg
│ │ ├── chevron-down.svg
│ │ ├── chevron-left-double.svg
│ │ ├── chevron-left.svg
│ │ ├── chevron-right-double.svg
│ │ ├── chevron-right.svg
│ │ ├── chip.svg
│ │ ├── clipboard.svg
│ │ ├── clock.svg
│ │ ├── close.svg
│ │ ├── cloud-tenant.svg
│ │ ├── cloud.svg
│ │ ├── code-box.svg
│ │ ├── code-browser.svg
│ │ ├── code-square.svg
│ │ ├── coin.svg
│ │ ├── collapse.svg
│ │ ├── connector-add.svg
│ │ ├── container.svg
│ │ ├── copy.svg
│ │ ├── credit-card.svg
│ │ ├── database.svg
│ │ ├── dataflow-2.svg
│ │ ├── dataflow.svg
│ │ ├── dots-circle.svg
│ │ ├── dots-horizontal.svg
│ │ ├── download-01.svg
│ │ ├── edit.svg
│ │ ├── expand-full.svg
│ │ ├── expand.svg
│ │ ├── eye-off.svg
│ │ ├── eye.svg
│ │ ├── file-question.svg
│ │ ├── file-text.svg
│ │ ├── file.svg
│ │ ├── folder.svg
│ │ ├── github.svg
│ │ ├── gitlab.svg
│ │ ├── hash.svg
│ │ ├── hat.svg
│ │ ├── help.svg
│ │ ├── info.svg
│ │ ├── key-icon.svg
│ │ ├── layout.svg
│ │ ├── link-external.svg
│ │ ├── logout.svg
│ │ ├── logs.svg
│ │ ├── mail.svg
│ │ ├── maximize.svg
│ │ ├── message-chat-square.svg
│ │ ├── minus.svg
│ │ ├── ml_model.svg
│ │ ├── name.svg
│ │ ├── package-plus.svg
│ │ ├── package-search.svg
│ │ ├── package.svg
│ │ ├── pin-01.svg
│ │ ├── pin.svg
│ │ ├── pipeline-template.svg
│ │ ├── pipeline.svg
│ │ ├── play-circle.svg
│ │ ├── plus.svg
│ │ ├── redirect.svg
│ │ ├── refresh.svg
│ │ ├── rocket.svg
│ │ ├── route.svg
│ │ ├── running.svg
│ │ ├── search.svg
│ │ ├── services
│ │ │ ├── airflow.svg
│ │ │ ├── aws.svg
│ │ │ ├── azure.svg
│ │ │ ├── docker.svg
│ │ │ ├── flyte.svg
│ │ │ ├── gcp.svg
│ │ │ ├── kedro.svg
│ │ │ ├── kubernetes.svg
│ │ │ ├── metaflow.svg
│ │ │ ├── openshift.svg
│ │ │ ├── prefect.svg
│ │ │ ├── slack.svg
│ │ │ └── terraform.svg
│ │ ├── settings.svg
│ │ ├── side-collapse.svg
│ │ ├── side-expand.svg
│ │ ├── slash-circle.svg
│ │ ├── slash-divider.svg
│ │ ├── spinner.svg
│ │ ├── stack.svg
│ │ ├── stars.svg
│ │ ├── storefront.svg
│ │ ├── switch-horizontal.svg
│ │ ├── terminal-square.svg
│ │ ├── terminal.svg
│ │ ├── tick-circle.svg
│ │ ├── tool-02.svg
│ │ ├── tool.svg
│ │ ├── transform.svg
│ │ ├── trash.svg
│ │ ├── trigger.svg
│ │ ├── user-plus.svg
│ │ ├── user.svg
│ │ ├── users.svg
│ │ ├── zenml-icon.svg
│ │ └── zenml.svg
│ ├── illustrations
│ │ ├── cloud-squares.svg
│ │ ├── connectivity.webp
│ │ ├── connectors-video.svg
│ │ ├── repos-video.svg
│ │ ├── secrets-video.svg
│ │ └── stacks-video.svg
│ ├── images
│ │ ├── acp.webp
│ │ ├── mcp.webp
│ │ ├── portraits
│ │ │ ├── adam.webp
│ │ │ ├── alex.webp
│ │ │ ├── baris.webp
│ │ │ ├── hamza.webp
│ │ │ └── stefan.webp
│ │ ├── product-tour
│ │ │ ├── settings_preview.webp
│ │ │ └── tour-cover.webp
│ │ ├── templates.webp
│ │ └── upgrade-form.webp
│ └── styles
│ │ ├── index.css
│ │ └── prism-github-light.css
├── components
│ ├── AlertDialogDropdownItem.tsx
│ ├── Analytics.tsx
│ ├── ArtifactIcon.tsx
│ ├── CodeHighlighter.tsx
│ ├── CodeSnippet.tsx
│ ├── CollapsibleCard.tsx
│ ├── ComponentIcon.tsx
│ ├── CopyButton.tsx
│ ├── DeleteAlertDialog.tsx
│ ├── DisplayDate.tsx
│ ├── EmptyState.tsx
│ ├── Error.tsx
│ ├── ExecutionStatus.tsx
│ ├── Infobox.tsx
│ ├── InlineAvatar.tsx
│ ├── KeyValue.tsx
│ ├── MetadataCards.tsx
│ ├── NavLink.tsx
│ ├── NestedCollapsible.tsx
│ ├── NumberBox.tsx
│ ├── PageHeader.tsx
│ ├── Pagination.tsx
│ ├── ProviderIcon.tsx
│ ├── SearchField.tsx
│ ├── Tick.tsx
│ ├── VideoModal.tsx
│ ├── artifacts
│ │ ├── CsvVizualization.tsx
│ │ ├── HtmlVisualization.tsx
│ │ ├── ImageVisualization.tsx
│ │ ├── JsonVisualization.tsx
│ │ ├── MarkdownVisualization.tsx
│ │ ├── Visualization.tsx
│ │ ├── artifact-node-sheet
│ │ │ ├── DetailCards.tsx
│ │ │ ├── DetailsTab.tsx
│ │ │ ├── MetadataTab.tsx
│ │ │ ├── SheetContent.tsx
│ │ │ ├── VisualizationTab.tsx
│ │ │ └── index.tsx
│ │ ├── download-artifact-button.tsx
│ │ ├── visualization-combobox.tsx
│ │ └── visualization-download-button.tsx
│ ├── avatar-stack.tsx
│ ├── breadcrumbs
│ │ ├── Breadcrumbs.tsx
│ │ ├── SegmentsBreadcrumbs.tsx
│ │ ├── library.ts
│ │ ├── project-link.tsx
│ │ ├── types.ts
│ │ └── workspace-link.tsx
│ ├── buttons
│ │ └── update-button-content.tsx
│ ├── copy-metadata-button.tsx
│ ├── dag-visualizer
│ │ ├── ArtifactNode.tsx
│ │ ├── BaseNode.tsx
│ │ ├── Controls.tsx
│ │ ├── NodeCopyButton.tsx
│ │ ├── PreviewArtifact.tsx
│ │ ├── PreviewStep.tsx
│ │ ├── SmartEdge.tsx
│ │ ├── StepNode.tsx
│ │ ├── layout.ts
│ │ └── layout
│ │ │ ├── compute.ts
│ │ │ └── status.ts
│ ├── dialog
│ │ └── DialogItem.tsx
│ ├── fallback-pages
│ │ ├── Cards.tsx
│ │ ├── Commands.tsx
│ │ └── Helpbox.tsx
│ ├── fallback
│ │ └── icon.tsx
│ ├── form
│ │ ├── array-field-renderer.tsx
│ │ ├── boolean-field-renderer.tsx
│ │ ├── common.tsx
│ │ ├── enum-field-renderer.tsx
│ │ ├── form.tsx
│ │ ├── helper.spec.ts
│ │ ├── helper.ts
│ │ ├── integer-field-renderer.tsx
│ │ ├── object-field-renderer.tsx
│ │ ├── sensitive-text-renderer.tsx
│ │ └── text-field-renderer.tsx
│ ├── login-command.tsx
│ ├── not-available.tsx
│ ├── onboarding
│ │ ├── ChecklistItem.tsx
│ │ ├── SkippedStep.tsx
│ │ └── floating-progress-link.tsx
│ ├── password
│ │ ├── PasswordChecker.tsx
│ │ └── UpdatePasswordSchemas.ts
│ ├── pro
│ │ └── ProCta.tsx
│ ├── repositories
│ │ └── RepoBadge.tsx
│ ├── runs
│ │ ├── auth-required.tsx
│ │ └── refresh-group
│ │ │ ├── deep-refresh.ts
│ │ │ ├── dropdown.tsx
│ │ │ └── index.tsx
│ ├── sensitive-value.tsx
│ ├── service-connectors
│ │ ├── connector-tag.tsx
│ │ ├── connector-type-tooltip.tsx
│ │ ├── create-fallback.tsx
│ │ ├── delete-connector.tsx
│ │ ├── expiry.tsx
│ │ ├── resource-name-tooltip.tsx
│ │ ├── resource-tyes-list.tsx
│ │ └── resource-type-tooltip.tsx
│ ├── sheet
│ │ └── SheetHeader.tsx
│ ├── stack-components
│ │ ├── ComponentBadge.tsx
│ │ ├── component-detail
│ │ │ ├── ConfigTab.tsx
│ │ │ ├── Header.tsx
│ │ │ ├── Tabs.tsx
│ │ │ ├── hooks.ts
│ │ │ └── service.ts
│ │ ├── component-sheet
│ │ │ ├── index.tsx
│ │ │ ├── runs-tab
│ │ │ │ ├── RunsList.tsx
│ │ │ │ └── columns.tsx
│ │ │ └── stacks-tab
│ │ │ │ ├── StackList.tsx
│ │ │ │ └── columns.tsx
│ │ ├── create-component
│ │ │ ├── configuration-form.tsx
│ │ │ ├── connector-section.tsx
│ │ │ ├── connector-select.tsx
│ │ │ ├── flavor-select.tsx
│ │ │ ├── info-tile.tsx
│ │ │ └── schema.ts
│ │ └── delete-component
│ │ │ └── delete-alert.tsx
│ ├── stacks
│ │ ├── Sheet
│ │ │ ├── IntegrationsContext.tsx
│ │ │ ├── index.tsx
│ │ │ └── update-stacks-dialog.tsx
│ │ ├── info
│ │ │ ├── ComponentCollapsible.tsx
│ │ │ ├── StackHeader.tsx
│ │ │ └── index.tsx
│ │ └── new-infra
│ │ │ └── TerraformCommands.tsx
│ ├── steps
│ │ └── step-sheet
│ │ │ ├── CodeTab.tsx
│ │ │ ├── ConfigurationTab.tsx
│ │ │ ├── DetailsTab.tsx
│ │ │ ├── LogsTab.tsx
│ │ │ ├── MetadataTab.tsx
│ │ │ ├── SheetContent.tsx
│ │ │ ├── StacksTab.tsx
│ │ │ ├── index.tsx
│ │ │ ├── sheet-headline.tsx
│ │ │ └── step-status-tooltip.tsx
│ ├── survey
│ │ ├── AccountDetailsForm.tsx
│ │ ├── Infrastructure.tsx
│ │ ├── PrimaryUse.tsx
│ │ ├── ServerName.tsx
│ │ ├── SetPassword.tsx
│ │ ├── SlackStep.tsx
│ │ ├── StepDisplay.tsx
│ │ ├── SuccessStep.tsx
│ │ ├── SurveyContext.tsx
│ │ ├── UsageReason.tsx
│ │ ├── UseRadioButton.tsx
│ │ └── form-schemas.ts
│ ├── tables
│ │ └── helper.tsx
│ ├── tour
│ │ ├── StartTourDialog.tsx
│ │ ├── Tour.tsx
│ │ └── TourContext.tsx
│ └── wizard
│ │ ├── LeftSideMenu.tsx
│ │ └── Wizard.tsx
├── contents
│ ├── components.tsx
│ ├── repositories.ts
│ ├── secrets.ts
│ └── stack.ts
├── context
│ ├── AuthContext.tsx
│ ├── SchemaContext.tsx
│ ├── VisualizationConfirmationContext.tsx
│ └── WizardContext.tsx
├── data
│ ├── api.ts
│ ├── artifact-versions
│ │ ├── artifact-download-token.ts
│ │ ├── artifact-version-detail-query.ts
│ │ └── artifact-visualization-query.ts
│ ├── auth
│ │ └── create-api-token.ts
│ ├── code-repositories
│ │ └── code-repositories-detail-query.ts
│ ├── components
│ │ ├── component-detail-query.ts
│ │ ├── components-list.ts
│ │ ├── create-component.ts
│ │ ├── delete-component.ts
│ │ ├── index.ts
│ │ └── update-component.ts
│ ├── devices
│ │ ├── device-detail-query.ts
│ │ └── device-verify-mutation.ts
│ ├── fetch.ts
│ ├── flavors
│ │ ├── flavor-detail.ts
│ │ ├── flavors-list.ts
│ │ └── index.ts
│ ├── pipeline-builds
│ │ └── all-pipeline-builds-query.ts
│ ├── pipeline-deployments
│ │ ├── index.ts
│ │ └── pipeline-deployments-detail.ts
│ ├── pipeline-runs
│ │ ├── all-pipeline-runs-query.ts
│ │ ├── delete-run.ts
│ │ ├── fetch-orchestrator-run.ts
│ │ ├── pipeline-run-detail-query.ts
│ │ └── run-dag.ts
│ ├── pipelines
│ │ ├── delete-pipeline.ts
│ │ ├── index.ts
│ │ └── pipeline-list-query.ts
│ ├── projects
│ │ ├── index.ts
│ │ ├── project-detail.ts
│ │ └── project-statistics.ts
│ ├── secrets
│ │ ├── create-secret-query.ts
│ │ ├── delete-secret-query.ts
│ │ ├── get-secret-detail.ts
│ │ ├── index.ts
│ │ ├── secrets-all-query.ts
│ │ └── update-secret-query.ts
│ ├── server
│ │ ├── activate-server-mutation.ts
│ │ ├── get-server-settings.ts
│ │ ├── info-query.ts
│ │ ├── onboarding-state.ts
│ │ └── update-server-settings-mutation.ts
│ ├── service-accounts
│ │ ├── create-api-key.ts
│ │ ├── create-service-account.ts
│ │ ├── delete-api-key.ts
│ │ ├── delete-service-account.ts
│ │ ├── get-api-key.ts
│ │ ├── get-service-account.ts
│ │ ├── index.ts
│ │ ├── list-api-keys.ts
│ │ ├── list-service-accounts.ts
│ │ ├── rotate-api-key.ts
│ │ ├── update-api-key.ts
│ │ └── update-service-account.ts
│ ├── service-connectors
│ │ ├── connector-detail.ts
│ │ ├── connector-list.ts
│ │ ├── connector-type-detail.ts
│ │ ├── connector-type-list.ts
│ │ ├── create-service-connector.ts
│ │ ├── delete-service-connector.ts
│ │ ├── index.ts
│ │ ├── resources-info.ts
│ │ ├── verify-config.ts
│ │ └── verify-existing-connectors.ts
│ ├── session
│ │ ├── login-mutation.ts
│ │ └── logout-mutation.ts
│ ├── stacks
│ │ ├── create-stack.ts
│ │ ├── delete-stack.ts
│ │ ├── index.ts
│ │ ├── stack-deployment-config.ts
│ │ ├── stack-deployment-info.ts
│ │ ├── stack-deployment-stack.ts
│ │ ├── stack-detail-query.ts
│ │ └── stacklist-query.ts
│ ├── steps
│ │ ├── step-detail-query.ts
│ │ └── step-logs-query.ts
│ └── users
│ │ ├── activate-user-mutation.ts
│ │ ├── create-user-mutation.ts
│ │ ├── current-user-query.ts
│ │ ├── delete-user-mutation.ts
│ │ ├── update-current-user-mutation.ts
│ │ └── users-all-query.ts
├── error-boundaries
│ ├── PageBoundary.tsx
│ └── RootBoundary.tsx
├── hooks
│ └── use-route-segment.tsx
├── layouts
│ ├── AuthenticatedLayout
│ │ ├── AuthenticatedHeader.tsx
│ │ ├── BreadcrumbsContext.tsx
│ │ ├── LocalBanner.tsx
│ │ ├── UserDropdown.tsx
│ │ ├── changelog-button.tsx
│ │ └── index.tsx
│ ├── GradientLayout.tsx
│ ├── RootLayout.tsx
│ ├── connectors-detail
│ │ ├── breadcrumbs.ts
│ │ ├── header.tsx
│ │ ├── layout.tsx
│ │ ├── resources-context.tsx
│ │ └── tabs.tsx
│ ├── non-project-scoped
│ │ ├── dropdown.tsx
│ │ ├── header.tsx
│ │ ├── layout.tsx
│ │ ├── name-section.tsx
│ │ ├── server-members.tsx
│ │ └── tabs.tsx
│ ├── project-tabs
│ │ ├── header.tsx
│ │ ├── id-section.tsx
│ │ ├── layout.tsx
│ │ ├── name-section.tsx
│ │ └── tabs.tsx
│ └── settings
│ │ ├── Menu.tsx
│ │ ├── project-settings
│ │ ├── layout.tsx
│ │ └── project-display.tsx
│ │ └── settings-layout
│ │ ├── LayoutSidebar.tsx
│ │ ├── VersionDisplay.tsx
│ │ └── layout.tsx
├── lib
│ ├── analytics.ts
│ ├── bulk-delete.tsx
│ ├── code-snippets.ts
│ ├── common.ts
│ ├── components.ts
│ ├── constants.ts
│ ├── copy.ts
│ ├── dates.ts
│ ├── fetch-error.ts
│ ├── forms.spec.ts
│ ├── forms.ts
│ ├── images.ts
│ ├── login-command.ts
│ ├── not-found-error.ts
│ ├── onboarding.spec.ts
│ ├── onboarding.ts
│ ├── provider.ts
│ ├── search.spec.ts
│ ├── search.ts
│ ├── server.spec.ts
│ ├── server.ts
│ ├── service-connectors.ts
│ ├── sessions.ts
│ ├── stacks.ts
│ ├── strings.spec.ts
│ ├── strings.ts
│ ├── type-guards.ts
│ ├── url.ts
│ ├── user.spec.ts
│ └── user.ts
├── main.tsx
├── router
│ ├── ProtectedRoute.tsx
│ ├── Router.tsx
│ ├── loaders.ts
│ ├── queryclient.ts
│ └── routes.tsx
├── types
│ ├── .gitkeep
│ ├── analytics.ts
│ ├── artifact-versions.ts
│ ├── auth.ts
│ ├── code-repository.ts
│ ├── common.ts
│ ├── components.ts
│ ├── core.ts
│ ├── dag-visualizer.ts
│ ├── devices.ts
│ ├── flavors.ts
│ ├── forms.ts
│ ├── onboarding.ts
│ ├── pipeline-builds.ts
│ ├── pipeline-deployments.ts
│ ├── pipeline-runs.ts
│ ├── pipelines.ts
│ ├── projects.ts
│ ├── reodotdev.d.ts
│ ├── secret.ts
│ ├── server.ts
│ ├── service-accounts.ts
│ ├── service-connectors.ts
│ ├── session.ts
│ ├── stack.ts
│ ├── steps.ts
│ └── user.ts
└── vite-env.d.ts
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
/.env.example:
--------------------------------------------------------------------------------
1 | VITE_API_BASE_URL=
2 | VITE_FRONTEND_VERSION=
3 | VITE_FEATURE_OS_KEY=
4 | VITE_REO_KEY=
5 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
26 | .env*
27 | !.env.example
28 |
29 | pnpm-lock.yaml
30 | yarn.lock
31 |
32 | scripts/types.js
33 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | "eslint:recommended",
6 | "plugin:@typescript-eslint/recommended",
7 | "plugin:react-hooks/recommended"
8 | ],
9 | ignorePatterns: ["dist", ".eslintrc.cjs"],
10 | parser: "@typescript-eslint/parser",
11 | plugins: ["react-refresh"],
12 | rules: {
13 | "react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
14 | "no-mixed-spaces-and-tabs": "off",
15 | "@typescript-eslint/no-explicit-any": "warn",
16 | "@typescript-eslint/no-unused-vars": [
17 | "warn", // or "error"
18 | {
19 | argsIgnorePattern: "^_",
20 | varsIgnorePattern: "^_",
21 | caughtErrorsIgnorePattern: "^_"
22 | }
23 | ]
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/.github/workflows/check-links.yml:
--------------------------------------------------------------------------------
1 | name: Check Links
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches:
7 | - main
8 | - future
9 | pull_request:
10 | types: [opened, synchronize, ready_for_review]
11 | branches:
12 | - main
13 | - future
14 | concurrency:
15 | # New commit on branch cancels running workflows of the same branch
16 | group: ${{ github.workflow }}-${{ github.ref }}
17 | cancel-in-progress: true
18 |
19 | jobs:
20 | check:
21 | if: github.event.pull_request.draft == false
22 | runs-on: ubuntu-latest
23 | steps:
24 | - name: Checkout Repository
25 | uses: actions/checkout@v4
26 |
27 | - name: Set up Node.js
28 | uses: actions/setup-node@v4
29 | with:
30 | node-version: "20"
31 |
32 | - name: Check Links
33 | run: bash scripts/check-links.sh
34 |
--------------------------------------------------------------------------------
/.github/workflows/playwright.yml:
--------------------------------------------------------------------------------
1 | name: Playwright Tests
2 | on:
3 | push:
4 | branches: [main, master]
5 | pull_request:
6 | branches: [main, master]
7 | jobs:
8 | test:
9 | timeout-minutes: 60
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4
13 | - uses: actions/setup-node@v4
14 | with:
15 | node-version: lts/*
16 | - name: Install dependencies
17 | run: npm install -g pnpm && pnpm install
18 | - name: Install Playwright Browsers
19 | run: pnpm exec playwright install --with-deps
20 | - name: Run Playwright tests
21 | run: pnpm exec playwright test
22 | - uses: actions/upload-artifact@v4
23 | if: always()
24 | with:
25 | name: playwright-report
26 | path: playwright-report/
27 | retention-days: 30
28 |
--------------------------------------------------------------------------------
/.github/workflows/unit-tests.yml:
--------------------------------------------------------------------------------
1 | name: Build & Unit Tests
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | workflow_dispatch:
9 |
10 | jobs:
11 | test:
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - name: Checkout Repository
16 | uses: actions/checkout@v2
17 |
18 | - name: Set up Node.js
19 | uses: actions/setup-node@v2
20 | with:
21 | node-version: "20"
22 |
23 | - name: Install pnpm
24 | uses: pnpm/action-setup@v2
25 | with:
26 | version: latest
27 |
28 | - name: Install project dependencies
29 | run: pnpm install --frozen-lockfile
30 |
31 | - name: "Run Build"
32 | run: pnpm build
33 |
34 | - name: Run Unit Tests
35 | run: pnpm test:unit
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 | /test-results/
26 | /playwright-report/
27 | /blob-report/
28 | /playwright/.cache/
29 |
30 | .env*
31 | !.env.example
32 |
33 | urls.txt
34 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | npx lint-staged
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
26 | .env*
27 | !.env.example
28 |
29 | pnpm-lock.yaml
30 | yarn.lock
31 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": true,
3 | "singleQuote": false,
4 | "trailingComma": "none",
5 | "printWidth": 100,
6 | "plugins": ["prettier-plugin-tailwindcss"]
7 | }
8 |
--------------------------------------------------------------------------------
/e2e-tests/example.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "@playwright/test";
2 |
3 | test("has title", async ({ page }) => {
4 | await page.goto("https://playwright.dev/");
5 |
6 | // Expect a title "to contain" a substring.
7 | await expect(page).toHaveTitle(/Playwright/);
8 | });
9 |
10 | test("get started link", async ({ page }) => {
11 | await page.goto("https://playwright.dev/");
12 |
13 | // Click the get started link.
14 | await page.getByRole("link", { name: "Get started" }).click();
15 |
16 | // Expects page to have a heading with the name of Installation.
17 | await expect(page.getByRole("heading", { name: "Installation" })).toBeVisible();
18 | });
19 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ZenML Dashboard
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {}
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/90236a062535d2182b67f20f23cd8c8280f01921/public/favicon.ico
--------------------------------------------------------------------------------
/scripts/check-links.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Define the file patterns to search for URLs
4 | file_patterns=("*.json" "*.tsx" "*.ts")
5 |
6 | # Define the output file for the URLs
7 | output_file="urls.txt"
8 |
9 | # Find unique URLs matching the specified pattern in the specified file types
10 | find_unique_urls() {
11 | include_patterns=""
12 | for pattern in "${file_patterns[@]}"; do
13 | include_patterns+="--include=${pattern} "
14 | done
15 | grep -E -o 'https?:\/\/([a-zA-Z0-9.-]*\.)?zenml\.io[^"'\''[:space:]]*' -r --no-filename $include_patterns "$@" | sort -u
16 | }
17 |
18 | # Find unique URLs in the specified file patterns within the "src" directory
19 | find_unique_urls src | sort -u > "$output_file"
20 |
21 | # Run the link checker script
22 | node scripts/check-links.js
23 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { QueryClientProvider } from "@tanstack/react-query";
2 | import { Toaster } from "@zenml-io/react-component-library";
3 | import { Suspense } from "react";
4 | import { RouterProvider } from "react-router-dom";
5 | import { ReactFlowProvider } from "reactflow";
6 | import "reactflow/dist/style.css";
7 | import { AuthProvider } from "./context/AuthContext";
8 | import { router } from "./router/Router";
9 | import { queryClient } from "./router/queryclient";
10 | import { TourProvider } from "./components/tour/TourContext";
11 |
12 | export function App() {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/src/app/activate-server/PasswordStep.tsx:
--------------------------------------------------------------------------------
1 | import { SetPasswordForm } from "@/components/survey/SetPassword";
2 | import { useSurveyContext } from "@/components/survey/SurveyContext";
3 | import { useServerActivationContext } from "./ServerActivationContext";
4 | import { SetPasswordStepType } from "@/components/survey/form-schemas";
5 |
6 | export function SetPasswordStep() {
7 | const { setSurveyStep } = useSurveyContext();
8 | const { setServerSettings } = useServerActivationContext();
9 |
10 | function handlePasswordSubmit({ newPassword, username }: SetPasswordStepType) {
11 | setServerSettings((prev) => ({
12 | ...prev,
13 | admin_password: newPassword,
14 | admin_username: username
15 | }));
16 | setSurveyStep((prev) => prev + 1);
17 | }
18 |
19 | return (
20 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/src/app/activate-server/Wizard.tsx:
--------------------------------------------------------------------------------
1 | import StepDisplay from "@/components/survey/StepDisplay";
2 | import { SuccessStep } from "@/components/survey/SuccessStep";
3 | import { useSurveyContext } from "@/components/survey/SurveyContext";
4 | import { SetPasswordStep } from "./PasswordStep";
5 | import { ServerActivationProvider } from "./ServerActivationContext";
6 | import { SetServerNameStep } from "./ServerNameStep";
7 | import { useState } from "react";
8 |
9 | export function ServerActivationWizard() {
10 | const { surveyStep } = useSurveyContext();
11 | const [username, setUsername] = useState("");
12 |
13 | return (
14 | <>
15 |
16 |
17 | {surveyStep === 1 && }
18 | {surveyStep === 2 && }
19 | {surveyStep === 3 && (
20 |
21 | )}
22 |
23 | >
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/app/activate-server/page.tsx:
--------------------------------------------------------------------------------
1 | import { SurveyProvider } from "@/components/survey/SurveyContext";
2 | import { ServerActivationWizard } from "./Wizard";
3 |
4 | export default function ServerActivationPage() {
5 | return (
6 |
7 |
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/src/app/activate-user/AccountDetailsStep.tsx:
--------------------------------------------------------------------------------
1 | import { AccountDetailsForm } from "@/components/survey/AccountDetailsForm";
2 | import { useSurveyContext } from "@/components/survey/SurveyContext";
3 | import { AccountDetailForm } from "@/components/survey/form-schemas";
4 | import { useActivationContext } from "./ActivationContext";
5 |
6 | export function AccountDetailsStep() {
7 | const { setSurveyStep } = useSurveyContext();
8 | const { setNewUser, newUser } = useActivationContext();
9 |
10 | function handleDetailsSubmit({ fullName, getUpdates, email }: AccountDetailForm) {
11 | setNewUser((prev) => ({
12 | ...prev,
13 | ...(email ? { email } : { email: null }),
14 | full_name: fullName,
15 | email_opted_in: getUpdates
16 | }));
17 |
18 | setSurveyStep((prev) => prev + 1);
19 | }
20 |
21 | return (
22 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/src/app/activate-user/PasswordStep.tsx:
--------------------------------------------------------------------------------
1 | import { UpdatePasswordFormType } from "@/components/password/UpdatePasswordSchemas";
2 | import { SetPasswordForm } from "@/components/survey/SetPassword";
3 | import { useSurveyContext } from "@/components/survey/SurveyContext";
4 | import { useActivationContext } from "./ActivationContext";
5 |
6 | export function SetPasswordStep() {
7 | const { setSurveyStep } = useSurveyContext();
8 | const { setNewUser } = useActivationContext();
9 |
10 | function handlePasswordSubmit({ newPassword }: UpdatePasswordFormType) {
11 | setNewUser((prev) => ({
12 | ...prev,
13 | password: newPassword
14 | }));
15 | setSurveyStep((prev) => prev + 1);
16 | }
17 |
18 | return ;
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/activate-user/PrimaryUseStep.tsx:
--------------------------------------------------------------------------------
1 | import { PrimaryUseForm } from "@/components/survey/PrimaryUse";
2 | import { useSurveyContext } from "@/components/survey/SurveyContext";
3 | import { PrimaryUseFormType } from "@/components/survey/form-schemas";
4 | import { useActivationContext } from "./ActivationContext";
5 | import { UserMetadata } from "@/types/user";
6 |
7 | export function PrimaryUseStep() {
8 | const { setSurveyStep } = useSurveyContext();
9 | const { setNewUser } = useActivationContext();
10 |
11 | function handlePrimaryUseSubmit({ primaryUse }: PrimaryUseFormType) {
12 | const newMetadata: UserMetadata = {
13 | primary_use: primaryUse
14 | };
15 | setNewUser((prev) => ({
16 | ...prev,
17 | user_metadata: { ...prev.user_metadata, ...newMetadata }
18 | }));
19 | setSurveyStep((prev) => prev + 1);
20 | }
21 |
22 | return ;
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/activate-user/page.tsx:
--------------------------------------------------------------------------------
1 | import { SurveyProvider } from "@/components/survey/SurveyContext";
2 | import { ActivateWizard } from "./Wizard";
3 |
4 | export default function ActivatePage() {
5 | return (
6 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/artifacts/Fragments.tsx:
--------------------------------------------------------------------------------
1 | import { Codesnippet } from "@/components/CodeSnippet";
2 | import { Box } from "@zenml-io/react-component-library";
3 | import { useSearchParams } from "react-router-dom";
4 |
5 | export function CommandSection() {
6 | const [searchParams] = useSearchParams();
7 | const artifactName = searchParams.get("artifact");
8 |
9 | function getCommand() {
10 | if (artifactName) return `zenml artifact list --name='contains:${artifactName}'`;
11 | return "zenml artifact list";
12 | }
13 |
14 | return (
15 |
16 |
17 |
Staying Open Source?
18 |
19 | No problem! Use this CLI command to list your artifacts
20 |
21 |
22 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/app/components/[componentId]/edit/page.tsx:
--------------------------------------------------------------------------------
1 | import { Wrapper } from "@/components/wizard/Wizard";
2 | import { EditComponentConfig } from "./form-step";
3 |
4 | export default function ComponentEditPage() {
5 | return (
6 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/components/[componentId]/layout.tsx:
--------------------------------------------------------------------------------
1 | import { StackComponentsDetailHeader } from "@/components/stack-components/component-detail/Header";
2 | import { Outlet, useParams } from "react-router-dom";
3 |
4 | export default function ComponentLayout() {
5 | const { componentId } = useParams() as { componentId: string };
6 | return (
7 |
8 |
9 |
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/components/[componentId]/page.tsx:
--------------------------------------------------------------------------------
1 | import { StackComponentTabs } from "@/components/stack-components/component-detail/Tabs";
2 | import { useParams } from "react-router-dom";
3 | import { RunsBody } from "../../runs/RunsBody";
4 | import { RunsSelectorContextProvider } from "../../runs/RunsSelectorContext";
5 | import { StackList } from "../../stacks/StackList";
6 |
7 | export default function ComponentDetailPage() {
8 | const { componentId } = useParams() as { componentId: string };
9 |
10 | return (
11 |
12 | }
15 | runsTabContent={
16 |
17 |
18 |
19 | }
20 | componentId={componentId}
21 | />
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/src/app/components/create/breadcrumb.ts:
--------------------------------------------------------------------------------
1 | import { useBreadcrumbsContext } from "@/layouts/AuthenticatedLayout/BreadcrumbsContext";
2 | import { componentBreadcrumb } from "@/components/breadcrumbs/library";
3 | import { routes } from "@/router/routes";
4 | import { useEffect } from "react";
5 |
6 | export function useComponentCreateBreadcrumbs() {
7 | const { setBreadcrumbs } = useBreadcrumbsContext();
8 |
9 | useEffect(() => {
10 | setBreadcrumbs([
11 | componentBreadcrumb,
12 | {
13 | label: "Create",
14 | href: routes.components.create
15 | }
16 | ]);
17 | }, [setBreadcrumbs]);
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/components/create/header.tsx:
--------------------------------------------------------------------------------
1 | import Container from "@/assets/icons/container.svg?react";
2 | import { PageHeader } from "../../../components/PageHeader";
3 | import { useComponentCreateBreadcrumbs } from "./breadcrumb";
4 |
5 | export function CreateComponentHeader() {
6 | useComponentCreateBreadcrumbs();
7 |
8 | return (
9 |
10 |
11 |
12 |
Register new Component
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/src/app/components/create/page.tsx:
--------------------------------------------------------------------------------
1 | import { CreateComponentHeader } from "./header";
2 | import { RegisterComponentWizard } from "./wizard";
3 |
4 | export default function ComponentCreatePage() {
5 | return (
6 |
7 |
8 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/app/components/page.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentSelectorContextProvider } from "./selector-context";
2 | import { StackComponentList } from "./StackComponentList";
3 |
4 | export default function ComponentsPage() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/components/selector-context.tsx:
--------------------------------------------------------------------------------
1 | import { createDataTableConsumerContext } from "@/components/tables/helper";
2 | import { componentQueries } from "@/data/components";
3 | import { useDeleteComponent } from "@/data/components/delete-component";
4 | import { useBulkDelete } from "@/lib/bulk-delete";
5 |
6 | export const {
7 | Context: ComponentSelectorContext,
8 | ContextProvider: ComponentSelectorContextProvider,
9 | useContext: useComponentSelectorContext
10 | } = createDataTableConsumerContext();
11 |
12 | export function useComponentBulkDelete() {
13 | const { setRowSelection } = useComponentSelectorContext();
14 |
15 | const { mutateAsync } = useDeleteComponent();
16 |
17 | async function handleDelete(id: string) {
18 | await mutateAsync({ componentId: id });
19 | }
20 |
21 | return useBulkDelete({
22 | deleteFn: handleDelete,
23 | queryKeyToInvalidate: componentQueries.all,
24 | setRowSelection
25 | });
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/components/service.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 | import { StackComponentListParams } from "@/types/components";
3 | import { useSearchParams } from "react-router-dom";
4 |
5 | const DEFAULT_PAGE = 1;
6 |
7 | const filterParamsSchema = z.object({
8 | page: z.coerce.number().min(DEFAULT_PAGE).optional().default(DEFAULT_PAGE).catch(DEFAULT_PAGE),
9 | name: z.string().optional(),
10 | operator: z.enum(["and", "or"]).optional()
11 | });
12 |
13 | export function useComponentlistQueryParams(): StackComponentListParams {
14 | const [searchParams] = useSearchParams();
15 |
16 | const { page, name, operator } = filterParamsSchema.parse({
17 | page: searchParams.get("page") || undefined,
18 | name: searchParams.get("name") || undefined
19 | });
20 |
21 | return { page, name, logical_operator: operator };
22 | }
23 |
--------------------------------------------------------------------------------
/src/app/devices/verify/DeviceInfo.tsx:
--------------------------------------------------------------------------------
1 | import { Device } from "@/types/devices";
2 | import { Box } from "@zenml-io/react-component-library";
3 |
4 | type Props = {
5 | device: Device;
6 | };
7 |
8 | export function DeviceInfo({ device }: Props) {
9 | return (
10 |
11 |
12 |
13 |
IP Address
14 | {device.body?.ip_address}
15 |
16 | {device.metadata?.city && device.metadata?.country && (
17 |
18 |
Location
19 |
20 | {device.metadata?.city}, {device.metadata?.country}
21 |
22 |
23 | )}
24 |
25 |
Hostname
26 | {device.body?.hostname}
27 |
28 |
29 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/src/app/devices/verify/Success.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@zenml-io/react-component-library";
2 | import CheckCircle from "@/assets/icons/check-circle.svg?react";
3 |
4 | export function DeviceVerificationSuccess() {
5 | return (
6 |
7 |
8 |
9 |
You successfully added your device
10 |
11 | You may close this screen and return to your CLI.
12 |
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/src/app/devices/verify/service.ts:
--------------------------------------------------------------------------------
1 | import { DeviceQueryParams } from "@/types/devices";
2 | import { useSearchParams } from "react-router-dom";
3 |
4 | import { z } from "zod";
5 |
6 | const filterParamsSchema = z.object({
7 | device_id: z.string().optional(),
8 | user_code: z.string().optional()
9 | });
10 |
11 | export function useDeviceSearchParams(): DeviceQueryParams & { device_id?: string } {
12 | const [searchParams] = useSearchParams();
13 |
14 | const { device_id, user_code } = filterParamsSchema.parse({
15 | device_id: searchParams.get("device_id") || undefined,
16 | user_code: searchParams.get("user_code") || undefined
17 | });
18 |
19 | return { user_code, device_id };
20 | }
21 |
22 | export const deviceRegistrationFormSchema = z.object({
23 | trustDevice: z.boolean()
24 | });
25 |
26 | export type DeviceRegistrationFormType = z.infer;
27 |
--------------------------------------------------------------------------------
/src/app/login/page.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@zenml-io/react-component-library";
2 | import { LoginForm } from "./LoginForm";
3 |
4 | export default function LoginPage() {
5 | return (
6 |
7 |
8 |
Log in to your account
9 |
10 | Please, fill in your details to log in to your ZenML account.
11 |
12 |
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/src/app/models/Fragments.tsx:
--------------------------------------------------------------------------------
1 | import { Codesnippet } from "@/components/CodeSnippet";
2 | import { Box } from "@zenml-io/react-component-library";
3 | import { useSearchParams } from "react-router-dom";
4 |
5 | export function CommandSection() {
6 | const [searchParams] = useSearchParams();
7 | const modelName = searchParams.get("model");
8 |
9 | function getCommand() {
10 | if (modelName) return `zenml model list --name='contains:${modelName}'`;
11 | return "zenml model list";
12 | }
13 |
14 | return (
15 |
16 |
17 |
Staying Open Source?
18 |
19 | No problem! Use this CLI command to list your models
20 |
21 |
22 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/app/onboarding/Header.tsx:
--------------------------------------------------------------------------------
1 | import { useCurrentUser } from "@/data/users/current-user-query";
2 | import { getUsername } from "@/lib/user";
3 | import { Skeleton } from "@zenml-io/react-component-library";
4 |
5 | export function HeaderOnboardingBox() {
6 | return (
7 |
8 |
9 |
10 | Hi
11 |
12 |
13 |
14 | Welcome to ZenML. This is the beginning to get the most advantage of all the Pro features
15 | of ZenML.
16 |
17 |
18 |
19 | );
20 | }
21 |
22 | function Username() {
23 | const user = useCurrentUser();
24 |
25 | if (user.isError) return null;
26 | if (user.isPending) return ;
27 | const name = getUsername(user.data);
28 | return <>{name ? `, ${name}` : ""}>;
29 | }
30 |
--------------------------------------------------------------------------------
/src/app/onboarding/Setup/constant.ts:
--------------------------------------------------------------------------------
1 | export const SCRIPT_CONFIG = {
2 | aws: {
3 | stackName: "zenml-aws",
4 | region: "eu-central-1",
5 | scriptLink:
6 | "https://raw.githubusercontent.com/zenml-io/zenml/refs/heads/main/infra/scripts/aws-artifact-store-setup.sh",
7 | cliName: "AWS CLI",
8 | integration: "s3"
9 | },
10 | azure: {
11 | stackName: "zenml-azure",
12 | region: "francecentral",
13 | scriptLink:
14 | "https://raw.githubusercontent.com/zenml-io/zenml/refs/heads/main/infra/scripts/azure-artifact-store-setup.sh",
15 | cliName: "Azure CLI",
16 | integration: "azure"
17 | },
18 |
19 | gcp: {
20 | stackName: "zenml-gcp",
21 | region: "europe-west1",
22 | scriptLink:
23 | "https://raw.githubusercontent.com/zenml-io/zenml/refs/heads/main/infra/scripts/gcp-artifact-store-setup.sh",
24 | cliName: "gcloud CLI",
25 | integration: "gcp"
26 | }
27 | } as const;
28 |
--------------------------------------------------------------------------------
/src/app/onboarding/Setup/set-project.tsx:
--------------------------------------------------------------------------------
1 | import { Codesnippet } from "@/components/CodeSnippet";
2 |
3 | type Props = {
4 | projectName: string;
5 | };
6 |
7 | export function SetProject({ projectName }: Props) {
8 | return (
9 |
10 |
Set your project
11 |
"}`}
14 | />
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/pipelines/PipelinesTab/ButtonGroup.tsx:
--------------------------------------------------------------------------------
1 | import { DeletePipelineAlert } from "./DeletePipelineAlert";
2 | import { usePipelineSelectorContext } from "./PipelineSelectorContext";
3 |
4 | export function PipelinesButtonGroup() {
5 | const { selectedRowCount } = usePipelineSelectorContext();
6 | return (
7 |
8 |
{`${selectedRowCount} Pipeline${selectedRowCount > 1 ? "s" : ""} selected`}
9 |
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/pipelines/PipelinesTab/PipelineSelectorContext.tsx:
--------------------------------------------------------------------------------
1 | import { createDataTableConsumerContext } from "@/components/tables/helper";
2 | import { pipelineQueries } from "@/data/pipelines";
3 | import { useDeletePipeline } from "@/data/pipelines/delete-pipeline";
4 |
5 | import { useBulkDelete } from "@/lib/bulk-delete";
6 |
7 | export const {
8 | Context: PipelineSelectorContext,
9 | ContextProvider: PipelineSelectorContextProvider,
10 | useContext: usePipelineSelectorContext
11 | } = createDataTableConsumerContext();
12 |
13 | export function usePipelineBulkDelete() {
14 | const { setRowSelection } = usePipelineSelectorContext();
15 |
16 | const { mutateAsync } = useDeletePipeline();
17 |
18 | async function handleDelete(id: string) {
19 | await mutateAsync({ pipelineId: id });
20 | }
21 |
22 | return useBulkDelete({
23 | deleteFn: handleDelete,
24 | queryKeyToInvalidate: pipelineQueries.all,
25 | setRowSelection
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/src/app/pipelines/PipelinesTab/service.ts:
--------------------------------------------------------------------------------
1 | import { PipelineListParams } from "@/types/pipelines";
2 | import { useSearchParams } from "react-router-dom";
3 |
4 | import { z } from "zod";
5 |
6 | const DEFAULT_PAGE = 1;
7 |
8 | const filterParamsSchema = z.object({
9 | page: z.coerce.number().min(DEFAULT_PAGE).optional().default(DEFAULT_PAGE).catch(DEFAULT_PAGE),
10 | name: z.string().optional(),
11 | operator: z.enum(["and", "or"]).optional()
12 | });
13 |
14 | export function usePipelineOverviewSearchParams(): PipelineListParams {
15 | const [searchParams] = useSearchParams();
16 |
17 | const { page, name, operator } = filterParamsSchema.parse({
18 | page: searchParams.get("page") || undefined,
19 | name: searchParams.get("name") || undefined,
20 | operator: searchParams.get("operator") || undefined
21 | });
22 |
23 | return { page, name, logical_operator: operator };
24 | }
25 |
--------------------------------------------------------------------------------
/src/app/pipelines/[namespace]/Header.tsx:
--------------------------------------------------------------------------------
1 | import Pipeline from "@/assets/icons/pipeline.svg?react";
2 | import { PageHeader } from "@/components/PageHeader";
3 | type Props = {
4 | namespace: string;
5 | };
6 |
7 | export function Header({ namespace }: Props) {
8 | return (
9 |
10 |
11 |
12 |
{decodeURIComponent(namespace)}
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/src/app/pipelines/[namespace]/breadcrumb.ts:
--------------------------------------------------------------------------------
1 | import { pipelineBreadcrumb } from "@/components/breadcrumbs/library";
2 | import { useBreadcrumbsContext } from "@/layouts/AuthenticatedLayout/BreadcrumbsContext";
3 | import { routes } from "@/router/routes";
4 | import { useEffect } from "react";
5 |
6 | export function usePipelineDetailBreadcrumbs(namespace?: string) {
7 | const { setBreadcrumbs } = useBreadcrumbsContext();
8 |
9 | useEffect(() => {
10 | if (namespace) {
11 | setBreadcrumbs([
12 | pipelineBreadcrumb,
13 | {
14 | label: namespace,
15 | href: routes.projects.pipelines.namespace(namespace)
16 | }
17 | ]);
18 | }
19 | }, [setBreadcrumbs, namespace]);
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/pipelines/[namespace]/page.tsx:
--------------------------------------------------------------------------------
1 | import { useParams } from "react-router-dom";
2 | import { RunsSelectorContextProvider } from "../../runs/RunsSelectorContext";
3 | import { usePipelineDetailBreadcrumbs } from "./breadcrumb";
4 | import { Header } from "./Header";
5 | import { PipelineRunsTable } from "./RunsTable";
6 |
7 | export default function PipelineNamespacePage() {
8 | const { namespace } = useParams() as { namespace: string };
9 | usePipelineDetailBreadcrumbs(namespace);
10 |
11 | return (
12 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/pipelines/[namespace]/service.ts:
--------------------------------------------------------------------------------
1 | import { PipelineRunOvervieweParams } from "@/types/pipeline-runs";
2 | import { useSearchParams } from "react-router-dom";
3 | import { z } from "zod";
4 |
5 | const DEFAULT_PAGE = 1;
6 |
7 | const filterParamsSchema = z.object({
8 | page: z.coerce.number().min(DEFAULT_PAGE).optional().default(DEFAULT_PAGE).catch(DEFAULT_PAGE),
9 | name: z.string().optional(),
10 |
11 | operator: z.enum(["and", "or"]).optional()
12 | });
13 |
14 | export function usePipelineRunParams(): PipelineRunOvervieweParams {
15 | const [searchParams] = useSearchParams();
16 |
17 | const { page, name, operator } = filterParamsSchema.parse({
18 | page: searchParams.get("page") || undefined,
19 | name: searchParams.get("name") || undefined
20 | });
21 |
22 | return { page, name, logical_operator: operator };
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/pipelines/page.tsx:
--------------------------------------------------------------------------------
1 | import { PipelineSelectorContextProvider } from "./PipelinesTab/PipelineSelectorContext";
2 | import { PipelinesBody } from "./PipelinesTab/PipelinesBody";
3 |
4 | export default function PipelinesPage() {
5 | return (
6 |
7 |
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/src/app/projects/page.tsx:
--------------------------------------------------------------------------------
1 | import { ProjectList } from "./project-list";
2 | import { ProjectsSearchBar } from "./searchbar";
3 |
4 | export default function ProjectsPage() {
5 | return (
6 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/run-templates/page.tsx:
--------------------------------------------------------------------------------
1 | import { CtaSection } from "./TemplateBody";
2 |
3 | export default function TemplatesPage() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/app/runs/ButtonGroup.tsx:
--------------------------------------------------------------------------------
1 | import { DeleteRunAlert } from "./DeleteRunAlert";
2 | import { useRunsSelectorContext } from "./RunsSelectorContext";
3 |
4 | export function RunsButtonGroup() {
5 | const { selectedRowCount } = useRunsSelectorContext();
6 | return (
7 |
8 |
{`${selectedRowCount} Run${selectedRowCount > 1 ? "s" : ""} selected`}
9 |
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/runs/RunsSelectorContext.tsx:
--------------------------------------------------------------------------------
1 | import { createDataTableConsumerContext } from "@/components/tables/helper";
2 | import { useDeleteRun } from "@/data/pipeline-runs/delete-run";
3 | import { useBulkDelete } from "@/lib/bulk-delete";
4 |
5 | export const {
6 | Context: RunsSelectorContext,
7 | ContextProvider: RunsSelectorContextProvider,
8 | useContext: useRunsSelectorContext
9 | } = createDataTableConsumerContext();
10 |
11 | export function useRunBulkDelete() {
12 | const { setRowSelection } = useRunsSelectorContext();
13 |
14 | const { mutateAsync } = useDeleteRun();
15 |
16 | async function handleDelete(id: string) {
17 | await mutateAsync({ runId: id });
18 | }
19 |
20 | return useBulkDelete({
21 | deleteFn: handleDelete,
22 | queryKeyToInvalidate: ["runs"],
23 | setRowSelection
24 | });
25 | }
26 |
--------------------------------------------------------------------------------
/src/app/runs/[id]/_Tabs/Overview/index.tsx:
--------------------------------------------------------------------------------
1 | import { AlertPanels } from "./AlertPanels";
2 | import { Details } from "./Details";
3 | import { OrchestratorCollapsible } from "./Orchestrator";
4 |
5 | export function OverviewTab() {
6 | return (
7 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/app/runs/[id]/_Tabs/service.ts:
--------------------------------------------------------------------------------
1 | import { useSearchParams } from "react-router-dom";
2 | import { z } from "zod";
3 |
4 | const tabParamSchema = z.object({
5 | tab: z
6 | .enum(["overview", "configuration", "metadata", "stack"])
7 | .optional()
8 | .default("overview")
9 | .catch("overview")
10 | });
11 |
12 | export function useSelectedTab() {
13 | const [searchParams] = useSearchParams();
14 | const { tab } = tabParamSchema.parse({
15 | tab: searchParams.get("tab") || undefined
16 | });
17 |
18 | return tab;
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/runs/page.tsx:
--------------------------------------------------------------------------------
1 | import { RunsBody } from "./RunsBody";
2 | import { RunsSelectorContextProvider } from "./RunsSelectorContext";
3 |
4 | export default function RunsPage() {
5 | return (
6 |
7 |
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/[id]/components/page.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentSelectorContextProvider } from "@/app/components/selector-context";
2 | import { StackComponentList } from "@/app/components/StackComponentList";
3 | import { useParams } from "react-router-dom";
4 |
5 | export default function ConnectorComponentPage() {
6 | const { connectorId } = useParams() as { connectorId: string };
7 |
8 | return (
9 |
10 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/[id]/configuration/page.tsx:
--------------------------------------------------------------------------------
1 | import { BasicParams } from "./params";
2 | import { ConfigurationPanel } from "./configuration";
3 |
4 | export default function ConnectorConfigPage() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/[id]/resources/page.tsx:
--------------------------------------------------------------------------------
1 | import { useResourcesContext } from "@/layouts/connectors-detail/resources-context";
2 | import { ResourcesList } from "./resources-list";
3 | import { ConnectorVerifyBox } from "./verify-box";
4 | export default function ResourcesContent() {
5 | const { resources } = useResourcesContext();
6 | if (resources === null) {
7 | return ;
8 | }
9 |
10 | return ;
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/[id]/resources/verify-box.tsx:
--------------------------------------------------------------------------------
1 | import AlertCircle from "@/assets/icons/alert-circle.svg?react";
2 | import { VerifyButton } from "./verify-button";
3 | import { FallbackIcon } from "@/components/fallback/icon";
4 |
5 | export function ConnectorVerifyBox() {
6 | return (
7 |
8 |
9 |
} />
10 |
11 |
Please verify your service connector
12 |
13 | We'll check your connection and load available resources.
14 |
15 | This may take up to a minute.
16 |
17 |
18 |
19 | Verify now
20 |
21 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/create/breadcrumbs.ts:
--------------------------------------------------------------------------------
1 | import { connectorBreadcrumb } from "@/components/breadcrumbs/library";
2 | import { useBreadcrumbsContext } from "@/layouts/AuthenticatedLayout/BreadcrumbsContext";
3 | import { routes } from "@/router/routes";
4 | import { useEffect } from "react";
5 |
6 | export function useCreateConnectorBreadcrumbs() {
7 | const { setBreadcrumbs } = useBreadcrumbsContext();
8 |
9 | useEffect(() => {
10 | setBreadcrumbs([
11 | ...connectorBreadcrumb,
12 | {
13 | label: "New Connector",
14 | href: routes.settings.connectors.create
15 | }
16 | ]);
17 | }, [setBreadcrumbs]);
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/create/configuration/debounced-check.ts:
--------------------------------------------------------------------------------
1 | import { fetchServiceConnectorList } from "@/data/service-connectors/connector-list";
2 | import AwesomeDebouncePromise from "awesome-debounce-promise";
3 |
4 | export const validateConnectorName = AwesomeDebouncePromise(async (name: string) => {
5 | try {
6 | const resData = await fetchServiceConnectorList({ name });
7 | if (resData.total > 0) {
8 | return false;
9 | }
10 | return true;
11 | } catch (error) {
12 | console.error(error);
13 | return false;
14 | }
15 | }, 500);
16 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/create/configuration/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const connectorConfigurationSchema = z.object({
4 | description: z.string().trim(),
5 | authMethod: z.string().trim().min(1, "Auth method is required"),
6 | resourceType: z.string().trim().min(1, "Resource type is required"),
7 | isValid: z.boolean().nullable(),
8 | skipValidation: z.boolean()
9 | });
10 |
11 | // define a zod schema for this ts type {name: string; skipValidation:boolean; [key: string]: unknown}
12 |
13 | export type ConnectorConfigForm = z.infer & {
14 | name: string;
15 | [key: string]: unknown;
16 | };
17 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/create/configuration/skip-verify.tsx:
--------------------------------------------------------------------------------
1 | import { Switch } from "@zenml-io/react-component-library/components/client";
2 | import { Controller, useFormContext } from "react-hook-form";
3 | import { ConnectorConfigForm } from "./schema";
4 | export function SkipVerify() {
5 | const { control } = useFormContext();
6 |
7 | return (
8 | (
12 |
13 |
14 |
15 | Register without validation
16 |
17 | {error &&
{error.message?.toString()}
}
18 |
19 | )}
20 | />
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/create/connector-type/description.tsx:
--------------------------------------------------------------------------------
1 | import Cloud from "@/assets/icons/cloud.svg?react";
2 |
3 | export function StepDescription() {
4 | return (
5 |
6 |
7 |
8 |
Select a Service Connector Type
9 |
10 |
11 | Select the type of service connector you want to create. This will allow you to integrate
12 | your cloud resources with ZenML for model deployment and management.
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/create/connector-type/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const connectorTypeSelectSchema = z.object({
4 | connectorType: z.string().trim().min(1)
5 | });
6 |
7 | export type ConnectorTypeSelectForm = z.infer;
8 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/create/header.tsx:
--------------------------------------------------------------------------------
1 | import { PageHeader } from "@/components/PageHeader";
2 | import { useCreateConnectorBreadcrumbs } from "./breadcrumbs";
3 |
4 | export function CreateConnectorHeader() {
5 | useCreateConnectorBreadcrumbs();
6 |
7 | return (
8 |
9 | Register a Service Connector
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/create/page.tsx:
--------------------------------------------------------------------------------
1 | import { WizardProvider } from "@/context/WizardContext";
2 | import { CreateConnectorHeader } from "./header";
3 | import { LeftSideMenu } from "@/components/wizard/LeftSideMenu";
4 | import { ConnectorRegistrationWizard } from "./wizard";
5 | const menuEntries = ["Connector Type", "Configuration"];
6 |
7 | export default function RegisterConnector() {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/create/success/success-box.tsx:
--------------------------------------------------------------------------------
1 | import CheckCircle from "@/assets/icons/check-circle.svg?react";
2 |
3 | type Props = {
4 | connectorName: string;
5 | };
6 |
7 | export function ConnectorSuccessBox({ connectorName }: Props) {
8 | return (
9 |
10 |
11 |
12 | You successfully registered the service connector `{connectorName}` and can now create new
13 | resources
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/create/success/table.tsx:
--------------------------------------------------------------------------------
1 | import { DataTable } from "@zenml-io/react-component-library/components/client";
2 | import { ServiceConnector } from "@/types/service-connectors";
3 | import { useServiceConnectorListColumns } from "./columns";
4 |
5 | type Props = {
6 | connector: ServiceConnector;
7 | };
8 | export function ConnectorSuccessTable({ connector }: Props) {
9 | const columns = useServiceConnectorListColumns();
10 | return (
11 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/create/wizard.tsx:
--------------------------------------------------------------------------------
1 | import { useWizardContext } from "@/context/WizardContext";
2 | import { RegisterConnectorProvider } from "./create-context";
3 | import { ConnectorTypeStep } from "./connector-type";
4 | import { ConnectorConfigurationStep } from "./configuration";
5 | import { ConnectorSuccessStep } from "./success";
6 |
7 | export function ConnectorRegistrationWizard() {
8 | return (
9 |
10 |
11 |
12 | );
13 | }
14 |
15 | function WizardContent() {
16 | const { currentStep } = useWizardContext();
17 |
18 | if (currentStep === 0) return ;
19 | if (currentStep === 1) return ;
20 | if (currentStep === 2) return ;
21 | }
22 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/header.tsx:
--------------------------------------------------------------------------------
1 | export function ServiceConnectorListHeader() {
2 | return (
3 |
4 |
Service Connectors
5 |
6 | Configure and manage your service connectors.{" "}
7 |
13 | Learn More
14 |
15 |
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/list-content.tsx:
--------------------------------------------------------------------------------
1 | import { SearchBar } from "./searchbar";
2 | import { ServiceConnectorListQueryParams } from "@/types/service-connectors";
3 | import { useConnectorListParams } from "./use-connector-params";
4 | import { ServiceConnectorList } from "./connector-list";
5 |
6 | export function ServiceConnectorListContent() {
7 | const params = useConnectorListParams();
8 |
9 | const queryParams: ServiceConnectorListQueryParams = {
10 | ...params,
11 | sort_by: "desc:updated"
12 | };
13 | return (
14 | <>
15 |
16 |
17 | >
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/new-connector-button.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@zenml-io/react-component-library/components/server";
2 | import Plus from "@/assets/icons/plus.svg?react";
3 | import { routes } from "@/router/routes";
4 |
5 | import { Link } from "react-router-dom";
6 | export function NewConnectorButton() {
7 | return (
8 |
9 |
10 |
11 | New Connector
12 |
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/page.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@zenml-io/react-component-library/components/server";
2 | import { ServiceConnectorListHeader } from "./header";
3 | import { ServiceConnectorListContent } from "./list-content";
4 | import { ConnectorSelectorContextProvider } from "./selector-context";
5 | export default function ConnectorsPage() {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/selector-context.ts:
--------------------------------------------------------------------------------
1 | import { createDataTableConsumerContext } from "@/components/tables/helper";
2 | import { serviceConnectorQueries } from "@/data/service-connectors";
3 | import { useDeleteConnector } from "@/data/service-connectors/delete-service-connector";
4 | import { useBulkDelete } from "@/lib/bulk-delete";
5 |
6 | export const {
7 | Context: ConnectorSelectorContext,
8 | ContextProvider: ConnectorSelectorContextProvider,
9 | useContext: useConnectorSelectorContext
10 | } = createDataTableConsumerContext();
11 |
12 | export function useConnectorBulkDelete() {
13 | const { setRowSelection } = useConnectorSelectorContext();
14 |
15 | const { mutateAsync } = useDeleteConnector();
16 |
17 | async function handleDelete(id: string) {
18 | await mutateAsync({ connectorId: id });
19 | }
20 |
21 | return useBulkDelete({
22 | deleteFn: handleDelete,
23 | queryKeyToInvalidate: [...serviceConnectorQueries.connectors],
24 | setRowSelection
25 | });
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/settings/connectors/use-connector-params.ts:
--------------------------------------------------------------------------------
1 | import { ServiceConnectorListQueryParams } from "@/types/service-connectors";
2 | import { useSearchParams } from "react-router-dom";
3 | import { z } from "zod";
4 |
5 | const DEFAULT_PAGE = 1;
6 |
7 | const filterParamsSchema = z.object({
8 | page: z.coerce.number().min(DEFAULT_PAGE).optional().default(DEFAULT_PAGE).catch(DEFAULT_PAGE),
9 | name: z.string().optional(),
10 | operator: z.enum(["and", "or"]).optional()
11 | });
12 |
13 | export function useConnectorListParams(): ServiceConnectorListQueryParams {
14 | const [searchParams] = useSearchParams();
15 |
16 | const { page, name, operator } = filterParamsSchema.parse({
17 | page: searchParams.get("page") || undefined,
18 | name: searchParams.get("name") || undefined,
19 | sortBy: searchParams.get("sort_by") || undefined
20 | });
21 |
22 | return { page, name, logical_operator: operator };
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/settings/general/GeneralFormSchema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const generalFormSchema = z.object({
4 | serverName: z.string().min(1)
5 | });
6 |
7 | export type GeneralFormType = z.infer;
8 |
--------------------------------------------------------------------------------
/src/app/settings/general/page.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@zenml-io/react-component-library";
2 | import { GeneralForm } from "./GeneralForm";
3 |
4 | export default function GeneralSettingsPage() {
5 | return (
6 |
7 | General
8 |
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/settings/members/page.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@zenml-io/react-component-library";
2 | import MembersTable from "./MembersTable";
3 |
4 | export default function MembersPage() {
5 | return (
6 |
7 | Members
8 |
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/settings/members/service.ts:
--------------------------------------------------------------------------------
1 | import { ListUserParams } from "@/types/user";
2 | import { useSearchParams } from "react-router-dom";
3 |
4 | import { z } from "zod";
5 |
6 | const DEFAULT_PAGE = 1;
7 |
8 | const filterParamsSchema = z.object({
9 | page: z.coerce.number().min(DEFAULT_PAGE).optional().default(DEFAULT_PAGE).catch(DEFAULT_PAGE),
10 | name: z.string().optional(),
11 | operator: z.enum(["and", "or"]).optional()
12 | });
13 |
14 | export function useUserOverviewSearchParams(): ListUserParams {
15 | const [searchParams] = useSearchParams();
16 |
17 | const { page, name, operator } = filterParamsSchema.parse({
18 | page: searchParams.get("page") || undefined,
19 | name: searchParams.get("name") || undefined,
20 | operator: searchParams.get("operator") || undefined
21 | });
22 |
23 | return { page, name, logical_operator: operator };
24 | }
25 |
--------------------------------------------------------------------------------
/src/app/settings/notifications/page.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Skeleton } from "@zenml-io/react-component-library";
2 | import { NotificationsForm } from "./NotificationsForm";
3 | import { useServerSettings } from "@/data/server/get-server-settings";
4 |
5 | export default function NotificationsPage() {
6 | const { data } = useServerSettings({ throwOnError: true });
7 |
8 | return (
9 |
10 |
11 |
Notifications
12 |
13 | ZenML comes equipped with default widgets designed to enhance your experience by analyzing
14 | usage patterns, gathering your feedback, and ensuring you stay informed about our latest
15 | updates and features.
16 |
17 |
18 |
19 | {data ? : }
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/settings/profile/UpdateProfileSchema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const UpdateProfileFormSchema = z
4 | .object({
5 | fullName: z.union([z.string(), z.literal("")]),
6 | username: z.union([z.string(), z.literal("")]),
7 | email: z.union([z.string().email(), z.literal("")])
8 | })
9 | .refine((data) => {
10 | return data.fullName !== "" || data.username !== "" || data.email !== "";
11 | });
12 |
13 | export type UpdateProfileForm = z.infer;
14 |
--------------------------------------------------------------------------------
/src/app/settings/profile/page.tsx:
--------------------------------------------------------------------------------
1 | import { Avatar, AvatarFallback, Box, Skeleton } from "@zenml-io/react-component-library";
2 | import { UpdateProfileForm } from "./UpdateProfileForm";
3 | import { useCurrentUser } from "@/data/users/current-user-query";
4 |
5 | export default function ProfilePage() {
6 | const { data: user } = useCurrentUser();
7 |
8 | return (
9 |
10 | Profile
11 |
12 | {user ?
:
}
13 |
14 | {user ? (
15 |
16 |
17 | {user.name[0]}
18 |
19 |
20 | ) : (
21 |
22 | )}
23 |
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/settings/repositories/page.tsx:
--------------------------------------------------------------------------------
1 | import { Box, ProgressOutstanding } from "@zenml-io/react-component-library";
2 | import { CommandSection, HeaderBox, InfoBox } from "./Fragments";
3 |
4 | export default function RepositoriesPage() {
5 | return (
6 |
7 | Repositories
8 |
9 |
10 |
11 |
12 | Administering your Code Repositories
13 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/settings/secrets/[id]/breadcrumbs.ts:
--------------------------------------------------------------------------------
1 | import { SecretsBreadcrumb } from "@/components/breadcrumbs/library";
2 | import { useBreadcrumbsContext } from "@/layouts/AuthenticatedLayout/BreadcrumbsContext";
3 | import { routes } from "@/router/routes";
4 | import { Secret } from "@/types/secret";
5 | import { useEffect } from "react";
6 |
7 | export function useSecretDetailBreadcrumbs(secret?: Secret) {
8 | const { setBreadcrumbs } = useBreadcrumbsContext();
9 |
10 | useEffect(() => {
11 | if (secret) {
12 | setBreadcrumbs([
13 | ...SecretsBreadcrumb,
14 | {
15 | label: secret.name,
16 | href: routes.settings.secrets.detail(secret.id)
17 | }
18 | ]);
19 | }
20 | }, [secret, setBreadcrumbs]);
21 | }
22 |
--------------------------------------------------------------------------------
/src/app/settings/secrets/form-schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const secretFormSchema = z.object({
4 | secretName: z.string().min(1, "Secret Name is required"),
5 | keysValues: z.array(
6 | z.object({
7 | key: z.string().min(1, "Key is required"),
8 | value: z.string().min(1, "Value is required"),
9 | showPassword: z.boolean().optional()
10 | })
11 | )
12 | });
13 |
14 | export type SecretFormType = z.infer;
15 |
--------------------------------------------------------------------------------
/src/app/settings/secrets/page.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@zenml-io/react-component-library";
2 | import SecretsTable from "./SecretsTable";
3 |
4 | export default function SecretsPage() {
5 | return (
6 |
7 |
8 |
Secrets
9 |
10 |
11 | Configure and manage your pipeline secrets and configurations.{" "}
12 |
18 | Learn More
19 |
20 |
21 |
22 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/app/settings/secrets/service.ts:
--------------------------------------------------------------------------------
1 | import { ListSecretsParams } from "@/types/secret";
2 | import { useSearchParams } from "react-router-dom";
3 |
4 | import { z } from "zod";
5 |
6 | const DEFAULT_PAGE = 1;
7 |
8 | const filterParamsSchema = z.object({
9 | page: z.coerce.number().min(DEFAULT_PAGE).optional().default(DEFAULT_PAGE).catch(DEFAULT_PAGE),
10 | name: z.string().optional(),
11 | operator: z.enum(["and", "or"]).optional()
12 | });
13 |
14 | export function useSecretOverviewSearchParams(): ListSecretsParams {
15 | const [searchParams] = useSearchParams();
16 |
17 | const { page, name, operator } = filterParamsSchema.parse({
18 | page: searchParams.get("page") || undefined,
19 | name: searchParams.get("name") || undefined,
20 | operator: searchParams.get("operator") || undefined
21 | });
22 |
23 | return { page, name, logical_operator: operator };
24 | }
25 |
--------------------------------------------------------------------------------
/src/app/settings/service-accounts/ButtonGroup.tsx:
--------------------------------------------------------------------------------
1 | import { DeleteServiceAccountAlert } from "./DeleteAlert";
2 | import { useServiceAccountSelectorContext } from "./SelectorContext";
3 |
4 | export function ServiceAccountsButtonGroup() {
5 | const { selectedRowCount } = useServiceAccountSelectorContext();
6 | return (
7 |
8 |
{`${
9 | selectedRowCount
10 | } Service Account${selectedRowCount > 1 ? "s" : ""} selected`}
11 |
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/settings/service-accounts/Fallback.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@zenml-io/react-component-library/components/server";
2 | import { AddServiceAccountDialog } from "./AddServiceAccount";
3 |
4 | export default function ServiceAccountFallback() {
5 | return (
6 |
7 |
8 |
9 | There are no service accounts yet.
10 |
11 |
12 | Create your first one now to automate your processes securely with ZenML.
13 |
14 |
15 |
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/settings/service-accounts/[service-account-id]/ButtonGroup.tsx:
--------------------------------------------------------------------------------
1 | import { DeleteApiKey } from "./DeleteApiKeyDialog";
2 | import { useApiKeySelectorContext } from "./SelectorContext";
3 |
4 | export function ApiKeyButtonGroup({ serviceAccountId }: { serviceAccountId: string }) {
5 | const { selectedRowCount } = useApiKeySelectorContext();
6 | return (
7 |
8 |
{`${
9 | selectedRowCount
10 | } Api Key${selectedRowCount > 1 ? "s" : ""} selected`}
11 |
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/settings/service-accounts/[service-account-id]/Fallback.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@zenml-io/react-component-library/components/server";
2 | import { useParams } from "react-router-dom";
3 | import { AddApiKeyDialog } from "./AddApiKeyDialog";
4 |
5 | export default function ApiKeyFallback() {
6 | const { serviceAccountId } = useParams() as { serviceAccountId: string };
7 |
8 | return (
9 |
10 |
11 |
12 | This service account doesn't have any API Keys yet
13 |
14 |
15 | Generate your first API Key to enable secure interactions with the ZenML Server.
16 |
17 |
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/src/app/settings/service-accounts/[service-account-id]/breadcrumb.ts:
--------------------------------------------------------------------------------
1 | import { serviceAccountBreadcrumb } from "@/components/breadcrumbs/library";
2 | import { useBreadcrumbsContext } from "@/layouts/AuthenticatedLayout/BreadcrumbsContext";
3 | import { routes } from "@/router/routes";
4 | import { ServiceAccount } from "@/types/service-accounts";
5 | import { useEffect } from "react";
6 |
7 | export function useServiceAccountDetailBreadcrumbs(serviceAccount?: ServiceAccount) {
8 | const { setBreadcrumbs } = useBreadcrumbsContext();
9 |
10 | useEffect(() => {
11 | if (serviceAccount) {
12 | setBreadcrumbs([
13 | ...serviceAccountBreadcrumb,
14 | {
15 | label: serviceAccount.name,
16 | href: routes.settings.service_accounts.detail(serviceAccount.id)
17 | }
18 | ]);
19 | }
20 | }, [setBreadcrumbs, serviceAccount]);
21 | }
22 |
--------------------------------------------------------------------------------
/src/app/settings/service-accounts/form-schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const serviceAccountFormSchema = z.object({
4 | name: z.string().min(1, "Service Account Name is required"),
5 | description: z.string()
6 | });
7 |
8 | export type ServiceAccountFormType = z.infer;
9 |
10 | export const createServiceAccountFormSchema = serviceAccountFormSchema.extend({
11 | createDefault: z.boolean()
12 | });
13 |
14 | export type CreateServiceAccountForm = z.infer;
15 |
--------------------------------------------------------------------------------
/src/app/settings/service-accounts/service.ts:
--------------------------------------------------------------------------------
1 | import { useSearchParams } from "react-router-dom";
2 |
3 | import { ListServiceAccountsParams } from "@/types/service-accounts";
4 | import { z } from "zod";
5 |
6 | const DEFAULT_PAGE = 1;
7 |
8 | const filterParamsSchema = z.object({
9 | page: z.coerce.number().min(DEFAULT_PAGE).optional().default(DEFAULT_PAGE).catch(DEFAULT_PAGE),
10 | name: z.string().optional(),
11 | operator: z.enum(["and", "or"]).optional()
12 | });
13 |
14 | export function useServiceAccountOverviewSearchParams(): ListServiceAccountsParams {
15 | const [searchParams] = useSearchParams();
16 |
17 | const { page, name, operator } = filterParamsSchema.parse({
18 | page: searchParams.get("page") || undefined,
19 | name: searchParams.get("name") || undefined,
20 | operator: searchParams.get("operator") || undefined
21 | });
22 |
23 | return { page, name, logical_operator: operator };
24 | }
25 |
--------------------------------------------------------------------------------
/src/app/stacks/ResumeBanner.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { parseWizardData } from "./create/new-infrastructure/persist";
3 | import { parseWizardData as parseTerraform } from "./create/terraform/persist";
4 | import { ResumeStackBanner } from "./ResumeStackBanner";
5 | import { ResumeTerraformBanner } from "./ResumeTerraformBanner";
6 |
7 | export function ResumeBanners() {
8 | const [hasResumeableStack, setResumeableStack] = useState(parseWizardData().success);
9 | const [hasResumeableTerraform, setResumeableTerraform] = useState(
10 | parseTerraform().success
11 | );
12 |
13 | if (!hasResumeableStack && !hasResumeableTerraform) return null;
14 |
15 | return (
16 |
17 | {hasResumeableStack && }
18 | {hasResumeableTerraform && (
19 |
20 | )}
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/stacks/create/LocalOverlay.tsx:
--------------------------------------------------------------------------------
1 | export function LocalOverlay() {
2 | return (
3 |
4 |
5 | This option is not available for local deployments
6 |
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/app/stacks/create/breadcrumb.ts:
--------------------------------------------------------------------------------
1 | import { stacksBreadcrumb } from "@/components/breadcrumbs/library";
2 | import { useBreadcrumbsContext } from "@/layouts/AuthenticatedLayout/BreadcrumbsContext";
3 | import { routes } from "@/router/routes";
4 | import { useEffect } from "react";
5 |
6 | export function useStackCreateBreadcrumbs() {
7 | const { setBreadcrumbs } = useBreadcrumbsContext();
8 |
9 | useEffect(() => {
10 | setBreadcrumbs([
11 | stacksBreadcrumb,
12 | {
13 | label: "New Stack",
14 | href: routes.stacks.create.index
15 | }
16 | ]);
17 | }, [setBreadcrumbs]);
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/stacks/create/components/CancelButton.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@zenml-io/react-component-library/components/server";
2 | import { useNavigate } from "react-router-dom";
3 | import { clearWizardData } from "../new-infrastructure/persist";
4 | import { routes } from "@/router/routes";
5 |
6 | export function CancelButton() {
7 | const navigate = useNavigate();
8 |
9 | function cancel() {
10 | clearWizardData();
11 | navigate(routes.stacks.create.index);
12 | }
13 | return (
14 | cancel()} intent="secondary" size="md">
15 | Cancel
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/stacks/create/components/WizardFooter.tsx:
--------------------------------------------------------------------------------
1 | import { Footer } from "@/components/wizard/Wizard";
2 | import { PropsWithChildren } from "react";
3 | import { CancelButton } from "./CancelButton";
4 |
5 | type Props = {
6 | displayCancel?: boolean;
7 | };
8 | export function StackWizardFooter({ children, displayCancel = true }: PropsWithChildren) {
9 | return (
10 |
11 | {displayCancel && }
12 | {children}
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/src/app/stacks/create/components/sharedSchema.ts:
--------------------------------------------------------------------------------
1 | import { validateStackName } from "@/lib/stacks";
2 | import { z } from "zod";
3 |
4 | export const stackNameSchema = z
5 | .string()
6 | .trim()
7 | .min(1, "Stack name is required")
8 | .max(255, "Stack name must be less than 255 characters")
9 | .refine((name) => validateStackName(name), "Stack name is already in use");
10 |
--------------------------------------------------------------------------------
/src/app/stacks/create/existing-infrastructure/page.tsx:
--------------------------------------------------------------------------------
1 | import { LeftSideMenu } from "@/components/wizard/LeftSideMenu";
2 | import { WizardProvider } from "@/context/WizardContext";
3 | import { ExistingInfraProvider } from "./ExistingInfraContext";
4 | import { ExistingInfraWizard } from "./Wizard";
5 |
6 | const menuEntries = [
7 | "Infrastructure Type",
8 | "Cloud Provider",
9 | "Connect your Cloud",
10 | "Artifact Store",
11 | "Orchestrator",
12 | "Container Registry"
13 | ];
14 |
15 | export default function ExistingInfraPage() {
16 | return (
17 |
18 |
19 |
25 |
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/src/app/stacks/create/existing-infrastructure/steps/artifact_store/Header.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentIcon } from "@/components/ComponentIcon";
2 |
3 | export function ArtifactStoreHeader() {
4 | return (
5 |
6 |
7 |
8 | Select your Artifact Store
9 |
10 |
11 | Choose one of the storages for the new Artifact Store.
12 |
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/src/app/stacks/create/existing-infrastructure/steps/artifact_store/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const artifactStoreSchema = z.object({
4 | resourceId: z.string().min(1),
5 | flavor: z.string()
6 | });
7 |
8 | export type ArtifactStoreForm = z.infer;
9 |
--------------------------------------------------------------------------------
/src/app/stacks/create/existing-infrastructure/steps/connect/AuthenticationMethod.tsx:
--------------------------------------------------------------------------------
1 | import Transform from "@/assets/icons/transform.svg?react";
2 | import { AuthMethodSelect } from "./AuthMethodSelect";
3 |
4 | export function AuthenticationMethod() {
5 | return (
6 |
7 |
8 |
9 |
10 | Select an Authentication Method
11 |
12 |
13 | Connect ZenML to your resources for seamless integration of cloud services into your ML
14 | pipelines.
15 |
16 |
17 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/stacks/create/existing-infrastructure/steps/connect/NewConnectorForm.tsx:
--------------------------------------------------------------------------------
1 | import { useFormContext } from "react-hook-form";
2 | import { StackName } from "../../../components/StackName";
3 | import { AuthenticationMethod } from "./AuthenticationMethod";
4 | import { ConnectorConfig } from "./Configuration";
5 | import { LoadingModal } from "./LoadingModal";
6 | import { useNewConnector } from "./useNewConnector";
7 |
8 | export function NewConnector() {
9 | const {
10 | handleSubmit,
11 | formState: { isSubmitting }
12 | } = useFormContext();
13 | const { handleFormSubmit, fullStackResources, loadingComponents } = useNewConnector();
14 |
15 | return (
16 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/stacks/create/existing-infrastructure/steps/connect/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 | import { stackNameSchema } from "../../../components/sharedSchema";
3 |
4 | export const newConnectorBaseSchema = z.object({
5 | authMethod: z.string().min(1),
6 | stackName: stackNameSchema
7 | });
8 |
9 | export type NewConnectorBaseForm = z.infer;
10 |
--------------------------------------------------------------------------------
/src/app/stacks/create/existing-infrastructure/steps/container_registry/Header.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentIcon } from "@/components/ComponentIcon";
2 |
3 | export function ContainerRegistryHeader() {
4 | return (
5 |
6 |
7 |
8 |
9 | Select your Container Registry
10 |
11 |
12 | Select one of the Container Registries for your new stack
13 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/stacks/create/existing-infrastructure/steps/container_registry/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const containerRegistrySchema = z.object({
4 | flavor: z.string().min(1),
5 | resourceId: z.string().min(1)
6 | });
7 |
8 | export type ContainerRegistryFormType = z.infer;
9 |
--------------------------------------------------------------------------------
/src/app/stacks/create/existing-infrastructure/steps/orchestrator/Header.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentIcon } from "@/components/ComponentIcon";
2 |
3 | export function OrchestratorHeader() {
4 | return (
5 |
6 |
7 |
8 |
9 | Select your Orchestrator
10 |
11 |
12 | Select one of the connected orchestrators for your new stack
13 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/stacks/create/existing-infrastructure/steps/orchestrator/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const orchestratorFormBaseSchema = z.object({
4 | flavor: z.string().min(1),
5 | resourceId: z.string().min(1)
6 | });
7 |
8 | export type OrchestratorForm = z.infer;
9 |
--------------------------------------------------------------------------------
/src/app/stacks/create/existing-infrastructure/steps/provider/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const providerSchema = z.object({
4 | provider: z.string().min(1)
5 | });
6 |
7 | export type ProviderForm = z.infer;
8 |
--------------------------------------------------------------------------------
/src/app/stacks/create/layout.tsx:
--------------------------------------------------------------------------------
1 | import Stack from "@/assets/icons/stack.svg?react";
2 | import { PageHeader } from "@/components/PageHeader";
3 | import { Outlet } from "react-router-dom";
4 | import { useStackCreateBreadcrumbs } from "./breadcrumb";
5 |
6 | export function CreateStacksLayout() {
7 | useStackCreateBreadcrumbs();
8 | return (
9 |
10 |
11 |
12 |
13 |
Register a Stack
14 |
15 |
16 |
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/stacks/create/manual/create-component-modal/flavor-select.tsx:
--------------------------------------------------------------------------------
1 | import { SelectFlavorList } from "@/components/stack-components/create-component/flavor-select";
2 | import { snakeCaseToTitleCase } from "@/lib/strings";
3 | import { StackComponentType } from "@/types/components";
4 | import { Flavor } from "@/types/flavors";
5 | import {
6 | DialogHeader,
7 | DialogTitle,
8 | ScrollArea
9 | } from "@zenml-io/react-component-library/components/client";
10 |
11 | type Props = {
12 | type: StackComponentType;
13 | setSelectedFlavor: (flavor: Flavor) => void;
14 | };
15 | export function SelectFlavorStep({ type, setSelectedFlavor }: Props) {
16 | return (
17 | <>
18 |
19 | Select {snakeCaseToTitleCase(type)} Flavor
20 |
21 |
22 |
23 |
24 |
25 |
26 | >
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/src/app/stacks/create/manual/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 | import { stackNameSchema } from "../components/sharedSchema";
3 | import { stackComponentTypes } from "@/lib/constants";
4 |
5 | const componentSchema = z.object({
6 | id: z.string().trim().min(1),
7 | name: z.string().trim().min(1),
8 | logoUrl: z.string().trim().min(1)
9 | });
10 |
11 | const typeSchema = stackComponentTypes.reduce(
12 | (schema, type) => {
13 | schema[type] = componentSchema.nullable();
14 | return schema;
15 | },
16 | {} as Record<
17 | (typeof stackComponentTypes)[number],
18 | z.ZodNullable>
19 | >
20 | );
21 |
22 | export const formSchema = z.object({
23 | stackName: stackNameSchema,
24 | components: z.object({
25 | ...typeSchema,
26 | orchestrator: componentSchema,
27 | artifact_store: componentSchema
28 | })
29 | });
30 |
31 | export type FormType = z.infer;
32 |
--------------------------------------------------------------------------------
/src/app/stacks/create/new-infrastructure/Steps/Deploy/index.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useNewInfraFormContext } from "../../NewInfraFormContext";
3 | import { WizardStepWrapper } from "../../Wizard";
4 | import { DeployButtonPart } from "./ButtonStep";
5 | import { ProvisioningStep } from "./ProvisioningStep";
6 |
7 | export function DeployStep() {
8 | return (
9 |
10 |
11 |
12 | );
13 | }
14 |
15 | function DisplaySteps() {
16 | const { setIsNextButtonDisabled, isLoading } = useNewInfraFormContext();
17 | useEffect(() => {
18 | setIsNextButtonDisabled(true);
19 | }, []);
20 | if (isLoading) return ;
21 | return ;
22 | }
23 |
--------------------------------------------------------------------------------
/src/app/stacks/create/new-infrastructure/Steps/Success/SuccessStep.tsx:
--------------------------------------------------------------------------------
1 | import { useNewInfraFormContext } from "../../NewInfraFormContext";
2 | import { WizardStepWrapper } from "../../Wizard";
3 | import { SuccessList } from "./SuccessList";
4 |
5 | export function SuccessStep() {
6 | const { setIsNextButtonDisabled, data, timestamp } = useNewInfraFormContext();
7 | setIsNextButtonDisabled(false);
8 | return (
9 |
10 |
11 |
12 | Here you can review the created stack and stack components. Now you can start running
13 | pipelines using this new configuration.
14 |
15 |
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/stacks/create/new-infrastructure/Steps/schemas.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 | import { stackNameSchema } from "../../components/sharedSchema";
3 |
4 | export const providerSchema = z.enum(["aws", "gcp", "azure"]);
5 |
6 | export const providerFormSchema = z.object({
7 | provider: providerSchema
8 | });
9 |
10 | export type ProviderForm = z.infer;
11 |
12 | export const configurationSchema = z.object({
13 | region: z.string().trim().min(1),
14 | stackName: stackNameSchema
15 | });
16 |
17 | export type ConfigurationForm = z.infer;
18 |
--------------------------------------------------------------------------------
/src/app/stacks/create/new-infrastructure/page.tsx:
--------------------------------------------------------------------------------
1 | import { LeftSideMenu } from "@/components/wizard/LeftSideMenu";
2 | import { WizardProvider } from "@/context/WizardContext";
3 | import { NewInfraFormProvider } from "./NewInfraFormContext";
4 | import { CreateNewInfraWizard } from "./Wizard";
5 | import { parseWizardData } from "./persist";
6 |
7 | const entries = ["Infrastructure Type", "Cloud Provider", "Review Configuration", "Deploy Stack"];
8 |
9 | export default function StackWithNewInfrastructurePage() {
10 | const { success } = parseWizardData();
11 | return (
12 |
13 |
14 |
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/stacks/create/new-infrastructure/persist.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 | import { providerSchema } from "./Steps/schemas";
3 |
4 | const NEWINFRAKEY = "new-infra-data";
5 |
6 | const wizardSchema = z.object({
7 | provider: providerSchema,
8 | stackName: z.string().min(1).trim(),
9 | location: z.string().min(1).trim(),
10 | timestamp: z.string().min(1).trim()
11 | });
12 |
13 | export type WizardData = z.infer;
14 |
15 | export function setWizardData(data: WizardData) {
16 | localStorage.setItem(NEWINFRAKEY, JSON.stringify(data));
17 | }
18 |
19 | export function parseWizardData() {
20 | try {
21 | const localData = localStorage.getItem(NEWINFRAKEY);
22 | const parsedData = localData ? JSON.parse(localData) : {};
23 | return wizardSchema.safeParse(parsedData);
24 | } catch (e) {
25 | clearWizardData();
26 | return wizardSchema.safeParse({});
27 | }
28 | }
29 |
30 | export function clearWizardData() {
31 | localStorage.removeItem(NEWINFRAKEY);
32 | }
33 |
--------------------------------------------------------------------------------
/src/app/stacks/create/page.tsx:
--------------------------------------------------------------------------------
1 | import { NewInfrastructure } from "./NewInfra";
2 | import { ExistingInfrastructure } from "./ExistingInfra";
3 |
4 | export default function NewStacksPage() {
5 | return (
6 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/stacks/create/terraform/TerraformWizard.tsx:
--------------------------------------------------------------------------------
1 | import { useWizardContext } from "@/context/WizardContext";
2 | import { ConfigurationStep } from "./steps/configuration";
3 | import { DeploymentStep } from "./steps/deploy";
4 | import { ProviderStep } from "./steps/provider";
5 | // import { SuccessStep } from "./steps/success";
6 |
7 | export function TerraformWizard() {
8 | const { currentStep } = useWizardContext();
9 |
10 | if (currentStep === 1) return ;
11 | if (currentStep === 2) return ;
12 | if (currentStep === 3) return ;
13 | // if (currentStep === 4) return ;
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/stacks/create/terraform/page.tsx:
--------------------------------------------------------------------------------
1 | import { WizardProvider } from "@/context/WizardContext";
2 | import { CreateTerraformProvider } from "./TerraformContext";
3 | import { TerraformWizard } from "./TerraformWizard";
4 | import { LeftSideMenu } from "@/components/wizard/LeftSideMenu";
5 |
6 | const entries = ["Infrastructure Type", "Cloud Provider", "Review Configuration", "Deploy Stack"];
7 |
8 | export default function TerraformPage() {
9 | return (
10 |
11 |
12 |
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/src/app/stacks/create/terraform/persist.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 | import { providerSchema } from "../new-infrastructure/Steps/schemas";
3 |
4 | export const storageKey = "create-terraform-data";
5 |
6 | const wizardSchema = z.object({
7 | provider: providerSchema,
8 | stackName: z.string().min(1).trim(),
9 | location: z.string().min(1).trim(),
10 | timestamp: z.string().min(1).trim()
11 | });
12 |
13 | export type WizardData = z.infer;
14 |
15 | export function setWizardData(data: WizardData) {
16 | localStorage.setItem(storageKey, JSON.stringify(data));
17 | }
18 |
19 | export function parseWizardData() {
20 | try {
21 | const localData = localStorage.getItem(storageKey);
22 | const parsedData = localData ? JSON.parse(localData) : {};
23 | return wizardSchema.safeParse(parsedData);
24 | } catch (e) {
25 | clearWizardData();
26 | return wizardSchema.safeParse({});
27 | }
28 | }
29 |
30 | export function clearWizardData() {
31 | localStorage.removeItem(storageKey);
32 | }
33 |
--------------------------------------------------------------------------------
/src/app/stacks/create/terraform/steps/provider/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 | import { providerSchema } from "../../../new-infrastructure/Steps/schemas";
3 |
4 | export const providerFormSchema = z.object({
5 | provider: providerSchema
6 | });
7 |
8 | export type ProviderForm = z.infer;
9 |
--------------------------------------------------------------------------------
/src/app/stacks/page.tsx:
--------------------------------------------------------------------------------
1 | import { useTourContext } from "@/components/tour/TourContext";
2 | import { useEffect } from "react";
3 | import { StackList } from "./StackList";
4 | import { ResumeBanners } from "./ResumeBanner";
5 |
6 | export default function StacksPage() {
7 | const {
8 | setTourState,
9 | tourState: { tourActive }
10 | } = useTourContext();
11 |
12 | useEffect(() => {
13 | if (tourActive) {
14 | setTourState((prev) => ({ ...prev, run: true, stepIndex: prev.stepIndex }));
15 | }
16 | }, [tourActive]);
17 |
18 | return (
19 |
20 |
21 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/src/app/stacks/service.ts:
--------------------------------------------------------------------------------
1 | import { StackListQueryParams } from "@/types/stack";
2 | import { useSearchParams } from "react-router-dom";
3 | import { z } from "zod";
4 |
5 | const DEFAULT_PAGE = 1;
6 |
7 | const filterParamsSchema = z.object({
8 | page: z.coerce.number().min(DEFAULT_PAGE).optional().default(DEFAULT_PAGE).catch(DEFAULT_PAGE),
9 | name: z.string().optional(),
10 |
11 | operator: z.enum(["and", "or"]).optional()
12 | });
13 |
14 | export function useStacklistQueryParams(): StackListQueryParams {
15 | const [searchParams] = useSearchParams();
16 |
17 | const { page, name, operator } = filterParamsSchema.parse({
18 | page: searchParams.get("page") || undefined,
19 | name: searchParams.get("name") || undefined
20 | });
21 |
22 | return { page, name, logical_operator: operator };
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/survey/PrimaryUseStep.tsx:
--------------------------------------------------------------------------------
1 | import { PrimaryUseForm } from "@/components/survey/PrimaryUse";
2 | import { useSurveyContext } from "@/components/survey/SurveyContext";
3 | import { PrimaryUseFormType } from "@/components/survey/form-schemas";
4 | import { User, UserMetadata } from "@/types/user";
5 | import { useSurveyUserContext } from "./SurveyUserContext";
6 |
7 | type Props = {
8 | user: User;
9 | };
10 |
11 | export function PrimaryUseStep({ user }: Props) {
12 | const { setSurveyStep } = useSurveyContext();
13 | const { setUser } = useSurveyUserContext();
14 |
15 | function handlePrimaryUseSubmit({ primaryUse }: PrimaryUseFormType) {
16 | const metadata: UserMetadata = {
17 | primary_use: primaryUse
18 | };
19 | setUser((prev) => ({
20 | ...prev,
21 | user_metadata: { ...prev.user_metadata, ...metadata }
22 | }));
23 | setSurveyStep(3);
24 | }
25 |
26 | return ;
27 | }
28 |
--------------------------------------------------------------------------------
/src/app/survey/SurveyUserContext.tsx:
--------------------------------------------------------------------------------
1 | import { UpdateUser } from "@/types/user";
2 | import { Dispatch, SetStateAction, createContext, useContext, useState } from "react";
3 |
4 | type SurveyUserContextType = {
5 | user: UpdateUser;
6 | setUser: Dispatch>;
7 | };
8 |
9 | export const SurveyUserContext = createContext(null);
10 |
11 | export function SurveyUserProvider({
12 | children,
13 | initialUser
14 | }: {
15 | children: React.ReactNode;
16 | initialUser?: UpdateUser;
17 | }) {
18 | const [user, setUser] = useState(initialUser || {});
19 | return (
20 | {children}
21 | );
22 | }
23 |
24 | export function useSurveyUserContext() {
25 | const context = useContext(SurveyUserContext);
26 | if (context === null) {
27 | throw new Error("useSurveyUserContext must be used within an SurveyUserProvider");
28 | }
29 | return context;
30 | }
31 |
--------------------------------------------------------------------------------
/src/app/survey/loader.ts:
--------------------------------------------------------------------------------
1 | import { QueryClient } from "@tanstack/react-query";
2 | import { fetchCurrentUser, getCurrentUserKey } from "@/data/users/current-user-query";
3 |
4 | export const surveyLoader = (queryClient: QueryClient) => async () => {
5 | await queryClient.ensureQueryData({ queryKey: getCurrentUserKey(), queryFn: fetchCurrentUser });
6 | return null;
7 | };
8 |
--------------------------------------------------------------------------------
/src/app/survey/page.tsx:
--------------------------------------------------------------------------------
1 | import { SurveyProvider } from "@/components/survey/SurveyContext";
2 | import { SurveyWizard } from "./Wizard";
3 |
4 | export default function SurveyPage() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/upgrade/components/Context.tsx:
--------------------------------------------------------------------------------
1 | import { createContext, Dispatch, SetStateAction, useContext, useState } from "react";
2 |
3 | type UpgradeContextType = {
4 | submitSuccess: boolean;
5 | setSubmitSuccess: Dispatch>;
6 | };
7 |
8 | export const UpgradeContext = createContext(null);
9 |
10 | export function UpgradeProvider({ children }: { children: React.ReactNode }) {
11 | const [success, setSuccess] = useState(false);
12 | return (
13 |
19 | {children}
20 |
21 | );
22 | }
23 |
24 | export function useUpgradeContext() {
25 | const context = useContext(UpgradeContext);
26 | if (context === null) {
27 | throw new Error("useUpgradeContext must be used within an UpgradeProvider");
28 | }
29 | return context;
30 | }
31 |
--------------------------------------------------------------------------------
/src/app/upgrade/components/form/Wrapper.tsx:
--------------------------------------------------------------------------------
1 | import { ProWrapper } from "@/components/pro/ProCta";
2 | import { useUpgradeContext } from "../Context";
3 | import { UpgradeFormStep } from "./index";
4 | import Image from "@/assets/images/upgrade-form.webp";
5 | import { SubmitSuccess } from "../Success";
6 |
7 | export function UpgradeWrapperBox() {
8 | const { submitSuccess } = useUpgradeContext();
9 | return (
10 |
11 |
15 | {submitSuccess === false && }
16 | {submitSuccess === true && }
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/src/app/upgrade/components/form/index.tsx:
--------------------------------------------------------------------------------
1 | import { UpgradeFormSection } from "./Form";
2 | import { UpgradeSteps } from "./Steps";
3 |
4 | export function UpgradeFormStep() {
5 | return (
6 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/upgrade/components/form/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const upgradeFormSchema = z.object({
4 | name: z.string().min(1),
5 | company: z.string().min(1),
6 | email: z.string().email()
7 | });
8 |
9 | export type UpgradeForm = z.infer;
10 |
--------------------------------------------------------------------------------
/src/app/upgrade/page.tsx:
--------------------------------------------------------------------------------
1 | import { UpgradeProvider } from "./components/Context";
2 | import { UpgradeWrapperBox } from "./components/form/Wrapper";
3 |
4 | export default function UpgradePage() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/assets/icons/alert-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/alert-triangle.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
8 |
9 |
--------------------------------------------------------------------------------
/src/assets/icons/arrow-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/arrow-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/cached.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/check-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/check.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/chevron-down.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/chevron-left-double.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/chevron-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/chevron-right-double.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/chevron-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/clock.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/cloud.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/collapse.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/dots-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/dots-horizontal.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/expand-full.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/expand.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/gitlab.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/assets/icons/info.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/minus.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/plus.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/refresh.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/search.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/services/terraform.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/side-collapse.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/side-expand.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/slash-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/slash-divider.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/spinner.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/switch-horizontal.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/tick-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/illustrations/connectivity.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/90236a062535d2182b67f20f23cd8c8280f01921/src/assets/illustrations/connectivity.webp
--------------------------------------------------------------------------------
/src/assets/images/acp.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/90236a062535d2182b67f20f23cd8c8280f01921/src/assets/images/acp.webp
--------------------------------------------------------------------------------
/src/assets/images/mcp.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/90236a062535d2182b67f20f23cd8c8280f01921/src/assets/images/mcp.webp
--------------------------------------------------------------------------------
/src/assets/images/portraits/adam.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/90236a062535d2182b67f20f23cd8c8280f01921/src/assets/images/portraits/adam.webp
--------------------------------------------------------------------------------
/src/assets/images/portraits/alex.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/90236a062535d2182b67f20f23cd8c8280f01921/src/assets/images/portraits/alex.webp
--------------------------------------------------------------------------------
/src/assets/images/portraits/baris.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/90236a062535d2182b67f20f23cd8c8280f01921/src/assets/images/portraits/baris.webp
--------------------------------------------------------------------------------
/src/assets/images/portraits/hamza.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/90236a062535d2182b67f20f23cd8c8280f01921/src/assets/images/portraits/hamza.webp
--------------------------------------------------------------------------------
/src/assets/images/portraits/stefan.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/90236a062535d2182b67f20f23cd8c8280f01921/src/assets/images/portraits/stefan.webp
--------------------------------------------------------------------------------
/src/assets/images/product-tour/settings_preview.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/90236a062535d2182b67f20f23cd8c8280f01921/src/assets/images/product-tour/settings_preview.webp
--------------------------------------------------------------------------------
/src/assets/images/product-tour/tour-cover.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/90236a062535d2182b67f20f23cd8c8280f01921/src/assets/images/product-tour/tour-cover.webp
--------------------------------------------------------------------------------
/src/assets/images/templates.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/90236a062535d2182b67f20f23cd8c8280f01921/src/assets/images/templates.webp
--------------------------------------------------------------------------------
/src/assets/images/upgrade-form.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/90236a062535d2182b67f20f23cd8c8280f01921/src/assets/images/upgrade-form.webp
--------------------------------------------------------------------------------
/src/assets/styles/index.css:
--------------------------------------------------------------------------------
1 | @import "@fontsource/inter/400";
2 | @import "@fontsource/inter/500";
3 | @import "@fontsource/inter/600";
4 |
5 | @tailwind base;
6 | @tailwind components;
7 | @tailwind utilities;
8 |
9 | @layer utilities {
10 | .layout-container {
11 | @apply mx-auto w-11/12 max-w-[1440px];
12 | }
13 | .link {
14 | @apply underline transition-all duration-150 hover:decoration-transparent;
15 | }
16 | }
17 |
18 | @layer components {
19 | [type="radio"]:checked {
20 | background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 16 16' fill='%237A3EF4' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='8' cy='8' r='5'/%3E%3C/svg%3E");
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/CodeHighlighter.tsx:
--------------------------------------------------------------------------------
1 | import Prism from "prismjs";
2 | import "prismjs/components/prism-python";
3 | import "@/assets/styles/prism-github-light.css";
4 | import { useEffect } from "react";
5 |
6 | type Props = {
7 | code: string;
8 | };
9 |
10 | export function CodeHighlighter({ code }: Props) {
11 | useEffect(() => {
12 | Prism.highlightAll();
13 | }, []);
14 | return {code}
;
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/DisplayDate.tsx:
--------------------------------------------------------------------------------
1 | export function DisplayDate({
2 | dateString,
3 | short = false
4 | }: {
5 | dateString: string;
6 | short?: boolean;
7 | }) {
8 | const date = new Date(`${dateString}Z`);
9 |
10 | return <>{short ? formatShortDate(date) : date.toLocaleString()}>;
11 | }
12 |
13 | function formatShortDate(date: Date) {
14 | const dateOptions: Intl.DateTimeFormatOptions = {
15 | month: "short",
16 | day: "numeric",
17 | year: "numeric"
18 | };
19 | const timeOptions: Intl.DateTimeFormatOptions = {
20 | hour: "numeric",
21 | minute: "numeric",
22 | hour12: false
23 | };
24 |
25 | const formattedDate = date.toLocaleDateString("en-US", dateOptions);
26 | const formattedTime = date.toLocaleTimeString("en-US", timeOptions);
27 | return `${formattedDate} ${formattedTime}`;
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/EmptyState.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren, ReactNode } from "react";
2 |
3 | type Props = {
4 | icon?: ReactNode;
5 | };
6 |
7 | export function EmptyState({ children, icon }: PropsWithChildren) {
8 | return (
9 |
10 | {icon}
11 | {children}
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/Error.tsx:
--------------------------------------------------------------------------------
1 | import File from "@/assets/icons/file.svg?react";
2 | import AlertCircle from "@/assets/icons/alert-circle.svg?react";
3 | import { EmptyState } from "@/components/EmptyState";
4 |
5 | type Props = {
6 | isAlertCircle?: boolean;
7 | err: Error;
8 | };
9 |
10 | export function ErrorFallback({ err, isAlertCircle = false }: Props) {
11 | return (
12 |
16 | ) : (
17 |
18 | )
19 | }
20 | >
21 |
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/InlineAvatar.tsx:
--------------------------------------------------------------------------------
1 | import { Avatar, AvatarFallback, cn } from "@zenml-io/react-component-library";
2 |
3 | type Props = {
4 | username: string;
5 | className?: string;
6 | };
7 |
8 | export function InlineAvatar({ username, className }: Props) {
9 | return (
10 |
11 |
12 | {username[0]}
13 |
14 |
{username}
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/NavLink.tsx:
--------------------------------------------------------------------------------
1 | import { NavLink as NavLinkPrimitive, NavLinkProps, useLocation } from "react-router-dom";
2 |
3 | interface CustomNavLinkProps extends NavLinkProps {
4 | isActiveOverride?: (pathname: string) => boolean;
5 | }
6 |
7 | export default function NavLink({ children, isActiveOverride, ...rest }: CustomNavLinkProps) {
8 | const location = useLocation();
9 | const isActive = isActiveOverride ? isActiveOverride(location.pathname) : false;
10 |
11 | return (
12 |
15 | ` ${
16 | isActive || defaultIsActive
17 | ? "bg-primary-50 text-theme-text-brand"
18 | : "hover:bg-neutral-200"
19 | } block rounded-md px-4 py-1 text-text-sm font-semibold `
20 | }
21 | >
22 | {children}
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/NumberBox.tsx:
--------------------------------------------------------------------------------
1 | import { cva, VariantProps } from "class-variance-authority";
2 | import { PropsWithChildren } from "react";
3 |
4 | const numberBoxVariants = cva(
5 | "flex h-7 w-7 items-center justify-center rounded-sm text-text-lg font-semibold",
6 | {
7 | variants: {
8 | intent: {
9 | default: "bg-primary-100 text-theme-text-brand",
10 | disabled: "bg-neutral-100 text-theme-text-tertiary"
11 | }
12 | },
13 | defaultVariants: {
14 | intent: "default"
15 | }
16 | }
17 | );
18 |
19 | type Props = VariantProps & PropsWithChildren;
20 |
21 | export function Numberbox({ children, intent }: Props) {
22 | return {children}
;
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/PageHeader.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@zenml-io/react-component-library";
2 | import { HTMLAttributes, PropsWithChildren } from "react";
3 |
4 | type Props = HTMLAttributes;
5 |
6 | export function PageHeader({ children, className, ...rest }: PropsWithChildren) {
7 | return (
8 |
15 | {children}
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/ProviderIcon.tsx:
--------------------------------------------------------------------------------
1 | import Aws from "@/assets/icons/services/aws.svg?react";
2 | import Gcp from "@/assets/icons/services/gcp.svg?react";
3 | import Azure from "@/assets/icons/services/azure.svg?react";
4 | import { HTMLAttributes } from "react";
5 | import { cn } from "@zenml-io/react-component-library";
6 | type Props = HTMLAttributes & {
7 | provider: "aws" | "gcp" | "azure";
8 | };
9 | export function CloudProviderIcon({ provider, className, ...rest }: Props) {
10 | const classname = cn("w-5 h-5 shrink-0", className);
11 | switch (provider) {
12 | case "aws":
13 | return ;
14 | case "azure":
15 | return ;
16 | case "gcp":
17 | return ;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/Tick.tsx:
--------------------------------------------------------------------------------
1 | import { ProgressTick, cn } from "@zenml-io/react-component-library";
2 | import Check from "@/assets/icons/check.svg?react";
3 | import { ComponentPropsWithoutRef } from "react";
4 |
5 | type Props = ComponentPropsWithoutRef & { tickClasses?: string };
6 | export function Tick({ tickClasses, ...rest }: Props) {
7 | return (
8 |
9 |
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/artifacts/HtmlVisualization.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from "react";
2 | import { Props } from "./Visualization";
3 |
4 | export function HTMLVisualization({ content }: Props) {
5 | const iframeRef = useRef(null);
6 |
7 | const handleIframeLoad = () => {
8 | if (iframeRef.current) {
9 | const contentHeight = iframeRef.current.contentWindow?.document.documentElement.scrollHeight;
10 | iframeRef.current.height = contentHeight ? `${contentHeight}px` : "100%";
11 | }
12 | };
13 |
14 | useEffect(() => {
15 | handleIframeLoad();
16 | window.addEventListener("resize", handleIframeLoad);
17 | return () => {
18 | window.removeEventListener("resize", handleIframeLoad);
19 | };
20 | }, []);
21 |
22 | return (
23 |
24 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/artifacts/ImageVisualization.tsx:
--------------------------------------------------------------------------------
1 | import { sanitizeUrl } from "@/lib/url";
2 | import { Props } from "./Visualization";
3 |
4 | export function ImageVisualization({ content }: Props) {
5 | return (
6 |
7 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/artifacts/JsonVisualization.tsx:
--------------------------------------------------------------------------------
1 | import { isString } from "@/lib/type-guards";
2 | import { Codesnippet } from "../CodeSnippet";
3 | import { Props } from "./Visualization";
4 |
5 | export function JSONVisualization({ content }: Props) {
6 | const json = parseJSON(content);
7 | return (
8 |
13 | );
14 | }
15 |
16 | function parseJSON(content: string): unknown {
17 | try {
18 | const parsedJSON = JSON.parse(content);
19 | return parsedJSON;
20 | } catch (e) {
21 | return content;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/artifacts/MarkdownVisualization.tsx:
--------------------------------------------------------------------------------
1 | import { Props } from "./Visualization";
2 | import Markdown from "react-markdown";
3 |
4 | export default function MarkdownVisualization({ content }: Props) {
5 | return {content} ;
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/artifacts/artifact-node-sheet/index.tsx:
--------------------------------------------------------------------------------
1 | import { Sheet, SheetContent, SheetTrigger } from "@zenml-io/react-component-library";
2 | import { PropsWithChildren } from "react";
3 | import { ArtifactSheetContent } from "./SheetContent";
4 |
5 | type Props = {
6 | artifactVersionId: string;
7 | onOpenChange?: (isOpen: boolean) => void;
8 | };
9 |
10 | export function ArtifactSheet({
11 | children,
12 | artifactVersionId,
13 | onOpenChange
14 | }: PropsWithChildren) {
15 | return (
16 |
17 | {children}
18 |
19 |
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/breadcrumbs/types.ts:
--------------------------------------------------------------------------------
1 | export type BreadcrumbSegment = {
2 | label: string;
3 | href: string;
4 | disabled?: boolean;
5 | };
6 |
7 | export type Breadcrumbs = BreadcrumbSegment[];
8 |
--------------------------------------------------------------------------------
/src/components/buttons/update-button-content.tsx:
--------------------------------------------------------------------------------
1 | import Edit from "@/assets/icons/edit.svg?react";
2 |
3 | export function UpdateButtonContent() {
4 | return (
5 | <>
6 |
7 | Update
8 | >
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/dag-visualizer/BaseNode.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren } from "react";
2 | import { Handle, Position } from "reactflow";
3 |
4 | export function BaseNode({ children }: PropsWithChildren) {
5 | return (
6 | <>
7 |
12 | {children}
13 |
19 | >
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/dag-visualizer/layout/status.ts:
--------------------------------------------------------------------------------
1 | import { ExecutionStatus } from "@/types/pipeline-runs";
2 |
3 | export function getIsStatusUnknown(stepStatus: ExecutionStatus, runStatus: ExecutionStatus) {
4 | return ["initializing", "running"].includes(stepStatus) && runStatus === "failed";
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/fallback-pages/Commands.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from "react";
2 | import { Codesnippet } from "../CodeSnippet";
3 |
4 | export type CommandListItem = {
5 | command: string;
6 | description: ReactNode;
7 | };
8 |
9 | export function generateCommandList(item: CommandListItem) {
10 | return (
11 |
12 |
{item.description}
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/fallback-pages/Helpbox.tsx:
--------------------------------------------------------------------------------
1 | import { Box, buttonVariants } from "@zenml-io/react-component-library";
2 | import Help from "@/assets/icons/help.svg?react";
3 | import { ReactNode } from "react";
4 |
5 | type HelpBoxProps = {
6 | text?: ReactNode;
7 | link: string;
8 | };
9 | export function HelpBox({ link, text = "Do you need help?" }: HelpBoxProps) {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
{text}
17 |
18 |
24 | Browse our docs
25 |
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/fallback/icon.tsx:
--------------------------------------------------------------------------------
1 | import CodeBox from "@/assets/icons/code-box.svg?react";
2 | import { ReactNode } from "react";
3 |
4 | export function FallbackIcon({ icon }: { icon: ReactNode }) {
5 | return (
6 |
7 |
8 |
9 | {icon}
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/form/common.tsx:
--------------------------------------------------------------------------------
1 | type Props = {
2 | label: string;
3 | isOptional: boolean;
4 | };
5 |
6 | export function RendererHeadline({ label, isOptional }: Props) {
7 | return (
8 | <>
9 | {label}
10 | {!isOptional && (
11 |
12 | *
13 |
14 | )}
15 | >
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/not-available.tsx:
--------------------------------------------------------------------------------
1 | export function NotAvailable() {
2 | return Not available
;
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/onboarding/SkippedStep.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@zenml-io/react-component-library";
2 | import { HTMLAttributes } from "react";
3 | import Forward from "@/assets/icons/chevron-right-double.svg?react";
4 |
5 | export function SkippedStep({ className, ...rest }: HTMLAttributes) {
6 | return (
7 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/password/UpdatePasswordSchemas.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const passwordSchema = z
4 | .string()
5 | .min(8, "Password must be at least 8 characters")
6 | .regex(/[A-Z]/, "Password must contain at least one uppercase letter")
7 | .regex(/[a-z]/, "Password must contain at least one lowercase letter")
8 | .regex(/[0-9]/, "Password must contain at least one number")
9 | .regex(/[!@#$%^&*(),.?":{}|<>\/]/, "Password must contain at least one special character");
10 |
11 | export const updatePasswordBaseFormSchema = z.object({
12 | oldPassword: z.string().optional(),
13 | newPassword: passwordSchema,
14 | confirmPassword: z.string().min(1)
15 | });
16 |
17 | export const updatePasswordFormSchema = updatePasswordBaseFormSchema.refine(
18 | (data) => data.newPassword === data.confirmPassword,
19 | {
20 | path: ["confirmPassword"],
21 | message: "Passwords do not match"
22 | }
23 | );
24 |
25 | export type UpdatePasswordFormType = z.infer;
26 |
--------------------------------------------------------------------------------
/src/components/runs/auth-required.tsx:
--------------------------------------------------------------------------------
1 | import Triange from "@/assets/icons/alert-triangle.svg?react";
2 | import {
3 | Tag,
4 | Tooltip,
5 | TooltipContent,
6 | TooltipProvider,
7 | TooltipTrigger
8 | } from "@zenml-io/react-component-library";
9 |
10 | export function AuthRequired() {
11 | return (
12 |
13 |
14 |
15 |
21 |
22 | Login Required
23 |
24 |
25 |
26 |
27 | You must be logged in for this link to work.
28 |
29 | Please ensure you're signed in before accessing.
30 |
31 |
32 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/runs/refresh-group/index.tsx:
--------------------------------------------------------------------------------
1 | import Refresh from "@/assets/icons/refresh.svg?react";
2 | import { Button } from "@zenml-io/react-component-library/components/server";
3 | import { RunRefreshDropdown } from "./dropdown";
4 | import { usePipelineRun } from "@/data/pipeline-runs/pipeline-run-detail-query";
5 |
6 | type Props = {
7 | runId: string;
8 | };
9 |
10 | export function RunRefreshGroup({ runId }: Props) {
11 | const { refetch } = usePipelineRun({ runId });
12 |
13 | return (
14 |
15 | refetch()}
21 | >
22 |
23 | Refresh
24 |
25 |
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/service-connectors/connector-tag.tsx:
--------------------------------------------------------------------------------
1 | import { Tag } from "@zenml-io/react-component-library/components/server";
2 | import Transform from "@/assets/icons/transform.svg?react";
3 |
4 | type Props = {
5 | connectorName: string;
6 | };
7 |
8 | export function ConnectorTag({ connectorName }: Props) {
9 | return (
10 |
11 |
12 | {connectorName}
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/service-connectors/connector-type-tooltip.tsx:
--------------------------------------------------------------------------------
1 | import { ConnectorType } from "@/types/service-connectors";
2 | import {
3 | Tooltip,
4 | TooltipContent,
5 | TooltipProvider,
6 | TooltipTrigger
7 | } from "@zenml-io/react-component-library/components/client";
8 |
9 | type Props = {
10 | connectorType: ConnectorType;
11 | };
12 |
13 | export function ConnectorTypeTooltip({ connectorType }: Props) {
14 | const url = connectorType.logo_url;
15 | const name = connectorType.name;
16 | return (
17 |
18 |
19 |
20 |
21 |
28 |
{name}
29 |
30 |
31 | {name}
32 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/service-connectors/create-fallback.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Dialog,
3 | DialogContent,
4 | DialogHeader,
5 | DialogTitle
6 | } from "@zenml-io/react-component-library/components/client";
7 | import { Codesnippet } from "../CodeSnippet";
8 |
9 | type Props = {
10 | open: boolean;
11 | setOpen: (open: boolean) => void;
12 | };
13 |
14 | export function CreateServiceConnectorFallback({ open, setOpen }: Props) {
15 | return (
16 |
17 |
18 |
19 | Create Service Connector
20 |
21 |
22 |
23 |
Create a new service connector
24 |
--type= --interactive`}
27 | />
28 |
29 |
30 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/service-connectors/resource-name-tooltip.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Tooltip,
3 | TooltipContent,
4 | TooltipProvider,
5 | TooltipTrigger
6 | } from "@zenml-io/react-component-library/components/client";
7 | import { Tag } from "@zenml-io/react-component-library/components/server";
8 | import { ServiceConnector } from "@/types/service-connectors";
9 |
10 | export function ResourceNameTooltip({ connector }: { connector: ServiceConnector }) {
11 | const name = connector.body?.resource_id || "Multiple";
12 | return (
13 |
14 |
15 |
16 |
23 | {name}
24 |
25 |
26 | {name}
27 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/service-connectors/resource-type-tooltip.tsx:
--------------------------------------------------------------------------------
1 | import { ResourceType } from "@/types/service-connectors";
2 | import {
3 | Tooltip,
4 | TooltipContent,
5 | TooltipProvider,
6 | TooltipTrigger
7 | } from "@zenml-io/react-component-library/components/client";
8 | type Props = {
9 | resourceType: ResourceType;
10 | };
11 |
12 | export function ResourceTypeTooltip({ resourceType }: Props) {
13 | const url = resourceType.logo_url;
14 | const name = resourceType.name;
15 | return (
16 |
17 |
18 |
19 |
20 |
27 |
{name}
28 |
29 |
30 | {name}
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/sheet/SheetHeader.tsx:
--------------------------------------------------------------------------------
1 | import DoubleChevronRight from "@/assets/icons/chevron-right-double.svg?react";
2 | import { Button, cn, SheetClose } from "@zenml-io/react-component-library";
3 | import { HTMLAttributes } from "react";
4 |
5 | type Props = HTMLAttributes;
6 |
7 | export function SheetHeader({ className, children, ...props }: Props) {
8 | return (
9 |
16 |
17 |
23 |
24 | Close
25 |
26 |
27 | {children}
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/stack-components/ComponentBadge.tsx:
--------------------------------------------------------------------------------
1 | import { StackComponentType } from "@/types/components";
2 | import { Tag } from "@zenml-io/react-component-library";
3 | import { ComponentIcon } from "@/components/ComponentIcon";
4 | import { ReactNode } from "react";
5 |
6 | type Props = {
7 | type: StackComponentType;
8 | children?: ReactNode;
9 | };
10 | export function ComponentBadge({ type, children }: Props) {
11 | return (
12 |
18 |
19 | {children}
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/stack-components/component-detail/hooks.ts:
--------------------------------------------------------------------------------
1 | import { componentQueries } from "@/data/components";
2 | import { useQuery } from "@tanstack/react-query";
3 |
4 | export function useComponent(id: string) {
5 | return useQuery({ ...componentQueries.componentDetail(id), throwOnError: true });
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/stack-components/component-detail/service.ts:
--------------------------------------------------------------------------------
1 | import { useSearchParams } from "react-router-dom";
2 | import { z } from "zod";
3 |
4 | const tabParamSchema = z.object({
5 | tab: z
6 | .enum(["configuration", "stacks", "runs"])
7 | .optional()
8 | .default("configuration")
9 | .catch("configuration")
10 | });
11 |
12 | export function useSelectedTab() {
13 | const [searchParams] = useSearchParams();
14 | const { tab } = tabParamSchema.parse({
15 | tab: searchParams.get("tab") || undefined
16 | });
17 |
18 | return tab;
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/stack-components/create-component/connector-section.tsx:
--------------------------------------------------------------------------------
1 | import ConnectorIcon from "@/assets/icons/transform.svg?react";
2 | import { ConnectorSelect } from "./connector-select";
3 |
4 | type Props = {
5 | connectorResourceType: string;
6 | };
7 |
8 | export function ConnectorSection({ connectorResourceType }: Props) {
9 | return (
10 |
11 |
12 |
13 |
14 |
Connect to resource
15 |
16 |
17 | You can select a Service Connector to connect ZenML to external resources
18 |
19 |
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/stack-components/create-component/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const componentBaseSchema = z.object({
4 | componentName: z.string().min(1, "Component name is required"),
5 | connector: z.string()
6 | });
7 |
--------------------------------------------------------------------------------
/src/components/stacks/Sheet/IntegrationsContext.tsx:
--------------------------------------------------------------------------------
1 | import { createContext, Dispatch, SetStateAction, useContext, useState } from "react";
2 |
3 | type IntegrationsContextType = {
4 | integrations: string[];
5 | setIntegrations: Dispatch>;
6 | };
7 |
8 | export const IntegrationsContext = createContext(null);
9 |
10 | export function IntegrationsContextProvider({ children }: { children: React.ReactNode }) {
11 | const [integrations, setIntegrations] = useState([]);
12 | return (
13 |
19 | {children}
20 |
21 | );
22 | }
23 |
24 | export function useIntegrationsContext() {
25 | const context = useContext(IntegrationsContext);
26 | if (context === null) {
27 | throw new Error("useIntegrationsContext must be used within an AuthProvider");
28 | }
29 | return context;
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/stacks/Sheet/update-stacks-dialog.tsx:
--------------------------------------------------------------------------------
1 | import { UpdateStackDialog as UpdateStackDialogComp } from "@/app/stacks/DialogItems";
2 | import { UpdateButtonContent } from "@/components/buttons/update-button-content";
3 | import { DialogTrigger } from "@zenml-io/react-component-library";
4 | import { Dialog } from "@zenml-io/react-component-library/components/client";
5 | import { Button } from "@zenml-io/react-component-library/components/server";
6 | import { useState } from "react";
7 |
8 | type Props = {
9 | name: string;
10 | };
11 | export function UpdateStackDialog({ name }: Props) {
12 | const [open, setOpen] = useState(false);
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/stacks/new-infra/TerraformCommands.tsx:
--------------------------------------------------------------------------------
1 | import { Codesnippet } from "../../CodeSnippet";
2 | import { Numberbox } from "../../NumberBox";
3 |
4 | export function TerraformCommands() {
5 | return (
6 |
7 |
8 |
9 | 3
10 | Run the following commands
11 |
12 |
13 |
14 |
15 | Initialize the Terraform configuration.
16 |
17 |
18 |
19 |
20 |
21 | Run terraform apply to deploy the ZenML stack to Azure.
22 |
23 |
24 |
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/steps/step-sheet/CodeTab.tsx:
--------------------------------------------------------------------------------
1 | import { Codesnippet } from "@/components/CodeSnippet";
2 | import { CollapsibleCard } from "@/components/CollapsibleCard";
3 | import { useStepDetail } from "@/data/steps/step-detail-query";
4 | import { Skeleton } from "@zenml-io/react-component-library";
5 | import { ErrorFallback } from "../../Error";
6 |
7 | type Props = {
8 | stepId: string;
9 | };
10 |
11 | export function StepCodeTab({ stepId }: Props) {
12 | const { data, isPending, isError, error } = useStepDetail({ stepId });
13 |
14 | if (isError) return ;
15 |
16 | if (isPending) return ;
17 |
18 | return (
19 |
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/steps/step-sheet/index.tsx:
--------------------------------------------------------------------------------
1 | import { Sheet, SheetContent, SheetTrigger } from "@zenml-io/react-component-library";
2 | import { PropsWithChildren } from "react";
3 | import { StepSheetContent } from "./SheetContent";
4 |
5 | type Props = {
6 | stepId: string;
7 | onOpenChange?: (isOpen: boolean) => void;
8 | };
9 |
10 | export function StepSheet({ children, stepId, onOpenChange }: PropsWithChildren) {
11 | return (
12 |
13 | {children}
14 |
15 |
16 |
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/survey/StepDisplay.tsx:
--------------------------------------------------------------------------------
1 | import { useSurveyContext } from "./SurveyContext";
2 |
3 | type StepDisplayProps = {
4 | stepAmount: number;
5 | };
6 |
7 | export default function StepDisplay({ stepAmount }: StepDisplayProps) {
8 | const { surveyStep } = useSurveyContext();
9 | if (surveyStep > stepAmount) return null;
10 | return (
11 |
12 | {Array.from({ length: stepAmount }, (_, i) => (
13 | i
20 | ? "bg-primary-300"
21 | : "bg-neutral-200"
22 | }`}
23 | >
24 | ))}
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/survey/SurveyContext.tsx:
--------------------------------------------------------------------------------
1 | import { Dispatch, SetStateAction, createContext, useContext, useState } from "react";
2 |
3 | type SuveyContextType = {
4 | surveyStep: number;
5 | setSurveyStep: Dispatch>;
6 | };
7 |
8 | export const SurveyContext = createContext(null);
9 |
10 | export function SurveyProvider({
11 | children,
12 | initialStep = 1
13 | }: {
14 | children: React.ReactNode;
15 | initialStep?: number;
16 | }) {
17 | const [surveyStep, setSurveyStep] = useState(initialStep);
18 |
19 | return (
20 |
21 | {children}
22 |
23 | );
24 | }
25 |
26 | export function useSurveyContext() {
27 | const context = useContext(SurveyContext);
28 | if (context === null) {
29 | throw new Error("useSurveyContext must be used within an SurveyProvider");
30 | }
31 | return context;
32 | }
33 |
--------------------------------------------------------------------------------
/src/contents/repositories.ts:
--------------------------------------------------------------------------------
1 | import { CommandListItem } from "../components/fallback-pages/Commands";
2 |
3 | export const repositoryCommands: CommandListItem[] = [
4 | {
5 | command:
6 | "zenml code-repository register --type= --type=custom --source= [--CODE_REPOSITORY_OPTIONS]",
12 | description: "Use a custom repository "
13 | },
14 | {
15 | command: "zenml code-repository list",
16 | description: "List your registered code repositories"
17 | },
18 | {
19 | command: "zenml code-repository delete ",
20 | description: "Delete a code repository that you have previously registered"
21 | }
22 | ];
23 |
--------------------------------------------------------------------------------
/src/contents/secrets.ts:
--------------------------------------------------------------------------------
1 | import { CommandListItem } from "../components/fallback-pages/Commands";
2 |
3 | export const secretCommands: CommandListItem[] = [
4 | {
5 | command: `zenml secret create SECRET_NAME --key1=value1 --key2=value2 --key3=value3 ...
6 | \n
7 | # Another option is to use the '--values' option and provide key-value pairs in either JSON or YAML format.
8 | zenml secret create SECRET_NAME --values '{"key1":"value2","key2":"value2","key3":"value3"}'`,
9 | description: "Create a secret"
10 | },
11 | {
12 | command: "zenml secret list",
13 | description: "List all the available secrets"
14 | },
15 | {
16 | command: "zenml secret get SECRET_NAME",
17 | description: "Get the key-value pairs for a particular secret"
18 | },
19 | {
20 | command: "zenml secret delete SECRET_NAME",
21 | description: "Delete a secret that you have previously registered"
22 | }
23 | ];
24 |
--------------------------------------------------------------------------------
/src/contents/stack.ts:
--------------------------------------------------------------------------------
1 | import { CommandListItem } from "../components/fallback-pages/Commands";
2 |
3 | export const stackCommands: CommandListItem[] = [
4 | {
5 | command: "zenml stack register a_new_local_stack -o default -a my_artifact_store",
6 | description: "Register a stack"
7 | },
8 | {
9 | command: "zenml stack list",
10 | description: "List the stacks that you have registered within your current ZenML server"
11 | },
12 | {
13 | command: "zenml stack delete STACK_NAME",
14 | description: "Delete a stack that you have previously registered"
15 | },
16 | {
17 | command: "zenml stack set STACK_NAME",
18 | description: "Set a different stack"
19 | },
20 | {
21 | command: "zenml stack get",
22 | description: "Check which stack is currently set as the default active stack"
23 | },
24 | {
25 | command: "zenml stack update STACK_NAME -o NEW_ORCHESTRATOR_NAME",
26 | description: "Update a stack"
27 | }
28 | ];
29 |
--------------------------------------------------------------------------------
/src/context/AuthContext.tsx:
--------------------------------------------------------------------------------
1 | import { authStateWriteSchema, getAuthState, removeAuthState, setAuthState } from "@/lib/sessions";
2 | import { createContext, useContext } from "react";
3 | import { z } from "zod";
4 |
5 | type AuthContextType = {
6 | getAuthState: () => boolean;
7 | removeAuthState: () => void;
8 | setAuthState: (value: z.infer) => void;
9 | };
10 |
11 | export const AuthContext = createContext(null);
12 |
13 | export function AuthProvider({ children }: { children: React.ReactNode }) {
14 | return (
15 |
22 | {children}
23 |
24 | );
25 | }
26 |
27 | export function useAuthContext() {
28 | const context = useContext(AuthContext);
29 | if (context === null) {
30 | throw new Error("useAuthContext must be used within an AuthProvider");
31 | }
32 | return context;
33 | }
34 |
--------------------------------------------------------------------------------
/src/data/fetch.ts:
--------------------------------------------------------------------------------
1 | function updateConfig(init?: RequestInit): RequestInit {
2 | return {
3 | credentials: "include",
4 | ...init,
5 | headers: {
6 | ...init?.headers,
7 | "Source-Context": "dashboard-v2"
8 | }
9 | };
10 | }
11 |
12 | export function fetcher(input: RequestInfo | URL, init?: RequestInit | undefined) {
13 | return fetch(input, updateConfig(init));
14 | }
15 |
--------------------------------------------------------------------------------
/src/data/pipeline-deployments/index.ts:
--------------------------------------------------------------------------------
1 | import { queryOptions } from "@tanstack/react-query";
2 | import { fetchPipelineDeployment } from "./pipeline-deployments-detail";
3 |
4 | export const pipelineDeploymentQueries = {
5 | all: ["pipeline_deployments"],
6 | detail: (deploymentId: string) =>
7 | queryOptions({
8 | queryKey: [...pipelineDeploymentQueries.all, deploymentId],
9 | queryFn: async () => fetchPipelineDeployment({ deploymentId })
10 | })
11 | };
12 |
--------------------------------------------------------------------------------
/src/data/pipeline-runs/fetch-orchestrator-run.ts:
--------------------------------------------------------------------------------
1 | import { FetchError } from "@/lib/fetch-error";
2 | import { UseMutationOptions, useMutation } from "@tanstack/react-query";
3 | import { fetchPipelineRun } from "./pipeline-run-detail-query";
4 | import { PipelineRun } from "@/types/pipeline-runs";
5 | type UpdateRunParams = {
6 | runId: string;
7 | };
8 |
9 | export function useFetchOrchestratorPipelineRun(
10 | options?: UseMutationOptions
11 | ) {
12 | return useMutation({
13 | ...options,
14 | mutationFn: async ({ runId }: UpdateRunParams) => {
15 | return fetchPipelineRun({ runId, queryParams: { refresh_status: true } });
16 | }
17 | });
18 | }
19 |
--------------------------------------------------------------------------------
/src/data/pipelines/index.ts:
--------------------------------------------------------------------------------
1 | import { PipelineListParams } from "@/types/pipelines";
2 | import { queryOptions } from "@tanstack/react-query";
3 | import { fetchAllPipelines } from "./pipeline-list-query";
4 |
5 | export const pipelineQueries = {
6 | all: ["pipelines"],
7 | pipelineList: (queryParams: PipelineListParams) =>
8 | queryOptions({
9 | queryKey: [...pipelineQueries.all, queryParams],
10 | queryFn: async () => fetchAllPipelines({ params: queryParams })
11 | })
12 | };
13 |
--------------------------------------------------------------------------------
/src/data/pipelines/pipeline-list-query.ts:
--------------------------------------------------------------------------------
1 | import { FetchError } from "@/lib/fetch-error";
2 | import { notFound } from "@/lib/not-found-error";
3 | import { objectToSearchParams } from "@/lib/url";
4 | import { PipelineListParams } from "@/types/pipelines";
5 | import { apiPaths, createApiPath } from "../api";
6 | import { fetcher } from "../fetch";
7 |
8 | export type PipelineOverview = {
9 | params: PipelineListParams;
10 | };
11 |
12 | export async function fetchAllPipelines({ params }: PipelineOverview) {
13 | const url = createApiPath(apiPaths.pipelines.all + "?" + objectToSearchParams(params));
14 | const res = await fetcher(url, {
15 | method: "GET",
16 | headers: {
17 | "Content-Type": "application/json"
18 | }
19 | });
20 |
21 | if (res.status === 404) notFound();
22 |
23 | if (!res.ok) {
24 | throw new FetchError({
25 | message: "Error while fetching pipelines",
26 | status: res.status,
27 | statusText: res.statusText
28 | });
29 | }
30 | return res.json();
31 | }
32 |
--------------------------------------------------------------------------------
/src/data/projects/index.ts:
--------------------------------------------------------------------------------
1 | import { queryOptions } from "@tanstack/react-query";
2 | import { fetchProject } from "./project-detail";
3 | import { fetchProjectStatistics } from "./project-statistics";
4 |
5 | export const projectQueries = {
6 | all: ["projects"],
7 | projectDetail: (projectId: string) =>
8 | queryOptions({
9 | queryKey: [...projectQueries.all, projectId],
10 | queryFn: async () => fetchProject({ projectId })
11 | }),
12 | projectStatistics: (projectId: string) =>
13 | queryOptions({
14 | queryKey: [...projectQueries.all, projectId, "statistics"],
15 | queryFn: async () => fetchProjectStatistics({ projectId })
16 | })
17 | };
18 |
--------------------------------------------------------------------------------
/src/data/secrets/index.ts:
--------------------------------------------------------------------------------
1 | import { ListSecretsParams } from "@/types/secret";
2 | import { queryOptions } from "@tanstack/react-query";
3 | import { fetchAllSecrets } from "./secrets-all-query";
4 | import { fetchSecretDetail } from "./get-secret-detail";
5 |
6 | export const secretQueries = {
7 | all: ["secrets"],
8 | secretList: (queryParams: ListSecretsParams) =>
9 | queryOptions({
10 | queryKey: [...secretQueries.all, queryParams],
11 | queryFn: async () => fetchAllSecrets({ params: queryParams })
12 | }),
13 | secretDetail: (secretId: string) =>
14 | queryOptions({
15 | queryKey: [...secretQueries.all, secretId],
16 | queryFn: async () => fetchSecretDetail(secretId)
17 | })
18 | };
19 |
--------------------------------------------------------------------------------
/src/data/session/logout-mutation.ts:
--------------------------------------------------------------------------------
1 | import { apiPaths, createApiPath } from "@/data/api";
2 | import { useMutation, UseMutationOptions } from "@tanstack/react-query";
3 | import { FetchError } from "../../lib/fetch-error";
4 | import { fetcher } from "../fetch";
5 |
6 | export async function loginUser() {
7 | const url = createApiPath(apiPaths.logout);
8 |
9 | // TODO possibly this fetch can be abstracted
10 | const res = await fetcher(url, {
11 | method: "GET",
12 | headers: {
13 | accept: "application/json"
14 | }
15 | });
16 |
17 | if (!res.ok) {
18 | throw new FetchError({
19 | status: res.status,
20 | statusText: res.statusText,
21 | message: "Failed to Login"
22 | });
23 | }
24 |
25 | return res.json();
26 | }
27 |
28 | export function useLogoutMutation(
29 | options?: Omit, "mutationFn">
30 | ) {
31 | return useMutation({
32 | mutationFn: async () => {
33 | return loginUser();
34 | },
35 | ...options
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/src/error-boundaries/PageBoundary.tsx:
--------------------------------------------------------------------------------
1 | import { isNotFoundError } from "@/lib/not-found-error";
2 | import { PropsWithChildren } from "react";
3 | import { useRouteError } from "react-router-dom";
4 |
5 | export function PageBoundary({ children }: PropsWithChildren) {
6 | const error = useRouteError();
7 | if (isNotFoundError(error)) {
8 | return <>{children ?? Not Found }>;
9 | }
10 |
11 | throw error;
12 | }
13 |
--------------------------------------------------------------------------------
/src/error-boundaries/RootBoundary.tsx:
--------------------------------------------------------------------------------
1 | import { useRouteError } from "react-router-dom";
2 | import AlertCircle from "@/assets/icons/alert-circle.svg?react";
3 |
4 | export function RootBoundary() {
5 | const error = useRouteError() as Error | undefined;
6 |
7 | return (
8 |
9 |
10 |
Something went wrong!
11 |
{error?.message}
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/hooks/use-route-segment.tsx:
--------------------------------------------------------------------------------
1 | import { useLocation } from "react-router-dom";
2 |
3 | export function useRouteSegment(index = 1) {
4 | const { pathname } = useLocation();
5 | const segments = pathname.split("/").filter(Boolean);
6 | return segments[index];
7 | }
8 |
--------------------------------------------------------------------------------
/src/layouts/AuthenticatedLayout/BreadcrumbsContext.tsx:
--------------------------------------------------------------------------------
1 | import { Breadcrumbs } from "@/components/breadcrumbs/types";
2 | import { createContext, useContext, useState } from "react";
3 |
4 | type BreadcrumbContextType = {
5 | breadcrumbs: Breadcrumbs;
6 | setBreadcrumbs: (breadcrumbs: Breadcrumbs) => void;
7 | };
8 |
9 | export const BreadcrumbContext = createContext(null);
10 |
11 | export function BreadcrumbContextProvider({ children }: { children: React.ReactNode }) {
12 | const [breadcrumbs, setBreadcrumbs] = useState([]);
13 |
14 | return (
15 |
16 | {children}
17 |
18 | );
19 | }
20 |
21 | export function useBreadcrumbsContext() {
22 | const context = useContext(BreadcrumbContext);
23 | if (context === null) {
24 | throw new Error("useBreadcrumbContext must be used within a BreadcrumbContextProvider");
25 | }
26 | return context;
27 | }
28 |
--------------------------------------------------------------------------------
/src/layouts/GradientLayout.tsx:
--------------------------------------------------------------------------------
1 | import { Outlet } from "react-router-dom";
2 | import ZenML from "@/assets/icons/zenml.svg?react";
3 |
4 | export function GradientLayout() {
5 | return (
6 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/layouts/RootLayout.tsx:
--------------------------------------------------------------------------------
1 | import { useServerInfo } from "@/data/server/info-query";
2 | import { routes } from "@/router/routes";
3 | import { useEffect } from "react";
4 | import { Outlet, useNavigate } from "react-router-dom";
5 |
6 | export function RootLayout() {
7 | const navigate = useNavigate();
8 | const { data } = useServerInfo({ throwOnError: true });
9 |
10 | useEffect(() => {
11 | if (data && data.active === false) {
12 | navigate(routes.activateServer + `?redirect=${routes.onboarding}`, { replace: true });
13 | }
14 | }, [data]);
15 |
16 | return (
17 |
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/src/layouts/connectors-detail/breadcrumbs.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useBreadcrumbsContext } from "../AuthenticatedLayout/BreadcrumbsContext";
3 | import { ServiceConnector } from "@/types/service-connectors";
4 | import { routes } from "@/router/routes";
5 | import { connectorBreadcrumb } from "@/components/breadcrumbs/library";
6 |
7 | export function useConnectorDetailBreadcrumbs(connector?: ServiceConnector) {
8 | const { setBreadcrumbs } = useBreadcrumbsContext();
9 |
10 | useEffect(() => {
11 | if (connector) {
12 | setBreadcrumbs([
13 | ...connectorBreadcrumb,
14 | {
15 | label: connector.name,
16 | href: routes.settings.connectors.detail.configuration(connector.id)
17 | }
18 | ]);
19 | }
20 | }, [setBreadcrumbs, connector]);
21 | }
22 |
--------------------------------------------------------------------------------
/src/layouts/connectors-detail/layout.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@zenml-io/react-component-library/components/server";
2 | import { Outlet } from "react-router-dom";
3 | import { ConnectorDetailHeader } from "./header";
4 | import { ResourcesContextProvider } from "./resources-context";
5 | import { ServiceConnectorTabs } from "./tabs";
6 |
7 | export default function ConnectorDetailLayout() {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/src/layouts/non-project-scoped/header.tsx:
--------------------------------------------------------------------------------
1 | import { NameSection } from "./name-section";
2 | import { NonProjectTabs } from "./tabs";
3 |
4 | export function Header() {
5 | return (
6 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/layouts/non-project-scoped/layout.tsx:
--------------------------------------------------------------------------------
1 | import { Outlet } from "react-router-dom";
2 | import { Header } from "./header";
3 | import { FloatingProgressLink } from "@/components/onboarding/floating-progress-link";
4 | export function NonProjectScopedLayout() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/layouts/non-project-scoped/server-members.tsx:
--------------------------------------------------------------------------------
1 | import { AvatarStack } from "@/components/avatar-stack";
2 | import { useAllMembers } from "@/data/users/users-all-query";
3 | import { Skeleton } from "@zenml-io/react-component-library/components/server";
4 |
5 | export function ServerMembers() {
6 | const membersQuery = useAllMembers({ params: { active: true } });
7 |
8 | if (membersQuery.isPending) return ;
9 | if (membersQuery.isError) return null;
10 |
11 | const users = membersQuery.data;
12 |
13 | return ;
14 | }
15 |
--------------------------------------------------------------------------------
/src/layouts/project-tabs/header.tsx:
--------------------------------------------------------------------------------
1 | import { NameSection } from "./name-section";
2 | import { ProjectTabs } from "./tabs";
3 | export function ProjectHeader() {
4 | return (
5 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/layouts/project-tabs/layout.tsx:
--------------------------------------------------------------------------------
1 | import { Outlet } from "react-router-dom";
2 | import { ProjectHeader } from "./header";
3 | export function ProjectTabsLayout() {
4 | return (
5 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/layouts/project-tabs/name-section.tsx:
--------------------------------------------------------------------------------
1 | import { ServerMembers } from "../non-project-scoped/server-members";
2 | import { IdSection } from "./id-section";
3 | export function NameSection() {
4 | return (
5 |
6 |
7 |
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/src/layouts/settings/Menu.tsx:
--------------------------------------------------------------------------------
1 | import NavLink from "@/components/NavLink";
2 |
3 | type MenuItem = {
4 | name: string;
5 | href: string;
6 | isActiveOverride?: (pathname: string) => boolean;
7 | };
8 |
9 | type MenuProps = {
10 | items: MenuItem[];
11 | };
12 |
13 | export function SettingsMenu({ items }: MenuProps) {
14 | return (
15 |
16 |
17 | {items.map((item) => (
18 |
19 |
20 | {item.name}
21 |
22 |
23 | ))}
24 |
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/src/layouts/settings/project-settings/project-display.tsx:
--------------------------------------------------------------------------------
1 | import Box from "@/assets/icons/container.svg?react";
2 |
3 | export function DisplayProject() {
4 | return (
5 |
6 |
7 |
8 |
9 |
default
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/layouts/settings/settings-layout/VersionDisplay.tsx:
--------------------------------------------------------------------------------
1 | import ZenMLIcon from "@/assets/icons/zenml-icon.svg?react";
2 | import { useServerInfo } from "@/data/server/info-query";
3 | import { Skeleton } from "@zenml-io/react-component-library";
4 |
5 | export function VersionDisplay() {
6 | const { data, isPending, isError } = useServerInfo();
7 | if (isPending) return ;
8 | if (isError) return null;
9 |
10 | return (
11 |
12 |
13 |
14 |
Open source
15 |
16 |
ZenML v{data.version}
17 |
18 | UI Version {import.meta.env.VITE_FRONTEND_VERSION}
19 |
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src/lib/analytics.ts:
--------------------------------------------------------------------------------
1 | export const analyticsServerUrl = "https://analytics.zenml.io/batch";
2 |
--------------------------------------------------------------------------------
/src/lib/code-snippets.ts:
--------------------------------------------------------------------------------
1 | export function getArtifactVersionSnippet(id: string) {
2 | return `from zenml.client import Client
3 |
4 | artifact = Client().get_artifact_version("${id}")
5 | data = artifact.load()`;
6 | }
7 |
8 | export function getStepSnippet(id: string) {
9 | return `from zenml.client import Client
10 |
11 | step = Client().get_run_step("${id}")
12 | config = step.config`;
13 | }
14 |
15 | export function getRunSnippet(id: string) {
16 | return `from zenml.client import Client
17 |
18 | run = Client().get_pipeline_run("${id}")
19 | config = run.config`;
20 | }
21 |
22 | export function getSecretSnippet(id: string) {
23 | return `from zenml.client import Client
24 |
25 | secret = Client().get_secret("${id}")
26 | `;
27 | }
28 |
--------------------------------------------------------------------------------
/src/lib/common.ts:
--------------------------------------------------------------------------------
1 | export function sleep(ms: number) {
2 | return new Promise((resolve) => setTimeout(resolve, ms));
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/components.ts:
--------------------------------------------------------------------------------
1 | import { StackComponent } from "@/types/components";
2 |
3 | export function extractComponents(json?: Record): StackComponent[] {
4 | if (!json) return [];
5 | const components: StackComponent[] = [];
6 |
7 | for (const key in json) {
8 | if (Array.isArray(json[key])) {
9 | components.push(...json[key]);
10 | }
11 | }
12 |
13 | return components;
14 | }
15 |
--------------------------------------------------------------------------------
/src/lib/constants.ts:
--------------------------------------------------------------------------------
1 | import { StackComponentType } from "../types/components";
2 |
3 | export const FIVEMEGABYTES = 5 * 1024 * 1024;
4 |
5 | export const stackComponentTypes: StackComponentType[] = [
6 | "orchestrator",
7 | "artifact_store",
8 | "container_registry",
9 | "step_operator",
10 | "model_deployer",
11 | "feature_store",
12 | "experiment_tracker",
13 | "alerter",
14 | "annotator",
15 | "data_validator",
16 | "image_builder",
17 | "model_registry"
18 | ] as const;
19 |
--------------------------------------------------------------------------------
/src/lib/copy.ts:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | export function useCopy() {
4 | const [copied, setCopied] = useState(false);
5 |
6 | function copyToClipboard(text: string) {
7 | if (navigator.clipboard) {
8 | navigator.clipboard.writeText(text);
9 | setCopied(true);
10 | setTimeout(() => {
11 | setCopied(false);
12 | }, 2000);
13 | }
14 | }
15 | return { copied, copyToClipboard };
16 | }
17 |
--------------------------------------------------------------------------------
/src/lib/fetch-error.ts:
--------------------------------------------------------------------------------
1 | export class FetchError extends Error {
2 | public status: number;
3 | public statusText: string;
4 | public message: string;
5 |
6 | constructor({
7 | message,
8 | status,
9 | statusText
10 | }: {
11 | status: number;
12 | statusText: string;
13 | message: string;
14 | }) {
15 | super(message);
16 | this.status = status;
17 | this.statusText = statusText;
18 | this.message = message;
19 | }
20 | }
21 |
22 | export function isFetchError(err: unknown): err is FetchError {
23 | return err instanceof FetchError;
24 | }
25 |
--------------------------------------------------------------------------------
/src/lib/images.ts:
--------------------------------------------------------------------------------
1 | export function getGradientImage(name: string, size: number = 24) {
2 | return `https://avatar.vercel.sh/${name}?size=${size}`;
3 | }
4 |
5 | export function generateNumberFromSalt(salt: string): number {
6 | function hashString(str: string): number {
7 | let hash = 0;
8 | for (let i = 0; i < str.length; i++) {
9 | hash = (hash * 31 + str.charCodeAt(i)) >>> 0;
10 | }
11 | return hash;
12 | }
13 |
14 | const hash = hashString(salt);
15 | return (hash % 49) + 1;
16 | }
17 |
--------------------------------------------------------------------------------
/src/lib/login-command.ts:
--------------------------------------------------------------------------------
1 | import { DeploymentType } from "../types/server";
2 |
3 | export function getLoginCommand(deploymentType: DeploymentType) {
4 | switch (deploymentType) {
5 | case "local":
6 | return "zenml login --local";
7 |
8 | case "docker":
9 | return "zenml login --local --docker";
10 | default:
11 | return `zenml login ${window.location.origin}`;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/lib/not-found-error.ts:
--------------------------------------------------------------------------------
1 | const NotFoundErrorCode = "NotFoundError";
2 |
3 | type NotFoundError = Error & { cause: typeof NotFoundErrorCode };
4 |
5 | export function notFound(): never {
6 | const error = new Error(NotFoundErrorCode);
7 | (error as NotFoundError).cause = NotFoundErrorCode;
8 | throw error;
9 | }
10 |
11 | export function isNotFoundError(error: unknown): error is NotFoundError {
12 | if (typeof error !== "object" || error === null || !("cause" in error)) {
13 | return false;
14 | }
15 | return error.cause === NotFoundErrorCode;
16 | }
17 |
--------------------------------------------------------------------------------
/src/lib/provider.ts:
--------------------------------------------------------------------------------
1 | import { StackDeploymentProvider } from "../types/stack";
2 |
3 | export function getCloudProviderName(name: StackDeploymentProvider | string) {
4 | switch (name) {
5 | case "aws":
6 | return "AWS";
7 | case "azure":
8 | return "Azure";
9 | case "gcp":
10 | return "GCP";
11 | default:
12 | return "Provider";
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/lib/search.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect, describe } from "vitest";
2 | import { sanitizeSearchValue } from "./search";
3 |
4 | describe("sanitizeSearchValue removes filter prefix if present", () => {
5 | const testCases = [
6 | { input: "equals:apple", expected: "apple" },
7 | { input: "contains:banana", expected: "banana" },
8 | { input: "startswith:cherry", expected: "cherry" },
9 | { input: "endswith:date", expected: "date" },
10 | { input: "gte:5", expected: "5" },
11 | { input: "gt:10", expected: "10" },
12 | { input: "lte:100", expected: "100" },
13 | { input: "lt:50", expected: "50" },
14 | { input: "dummyData", expected: "dummyData" },
15 | { input: "random:value", expected: "random:value" }, // No filter prefix, should return unchanged
16 | { input: "", expected: "" } // Empty string should remain empty
17 | ];
18 |
19 | testCases.forEach(({ input, expected }) =>
20 | test(input, () => expect(sanitizeSearchValue(input)).toBe(expected))
21 | );
22 | });
23 |
--------------------------------------------------------------------------------
/src/lib/search.ts:
--------------------------------------------------------------------------------
1 | const filterPrefixes = [
2 | "equals:",
3 | "contains:",
4 | "startswith:",
5 | "endswith:",
6 | "gte:",
7 | "gt:",
8 | "lte:",
9 | "lt:"
10 | ];
11 |
12 | export function sanitizeSearchValue(value: string) {
13 | //check if value starts with filter prefix and remove it in case it does
14 | const filterPrefix = filterPrefixes.find((prefix) => value.startsWith(prefix));
15 | if (filterPrefix) {
16 | return value.slice(filterPrefix.length);
17 | }
18 | return value;
19 | }
20 |
--------------------------------------------------------------------------------
/src/lib/server.spec.ts:
--------------------------------------------------------------------------------
1 | import { DeploymentType } from "@/types/server";
2 | import { describe, test, expect } from "vitest";
3 | import { checkIsLocalServer } from "./server";
4 |
5 | describe("returns if the server is deployed locally or not", () => {
6 | test("returns true if the deployment type is local", () => {
7 | const deploymentType: DeploymentType = "local";
8 | const isLocal = checkIsLocalServer(deploymentType);
9 | expect(isLocal).toBe(true);
10 | });
11 |
12 | test("returns true if the deployment type is docker", () => {
13 | const deploymentType: DeploymentType = "docker";
14 | const isLocal = checkIsLocalServer(deploymentType);
15 | expect(isLocal).toBe(true);
16 | });
17 |
18 | test("returns false if the deployment type is not local and not docker", () => {
19 | const deploymentType: DeploymentType = "other";
20 | const isLocal = checkIsLocalServer(deploymentType);
21 | expect(isLocal).toBe(false);
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/src/lib/server.ts:
--------------------------------------------------------------------------------
1 | import { AuthScheme, DeploymentType } from "@/types/server";
2 |
3 | export function checkIsLocalServer(deploymentType: DeploymentType) {
4 | return deploymentType === "local" || deploymentType === "docker";
5 | }
6 |
7 | export function isNoAuthServer(authScheme: AuthScheme) {
8 | return authScheme === "NO_AUTH";
9 | }
10 |
--------------------------------------------------------------------------------
/src/lib/sessions.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const authStateSchema = z.boolean();
4 | export const authStateWriteSchema = z.enum(["true", "false"]);
5 |
6 | const authStateKey = "isAuthenticated";
7 |
8 | export function getAuthState() {
9 | try {
10 | const authState = localStorage.getItem(authStateKey);
11 | if (authState === null) {
12 | return false;
13 | }
14 | return authStateSchema.parse(JSON.parse(authState));
15 | } catch (e) {
16 | return false;
17 | }
18 | }
19 |
20 | export function removeAuthState() {
21 | localStorage.removeItem(authStateKey);
22 | }
23 |
24 | export function setAuthState(value: z.infer) {
25 | localStorage.setItem(authStateKey, value);
26 | }
27 |
--------------------------------------------------------------------------------
/src/lib/stacks.ts:
--------------------------------------------------------------------------------
1 | import { fetchStacks } from "@/data/stacks/stacklist-query";
2 | import AwesomeDebouncePromise from "awesome-debounce-promise";
3 |
4 | export const validateStackName = AwesomeDebouncePromise(async (name: string) => {
5 | const data = await fetchStacks({ name });
6 | return data.total === 0;
7 | }, 500);
8 |
--------------------------------------------------------------------------------
/src/lib/type-guards.ts:
--------------------------------------------------------------------------------
1 | export function isString(value: unknown): value is string {
2 | return typeof value === "string";
3 | }
4 |
5 | export function isNumber(value: unknown): value is number {
6 | return typeof value === "number" && !isNaN(value);
7 | }
8 |
9 | export function isBoolean(value: unknown): value is boolean {
10 | return typeof value === "boolean";
11 | }
12 |
13 | export function isArray(value: unknown): value is T[] {
14 | return Array.isArray(value);
15 | }
16 |
17 | export function isObject(value: unknown): value is object {
18 | return value !== null && typeof value === "object" && !Array.isArray(value);
19 | }
20 |
--------------------------------------------------------------------------------
/src/lib/user.ts:
--------------------------------------------------------------------------------
1 | import { routes } from "@/router/routes";
2 | import { User, UserMetadata } from "@/types/user";
3 |
4 | export function getUsername(user: User) {
5 | return user.body?.full_name || user.name;
6 | }
7 |
8 | export function getActivationToken(user: User) {
9 | return `${window.location.origin}${routes.activateUser}?user=${user.id}&token=${user.body?.activation_token}`;
10 | }
11 |
12 | export function checkUserOnboarding(user: User) {
13 | if (user.body?.email_opted_in === null) return true;
14 | if ((user.metadata?.user_metadata as UserMetadata)?.finished_onboarding_survey) return false;
15 | // old versions need to check for awareness_channels
16 | if (
17 | !(user.metadata?.user_metadata as { awareness_channels?: string[] })?.awareness_channels?.length
18 | )
19 | return true;
20 |
21 | return false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { App } from "./App.tsx";
4 | import "./assets/styles/index.css";
5 |
6 | ReactDOM.createRoot(document.getElementById("root")!).render(
7 |
8 |
9 |
10 | );
11 |
--------------------------------------------------------------------------------
/src/router/queryclient.ts:
--------------------------------------------------------------------------------
1 | import { FetchError } from "@/lib/fetch-error";
2 | import { removeAuthState } from "@/lib/sessions";
3 | import { QueryCache, QueryClient } from "@tanstack/react-query";
4 |
5 | function handle401() {
6 | removeAuthState();
7 | window.location.reload();
8 | }
9 |
10 | export const queryClient = new QueryClient({
11 | defaultOptions: {
12 | queries: {
13 | retry: false
14 | }
15 | },
16 | queryCache: new QueryCache({
17 | onError: (error) => {
18 | if (error instanceof FetchError) {
19 | if (error.status === 401) {
20 | handle401();
21 | }
22 | }
23 | }
24 | })
25 | });
26 |
--------------------------------------------------------------------------------
/src/types/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/90236a062535d2182b67f20f23cd8c8280f01921/src/types/.gitkeep
--------------------------------------------------------------------------------
/src/types/analytics.ts:
--------------------------------------------------------------------------------
1 | export type PageEvent = {
2 | type: "page";
3 | user_id: string;
4 | debug: boolean;
5 | name: string;
6 | category: string;
7 | context: PageEventContext;
8 | properties: PageEventProperties;
9 | };
10 |
11 | export type PageEventPage = {
12 | path: string;
13 | referrer: string;
14 | search: string;
15 | title: string;
16 | };
17 |
18 | export type PageEventContext = {
19 | locale: string;
20 | timezone: string;
21 | userAgent: string;
22 | page: PageEventPage;
23 | };
24 |
25 | export type PageEventProperties = PageEventPage & { category: string; [key: string]: any };
26 |
27 | export type TrackEvent = {
28 | type: "track";
29 | user_id: string;
30 | debug: boolean;
31 | event: string;
32 | properties: Record;
33 | };
34 |
--------------------------------------------------------------------------------
/src/types/artifact-versions.ts:
--------------------------------------------------------------------------------
1 | import { components, operations } from "./core";
2 |
3 | export type ArtifactVersionBody = components["schemas"]["ArtifactVersionResponseBody"];
4 | export type ArtifactVersion = components["schemas"]["ArtifactVersionResponse"];
5 | // Artifact Visualization
6 | export type LoadedVisualization = components["schemas"]["LoadedVisualization"];
7 | export type ArtifactVisualization = components["schemas"]["ArtifactVisualizationResponse"];
8 | export type ArtifactVisualizationQueryParams = NonNullable<
9 | operations["get_artifact_visualization_api_v1_artifact_versions__artifact_version_id__visualize_get"]["parameters"]["query"]
10 | >;
11 |
12 | export type ArtifactType = components["schemas"]["ArtifactType"];
13 |
--------------------------------------------------------------------------------
/src/types/auth.ts:
--------------------------------------------------------------------------------
1 | import { operations } from "./core";
2 |
3 | export type ApiTokenQueryParams = NonNullable<
4 | operations["api_token_api_v1_api_token_get"]["parameters"]["query"]
5 | >;
6 |
--------------------------------------------------------------------------------
/src/types/code-repository.ts:
--------------------------------------------------------------------------------
1 | import { components, operations } from "./core";
2 |
3 | export type CodeRepository = components["schemas"]["CodeRepositoryResponse"];
4 |
5 | export type PageCodeRepositoryResponse = components["schemas"]["Page_CodeRepositoryResponse_"];
6 |
7 | export type CodeRepositoryListQueryParams = NonNullable<
8 | operations["list_code_repositories_api_v1_code_repositories_get"]["parameters"]["query"]
9 | >;
10 |
--------------------------------------------------------------------------------
/src/types/common.ts:
--------------------------------------------------------------------------------
1 | export type ResponsePage = {
2 | index: number;
3 | max_size: number;
4 | total_pages: number;
5 | total: number;
6 | items: T[];
7 | };
8 |
9 | export type AnyDict = {
10 | [key: string]: any;
11 | };
12 |
13 | export type MetadataMap = {
14 | [key: string]: string | number | boolean | Record | unknown[];
15 | };
16 |
--------------------------------------------------------------------------------
/src/types/components.ts:
--------------------------------------------------------------------------------
1 | import { components, operations } from "./core";
2 |
3 | export type StackComponent = components["schemas"]["ComponentResponse"];
4 | export type StackComponentType = components["schemas"]["StackComponentType"];
5 | export type StackComponentPage = components["schemas"]["Page_ComponentResponse_"];
6 | export type StackComponentListParams = NonNullable<
7 | operations["list_stack_components_api_v1_components_get"]["parameters"]["query"]
8 | >;
9 |
10 | export type StackComponentRequest = components["schemas"]["ComponentRequest"];
11 | export type StackComponentUpdateRequest = components["schemas"]["ComponentUpdate"];
12 |
--------------------------------------------------------------------------------
/src/types/devices.ts:
--------------------------------------------------------------------------------
1 | import { components, operations } from "./core";
2 |
3 | export type Device = components["schemas"]["OAuthDeviceResponse"];
4 | export type DeviceQueryParams = NonNullable<
5 | operations["get_authorization_device_api_v1_devices__device_id__get"]["parameters"]["query"]
6 | >;
7 |
8 | export type DeviceVerifyPayload = components["schemas"]["OAuthDeviceVerificationRequest"];
9 |
--------------------------------------------------------------------------------
/src/types/flavors.ts:
--------------------------------------------------------------------------------
1 | import { components, operations } from "./core";
2 |
3 | export type FlavorsPage = components["schemas"]["Page_FlavorResponse_"];
4 | export type FlavorListQueryParams = NonNullable<
5 | operations["list_flavors_api_v1_flavors_get"]["parameters"]["query"]
6 | >;
7 |
8 | export type Flavor = components["schemas"]["FlavorResponse"];
9 |
--------------------------------------------------------------------------------
/src/types/onboarding.ts:
--------------------------------------------------------------------------------
1 | export type OnboardingChecklistItemName =
2 | | "device_verified"
3 | | "pipeline_run"
4 | | "stack_with_remote_orchestrator_created"
5 | | "stack_with_remote_artifact_store_created"
6 | | "pipeline_run_with_remote_orchestrator"
7 | | "pipeline_run_with_remote_artifact_store"
8 | | "production_setup_completed";
9 |
10 | export type OnboardingResponse = OnboardingChecklistItemName[];
11 |
12 | export type OnboardingStep = {
13 | completed: boolean;
14 | active: boolean;
15 | hasDownstreamStep: boolean;
16 | };
17 |
--------------------------------------------------------------------------------
/src/types/pipeline-builds.ts:
--------------------------------------------------------------------------------
1 | import { components } from "./core";
2 |
3 | export type PipelineBuildResponse = components["schemas"]["PipelineBuildResponse"];
4 | export type BuildItem = components["schemas"]["BuildItem"];
5 | export type BuildItemMap = { [key: string]: BuildItem };
6 |
--------------------------------------------------------------------------------
/src/types/pipeline-deployments.ts:
--------------------------------------------------------------------------------
1 | import { components } from "./core";
2 |
3 | export type PipelineDeployment = components["schemas"]["PipelineDeploymentResponse"];
4 | export type StepOutput = components["schemas"]["Step-Output"];
5 | export type StepOutputInput = {
6 | step_name: string;
7 | output_name: string;
8 | };
9 |
10 | export type ExternalArtifactConfig = components["schemas"]["ExternalArtifactConfiguration"];
11 | export type ModelVersionLazyLoader = components["schemas"]["ModelVersionDataLazyLoader"];
12 | export type ClientLazyLoader = components["schemas"]["ClientLazyLoader"];
13 |
--------------------------------------------------------------------------------
/src/types/pipeline-runs.ts:
--------------------------------------------------------------------------------
1 | import { components, operations } from "./core";
2 |
3 | export type ExecutionStatus = components["schemas"]["ExecutionStatus"];
4 | export type PipelineRun = components["schemas"]["PipelineRunResponse"];
5 | export type PipelineRunBody = components["schemas"]["PipelineRunResponseBody"];
6 |
7 | export type PipelineRunOvervieweParams = NonNullable<
8 | operations["list_runs_api_v1_runs_get"]["parameters"]["query"]
9 | >;
10 |
11 | export type PipelineRunDetailQueryParams = NonNullable<
12 | operations["get_run_api_v1_runs__run_id__get"]["parameters"]["query"]
13 | >;
14 | export type PipelineRunPage = components["schemas"]["Page_PipelineRunResponse_"];
15 |
--------------------------------------------------------------------------------
/src/types/pipelines.ts:
--------------------------------------------------------------------------------
1 | import { components, operations } from "./core";
2 |
3 | export type Pipeline = components["schemas"]["PipelineResponse"];
4 | export type PipelineBody = components["schemas"]["PipelineResponseBody"];
5 |
6 | export type PipelineListParams = NonNullable<
7 | operations["list_pipelines_api_v1_pipelines_get"]["parameters"]["query"]
8 | >;
9 |
--------------------------------------------------------------------------------
/src/types/projects.ts:
--------------------------------------------------------------------------------
1 | import { components } from "./core";
2 |
3 | export type Project = components["schemas"]["ProjectResponse"];
4 | export type ProjectStatistics = components["schemas"]["ProjectStatistics"];
5 |
--------------------------------------------------------------------------------
/src/types/reodotdev.d.ts:
--------------------------------------------------------------------------------
1 | declare module "reodotdev" {
2 | interface ReoInstance {
3 | init(options: { clientID: string }): void;
4 | identify(identity: { username: string; type: string }): void;
5 | }
6 |
7 | export function loadReoScript(options: { clientID: string }): Promise;
8 | }
9 |
--------------------------------------------------------------------------------
/src/types/secret.ts:
--------------------------------------------------------------------------------
1 | import { components, operations } from "./core";
2 |
3 | export type SecretNamespace = components["schemas"]["SecretResponse"];
4 |
5 | export type CreateSecret = components["schemas"]["SecretRequest"];
6 | export type Secret = components["schemas"]["SecretResponse"];
7 | export type UpdateSecret = components["schemas"]["SecretUpdate"];
8 | export type ListSecretsParams = NonNullable<
9 | operations["list_secrets_api_v1_secrets_get"]["parameters"]["query"]
10 | >;
11 | export type SecretsPage = components["schemas"]["Page_SecretResponse_"];
12 |
--------------------------------------------------------------------------------
/src/types/server.ts:
--------------------------------------------------------------------------------
1 | import { components } from "./core";
2 |
3 | export type ServerInfo = components["schemas"]["ServerModel"];
4 | export type DeploymentType = components["schemas"]["ServerDeploymentType"];
5 | export type AuthScheme = components["schemas"]["AuthScheme"];
6 |
7 | export type ServerSettings = components["schemas"]["ServerSettingsResponse"];
8 | export type ServerSettigsUpdate = components["schemas"]["ServerSettingsUpdate"];
9 |
10 | export type ServerActivationPayload = components["schemas"]["ServerActivationRequest"];
11 |
--------------------------------------------------------------------------------
/src/types/service-accounts.ts:
--------------------------------------------------------------------------------
1 | import { components, operations } from "./core";
2 |
3 | export type ListServiceAccountsParams = NonNullable<
4 | operations["list_service_accounts_api_v1_service_accounts_get"]["parameters"]["query"]
5 | >;
6 | export type ServiceAccountsList = components["schemas"]["Page_ServiceAccountResponse_"];
7 | export type ServiceAccount = components["schemas"]["ServiceAccountResponse"];
8 | export type CreateServiceAccount = components["schemas"]["ServiceAccountRequest"];
9 | export type UpdateServiceAccount = components["schemas"]["ServiceAccountUpdate"];
10 |
11 | export type ApiKey = components["schemas"]["APIKeyResponse"];
12 | export type CreateApiKey = components["schemas"]["APIKeyRequest"];
13 | export type UpdateApiKey = components["schemas"]["APIKeyUpdate"];
14 | export type RotateApi = components["schemas"]["APIKeyRotateRequest"];
15 | export type ApiKeyList = components["schemas"]["Page_APIKeyResponse_"];
16 |
--------------------------------------------------------------------------------
/src/types/session.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 | import { components } from "./core";
3 |
4 | export type LoginResponse = components["schemas"]["OAuthTokenResponse"];
5 |
6 | export const loginFormSchema = z.object({
7 | username: z.string().min(1),
8 | password: z.string().optional()
9 | });
10 |
11 | export type LoginFormType = z.infer;
12 |
--------------------------------------------------------------------------------
/src/types/steps.ts:
--------------------------------------------------------------------------------
1 | import { components } from "./core";
2 |
3 | export type Step = components["schemas"]["StepRunResponse"];
4 |
5 | export type StepDict = Record;
6 |
--------------------------------------------------------------------------------
/src/types/user.ts:
--------------------------------------------------------------------------------
1 | import { components, operations } from "./core";
2 |
3 | export type User = components["schemas"]["UserResponse"];
4 | export type UserBody = components["schemas"]["UserResponseBody"];
5 | export type UserPage = components["schemas"]["Page_UserResponse_"];
6 |
7 | export type CreateUser = components["schemas"]["UserRequest"];
8 |
9 | export type ListUserParams = NonNullable<
10 | operations["list_users_api_v1_users_get"]["parameters"]["query"]
11 | >;
12 |
13 | export type UpdateUser = components["schemas"]["UserUpdate"];
14 |
15 | export type UserMetadata = {
16 | infra_providers?: string[];
17 | primary_use?: string;
18 | overview_tour_done?: boolean;
19 | usage_reason?: string;
20 | comparing_tools?: string[];
21 | finished_onboarding_survey?: boolean;
22 | };
23 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | import { zenmlPreset } from "@zenml-io/react-component-library/tailwind";
2 | import defaultTheme from "tailwindcss/defaultTheme";
3 |
4 | /** @type {import('tailwindcss').Config} */
5 | export default {
6 | content: [
7 | "./index.html",
8 | "./src/**/*.{js,ts,jsx,tsx}",
9 | "./node_modules/@zenml-io/react-component-library/**/*.{js,ts,jsx,tsx}"
10 | ],
11 | theme: {
12 | extend: {
13 | fontFamily: {
14 | sans: ["Inter", ...defaultTheme.fontFamily.sans]
15 | }
16 | }
17 | },
18 | plugins: [
19 | require("@tailwindcss/forms"),
20 | require("tailwindcss-animate"),
21 | require("@tailwindcss/typography"),
22 | require("@tailwindcss/container-queries")
23 | ],
24 | presets: [zenmlPreset]
25 | };
26 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true,
22 | "baseUrl": "./",
23 | "paths": {
24 | "@/*": ["src/*"]
25 | }
26 | },
27 | "include": ["src"],
28 | "references": [{ "path": "./tsconfig.node.json" }]
29 | }
30 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true,
8 | "strict": true
9 | },
10 | "include": ["vite.config.ts"]
11 | }
12 |
--------------------------------------------------------------------------------