├── src
├── types
│ ├── .gitkeep
│ ├── code-highlighting.ts
│ ├── auth.ts
│ ├── projects.ts
│ ├── pipeline-builds.ts
│ ├── reodotdev.d.ts
│ ├── steps.ts
│ ├── flavors.ts
│ ├── common.ts
│ ├── session.ts
│ ├── onboarding.ts
│ ├── devices.ts
│ ├── code-repository.ts
│ ├── pipelines.ts
│ ├── logs.ts
│ ├── pipeline-deployments.ts
│ ├── deployments.ts
│ ├── server.ts
│ ├── secret.ts
│ ├── pipeline-snapshots.ts
│ ├── components.ts
│ ├── artifact-versions.ts
│ ├── user.ts
│ └── analytics.ts
├── contents
│ ├── secrets.ts
│ ├── stack.ts
│ └── repositories.ts
├── lib
│ ├── analytics.ts
│ ├── environment.ts
│ ├── common.ts
│ ├── json-faker.ts
│ ├── stacks.ts
│ ├── name.ts
│ ├── server.ts
│ ├── provider.ts
│ ├── login-command.ts
│ ├── components.ts
│ ├── copy.ts
│ ├── search.ts
│ ├── not-found-error.ts
│ ├── fetch-error.ts
│ ├── constants.ts
│ ├── type-guards.ts
│ ├── images.ts
│ ├── sessions.ts
│ ├── code-snippets.ts
│ ├── timeline
│ │ └── types.ts
│ └── user.ts
├── app
│ ├── runs
│ │ ├── [id]
│ │ │ ├── pipeline-viz
│ │ │ │ ├── types.ts
│ │ │ │ └── timeline
│ │ │ │ │ ├── hooks
│ │ │ │ │ └── use-timelie-item.ts
│ │ │ │ │ ├── services
│ │ │ │ │ └── timeline-empty-state-message.ts
│ │ │ │ │ └── components
│ │ │ │ │ └── timeline-empty-state.tsx
│ │ │ └── create-snapshot
│ │ │ │ └── page.tsx
│ │ ├── page.tsx
│ │ ├── ButtonGroup.tsx
│ │ └── RunsSelectorContext.tsx
│ ├── deployments
│ │ ├── [deploymentId]
│ │ │ ├── playground
│ │ │ │ └── _components
│ │ │ │ │ ├── outputs
│ │ │ │ │ ├── types.ts
│ │ │ │ │ ├── empty-state.tsx
│ │ │ │ │ ├── footer.tsx
│ │ │ │ │ ├── header.tsx
│ │ │ │ │ └── run-card-avatar.tsx
│ │ │ │ │ └── loader.tsx
│ │ │ ├── _layout
│ │ │ │ └── header
│ │ │ │ │ ├── not-available-tag.tsx
│ │ │ │ │ ├── use-active-tab.ts
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── pipeline-tag.tsx
│ │ │ ├── runs
│ │ │ │ └── page.tsx
│ │ │ ├── layout.tsx
│ │ │ └── _components
│ │ │ │ └── common.tsx
│ │ ├── page.tsx
│ │ └── columns.tsx
│ ├── settings
│ │ ├── general
│ │ │ ├── GeneralFormSchema.ts
│ │ │ └── page.tsx
│ │ ├── connectors
│ │ │ ├── create
│ │ │ │ ├── connector-type
│ │ │ │ │ ├── schema.ts
│ │ │ │ │ └── description.tsx
│ │ │ │ ├── header.tsx
│ │ │ │ ├── configuration
│ │ │ │ │ ├── debounced-check.ts
│ │ │ │ │ ├── schema.ts
│ │ │ │ │ └── skip-verify.tsx
│ │ │ │ ├── breadcrumbs.ts
│ │ │ │ ├── success
│ │ │ │ │ ├── success-box.tsx
│ │ │ │ │ └── table.tsx
│ │ │ │ ├── wizard.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── [id]
│ │ │ │ ├── configuration
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── resources
│ │ │ │ │ └── page.tsx
│ │ │ │ └── components
│ │ │ │ │ └── page.tsx
│ │ │ ├── new-connector-button.tsx
│ │ │ ├── page.tsx
│ │ │ ├── header.tsx
│ │ │ └── list-content.tsx
│ │ ├── members
│ │ │ ├── page.tsx
│ │ │ └── service.ts
│ │ ├── secrets
│ │ │ ├── form-schema.ts
│ │ │ ├── [id]
│ │ │ │ └── breadcrumbs.ts
│ │ │ ├── page.tsx
│ │ │ └── service.ts
│ │ ├── mcp
│ │ │ └── types.ts
│ │ ├── profile
│ │ │ └── UpdateProfileSchema.ts
│ │ ├── service-accounts
│ │ │ ├── form-schema.ts
│ │ │ ├── [service-account-id]
│ │ │ │ ├── page.tsx
│ │ │ │ ├── ButtonGroup.tsx
│ │ │ │ └── breadcrumb.ts
│ │ │ ├── Fallback.tsx
│ │ │ └── ButtonGroup.tsx
│ │ └── repositories
│ │ │ └── page.tsx
│ ├── stacks
│ │ ├── create
│ │ │ ├── existing-infrastructure
│ │ │ │ └── steps
│ │ │ │ │ ├── provider
│ │ │ │ │ └── schema.ts
│ │ │ │ │ ├── artifact_store
│ │ │ │ │ ├── schema.ts
│ │ │ │ │ └── Header.tsx
│ │ │ │ │ ├── orchestrator
│ │ │ │ │ ├── schema.ts
│ │ │ │ │ └── Header.tsx
│ │ │ │ │ ├── container_registry
│ │ │ │ │ ├── schema.ts
│ │ │ │ │ └── Header.tsx
│ │ │ │ │ └── connect
│ │ │ │ │ ├── schema.ts
│ │ │ │ │ └── AuthenticationMethod.tsx
│ │ │ ├── terraform
│ │ │ │ ├── steps
│ │ │ │ │ └── provider
│ │ │ │ │ │ └── schema.ts
│ │ │ │ ├── TerraformWizard.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── page.tsx
│ │ │ ├── components
│ │ │ │ ├── sharedSchema.ts
│ │ │ │ ├── WizardFooter.tsx
│ │ │ │ └── CancelButton.tsx
│ │ │ ├── LocalOverlay.tsx
│ │ │ ├── new-infrastructure
│ │ │ │ └── Steps
│ │ │ │ │ ├── schemas.ts
│ │ │ │ │ └── Deploy
│ │ │ │ │ └── index.tsx
│ │ │ ├── breadcrumb.ts
│ │ │ └── layout.tsx
│ │ ├── DialogItems.tsx
│ │ ├── page.tsx
│ │ ├── service.ts
│ │ └── DeleteStackModal.tsx
│ ├── snapshots
│ │ ├── [snapshotId]
│ │ │ ├── _layout
│ │ │ │ └── header
│ │ │ │ │ ├── use-active-tab.ts
│ │ │ │ │ └── index.tsx
│ │ │ ├── runs
│ │ │ │ └── page.tsx
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
│ │ ├── create
│ │ │ ├── page.tsx
│ │ │ └── breadcrumbs.ts
│ │ └── page.tsx
│ ├── upgrade
│ │ ├── components
│ │ │ └── form
│ │ │ │ ├── schema.ts
│ │ │ │ ├── index.tsx
│ │ │ │ └── Wrapper.tsx
│ │ └── page.tsx
│ ├── pipelines
│ │ ├── [pipelineId]
│ │ │ ├── _layout
│ │ │ │ ├── use-active-tab.ts
│ │ │ │ └── header.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── deployments
│ │ │ │ ├── page.tsx
│ │ │ │ └── columns.tsx
│ │ │ ├── runs
│ │ │ │ ├── page.tsx
│ │ │ │ ├── breadcrumb.ts
│ │ │ │ └── service.ts
│ │ │ └── snapshots
│ │ │ │ ├── page.tsx
│ │ │ │ └── use-breadcrumbs.ts
│ │ ├── page.tsx
│ │ └── _components
│ │ │ └── ButtonGroup.tsx
│ ├── survey
│ │ ├── page.tsx
│ │ └── loader.ts
│ ├── activate-server
│ │ └── page.tsx
│ ├── activate-user
│ │ ├── page.tsx
│ │ └── PasswordStep.tsx
│ ├── projects
│ │ └── page.tsx
│ ├── components
│ │ ├── [componentId]
│ │ │ ├── edit
│ │ │ │ └── page.tsx
│ │ │ └── layout.tsx
│ │ ├── page.tsx
│ │ ├── create
│ │ │ ├── page.tsx
│ │ │ ├── header.tsx
│ │ │ └── breadcrumb.ts
│ │ └── service.ts
│ ├── onboarding
│ │ └── Setup
│ │ │ └── set-project.tsx
│ ├── login
│ │ └── page.tsx
│ ├── devices
│ │ └── verify
│ │ │ └── Success.tsx
│ ├── overview
│ │ └── page.tsx
│ └── models
│ │ └── Fragments.tsx
├── assets
│ ├── images
│ │ ├── acp.webp
│ │ ├── mcp.webp
│ │ ├── cursor.webp
│ │ ├── vs-code.webp
│ │ ├── gradient_bg.webp
│ │ ├── upgrade-form.webp
│ │ ├── portraits
│ │ │ ├── adam.webp
│ │ │ ├── alex.webp
│ │ │ ├── baris.webp
│ │ │ ├── hamza.webp
│ │ │ └── stefan.webp
│ │ └── product-tour
│ │ │ ├── tour-cover.webp
│ │ │ └── settings_preview.webp
│ ├── illustrations
│ │ └── connectivity.webp
│ └── icons
│ │ ├── collapse-text.svg
│ │ ├── minus.svg
│ │ ├── plus.svg
│ │ ├── slash-divider.svg
│ │ ├── services
│ │ └── terraform.svg
│ │ ├── long-arrow-right.svg
│ │ ├── check.svg
│ │ ├── chevron-down.svg
│ │ ├── dots-horizontal.svg
│ │ ├── slash-circle.svg
│ │ ├── arrow-right.svg
│ │ ├── arrow-left.svg
│ │ ├── chevron-left.svg
│ │ ├── chevron-right.svg
│ │ ├── spinner.svg
│ │ ├── clock.svg
│ │ ├── info.svg
│ │ ├── expand.svg
│ │ ├── side-collapse.svg
│ │ ├── close.svg
│ │ ├── alert-circle.svg
│ │ ├── collapse.svg
│ │ ├── side-expand.svg
│ │ ├── cloud.svg
│ │ ├── search.svg
│ │ ├── alert-triangle.svg
│ │ ├── tick-circle.svg
│ │ ├── stopped.svg
│ │ ├── expand-full.svg
│ │ ├── refresh.svg
│ │ ├── dots-circle.svg
│ │ ├── check-circle.svg
│ │ ├── chevron-left-double.svg
│ │ └── chevron-right-double.svg
├── vite-env.d.ts
├── components
│ ├── not-available.tsx
│ ├── stack-components
│ │ ├── create-component
│ │ │ └── schema.ts
│ │ ├── component-detail
│ │ │ ├── hooks.ts
│ │ │ └── service.ts
│ │ └── ComponentBadge.tsx
│ ├── breadcrumbs
│ │ └── types.ts
│ ├── admin-badge.tsx
│ ├── pipeline-snapshots
│ │ ├── create
│ │ │ ├── _form-components
│ │ │ │ ├── create-snapshot-form-header.tsx
│ │ │ │ └── create-snapshot-form-footer.tsx
│ │ │ └── form-schema.ts
│ │ └── list
│ │ │ ├── button-group.tsx
│ │ │ └── use-queryparams.ts
│ ├── bot-badge.tsx
│ ├── deployments
│ │ ├── playground
│ │ │ ├── preview-visualization
│ │ │ │ ├── services
│ │ │ │ │ └── string-threshold.ts
│ │ │ │ └── components
│ │ │ │ │ ├── expand-collapse-icon.tsx
│ │ │ │ │ └── boolean-value.tsx
│ │ │ └── playground-view-switcher-button.tsx
│ │ ├── list
│ │ │ ├── button-group.tsx
│ │ │ └── use-deployment-queryparams.ts
│ │ └── selector-context.ts
│ ├── buttons
│ │ └── update-button-content.tsx
│ ├── dag-visualizer
│ │ ├── layout
│ │ │ └── status.ts
│ │ └── BaseNode.tsx
│ ├── announcements
│ │ ├── use-last-seen.ts
│ │ ├── announcement-label.tsx
│ │ ├── announcement-list
│ │ │ ├── get-filtered-items.ts
│ │ │ └── announcement-dialog-search-bar.tsx
│ │ ├── announcement-highlight
│ │ │ └── page-indicator.tsx
│ │ ├── announcement-image.tsx
│ │ ├── announcement-video.tsx
│ │ └── announcement-indicator.tsx
│ ├── runs
│ │ ├── detail-tabs
│ │ │ ├── LogTab
│ │ │ │ └── boundary.tsx
│ │ │ ├── service.ts
│ │ │ └── Overview
│ │ │ │ └── index.tsx
│ │ ├── run-name.tsx
│ │ ├── run-index-prefix.tsx
│ │ ├── runs.ts
│ │ ├── stop-group
│ │ │ └── use-stop-run.tsx
│ │ └── refresh-group
│ │ │ └── index.tsx
│ ├── artifacts
│ │ ├── ImageVisualization.tsx
│ │ ├── MarkdownVisualization.tsx
│ │ └── JsonVisualization.tsx
│ ├── fallback
│ │ └── icon.tsx
│ ├── Tick.tsx
│ ├── PageHeader.tsx
│ ├── fallback-pages
│ │ └── Commands.tsx
│ ├── tab-icon.tsx
│ ├── triggers
│ │ └── schedule-tag.tsx
│ ├── EmptyState.tsx
│ ├── stacks
│ │ └── info
│ │ │ ├── stack-info-full.tsx
│ │ │ ├── sort-component.ts
│ │ │ └── stack-info-component-list.tsx
│ ├── collapsible-chevron.tsx
│ ├── logs
│ │ ├── use-loglevel-filter.ts
│ │ ├── empty-state-logs.tsx
│ │ └── use-logpage-input.ts
│ ├── onboarding
│ │ └── SkippedStep.tsx
│ ├── tables
│ │ └── action-cell.tsx
│ ├── form
│ │ └── common.tsx
│ ├── tabs
│ │ └── scrolling-tabs-list.tsx
│ ├── NumberBox.tsx
│ ├── service-connectors
│ │ └── connector-tag.tsx
│ ├── CodeHighlighter.tsx
│ ├── ProviderIcon.tsx
│ ├── Error.tsx
│ ├── NavLink.tsx
│ └── survey
│ │ └── StepDisplay.tsx
├── hooks
│ └── use-route-segment.tsx
├── main.tsx
├── layouts
│ ├── project-tabs
│ │ ├── layout.tsx
│ │ ├── name-section.tsx
│ │ └── header.tsx
│ ├── non-project-scoped
│ │ ├── header.tsx
│ │ ├── layout.tsx
│ │ └── server-members.tsx
│ ├── GradientLayout.tsx
│ ├── settings
│ │ ├── project-settings
│ │ │ └── project-display.tsx
│ │ └── Menu.tsx
│ ├── AuthenticatedLayout
│ │ ├── use-announcement.ts
│ │ ├── whats-new-button.tsx
│ │ └── back-button.tsx
│ ├── connectors-detail
│ │ ├── layout.tsx
│ │ └── breadcrumbs.ts
│ └── RootLayout.tsx
├── data
│ ├── fetch.ts
│ ├── projects
│ │ └── index.ts
│ ├── secrets
│ │ └── index.ts
│ ├── deployments
│ │ └── index.ts
│ ├── analytics
│ │ └── event.ts
│ ├── pipeline-snapshots
│ │ └── index.ts
│ ├── github
│ │ ├── pipeline-summary.ts
│ │ └── pipeline-content.ts
│ └── pipelines
│ │ └── pipeline-detail.ts
├── error-boundaries
│ ├── PageBoundary.tsx
│ └── RootBoundary.tsx
├── router
│ └── queryclient.ts
└── monaco-setup.ts
├── .husky
└── pre-commit
├── .env.example
├── public
└── favicon.ico
├── postcss.config.js
├── .prettierrc
├── tsconfig.node.json
├── .github
├── teams.yml
└── workflows
│ ├── unit-tests.yml
│ ├── playwright.yml
│ └── check-links.yml
├── index.html
├── .prettierignore
├── .eslintignore
├── .gitignore
├── e2e-tests
└── example.spec.ts
├── tsconfig.json
├── .eslintrc.cjs
├── scripts
└── check-links.sh
└── tailwind.config.js
/src/types/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/contents/secrets.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/contents/stack.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | npx lint-staged
2 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | VITE_API_BASE_URL=
2 | VITE_FRONTEND_VERSION=
3 | VITE_REO_KEY=
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/lib/analytics.ts:
--------------------------------------------------------------------------------
1 | export const analyticsServerUrl = "https://analytics.zenml.io/batch";
2 |
--------------------------------------------------------------------------------
/src/app/runs/[id]/pipeline-viz/types.ts:
--------------------------------------------------------------------------------
1 | export type PiplineRunVisualizationView = "dag" | "timeline";
2 |
--------------------------------------------------------------------------------
/src/assets/images/acp.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/src/assets/images/acp.webp
--------------------------------------------------------------------------------
/src/assets/images/mcp.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/src/assets/images/mcp.webp
--------------------------------------------------------------------------------
/src/lib/environment.ts:
--------------------------------------------------------------------------------
1 | export const IS_SAFARI = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
2 |
--------------------------------------------------------------------------------
/src/types/code-highlighting.ts:
--------------------------------------------------------------------------------
1 | export type CodeLanguage = "python" | "bash" | "ts" | "dockerfile" | "json";
2 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {}
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/src/assets/images/cursor.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/src/assets/images/cursor.webp
--------------------------------------------------------------------------------
/src/assets/images/vs-code.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/src/assets/images/vs-code.webp
--------------------------------------------------------------------------------
/src/assets/images/gradient_bg.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/src/assets/images/gradient_bg.webp
--------------------------------------------------------------------------------
/src/lib/common.ts:
--------------------------------------------------------------------------------
1 | export function sleep(ms: number) {
2 | return new Promise((resolve) => setTimeout(resolve, ms));
3 | }
4 |
--------------------------------------------------------------------------------
/src/assets/images/upgrade-form.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/src/assets/images/upgrade-form.webp
--------------------------------------------------------------------------------
/src/app/deployments/[deploymentId]/playground/_components/outputs/types.ts:
--------------------------------------------------------------------------------
1 | export type PlaygroundOutputsView = "preview" | "json";
2 |
--------------------------------------------------------------------------------
/src/assets/images/portraits/adam.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/src/assets/images/portraits/adam.webp
--------------------------------------------------------------------------------
/src/assets/images/portraits/alex.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/src/assets/images/portraits/alex.webp
--------------------------------------------------------------------------------
/src/assets/images/portraits/baris.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/src/assets/images/portraits/baris.webp
--------------------------------------------------------------------------------
/src/assets/images/portraits/hamza.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/src/assets/images/portraits/hamza.webp
--------------------------------------------------------------------------------
/src/assets/images/portraits/stefan.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/src/assets/images/portraits/stefan.webp
--------------------------------------------------------------------------------
/src/assets/illustrations/connectivity.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/src/assets/illustrations/connectivity.webp
--------------------------------------------------------------------------------
/src/assets/images/product-tour/tour-cover.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/src/assets/images/product-tour/tour-cover.webp
--------------------------------------------------------------------------------
/src/components/not-available.tsx:
--------------------------------------------------------------------------------
1 | export function NotAvailable() {
2 | return
Not available
;
3 | }
4 |
--------------------------------------------------------------------------------
/src/assets/images/product-tour/settings_preview.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zenml-io/zenml-dashboard/HEAD/src/assets/images/product-tour/settings_preview.webp
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": true,
3 | "singleQuote": false,
4 | "trailingComma": "none",
5 | "printWidth": 100,
6 | "plugins": ["prettier-plugin-tailwindcss"]
7 | }
8 |
--------------------------------------------------------------------------------
/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/projects.ts:
--------------------------------------------------------------------------------
1 | import { components } from "./core";
2 |
3 | export type Project = components["schemas"]["ProjectResponse"];
4 | export type ProjectStatistics = components["schemas"]["ProjectStatistics"];
5 |
--------------------------------------------------------------------------------
/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/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/lib/json-faker.ts:
--------------------------------------------------------------------------------
1 | import { JSONSchemaFaker } from "json-schema-faker";
2 |
3 | JSONSchemaFaker.option({
4 | alwaysFakeOptionals: true,
5 | useDefaultValue: true,
6 | random: () => 0
7 | });
8 |
9 | export { JSONSchemaFaker };
10 |
--------------------------------------------------------------------------------
/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/components/breadcrumbs/types.ts:
--------------------------------------------------------------------------------
1 | import { ReactNode } from "react";
2 |
3 | export type BreadcrumbSegment = {
4 | label: ReactNode;
5 | href: string;
6 | disabled?: boolean;
7 | };
8 |
9 | export type Breadcrumbs = BreadcrumbSegment[];
10 |
--------------------------------------------------------------------------------
/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/assets/icons/collapse-text.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/components/admin-badge.tsx:
--------------------------------------------------------------------------------
1 | import { Badge } from "@zenml-io/react-component-library/components/server";
2 |
3 | export function AdminBadge() {
4 | return (
5 |
6 | admin
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/pipeline-snapshots/create/_form-components/create-snapshot-form-header.tsx:
--------------------------------------------------------------------------------
1 | export function CreateSnapshotFormHeader() {
2 | return (
3 |
4 |
Create Snapshot
5 |
6 | );
7 | }
8 |
--------------------------------------------------------------------------------
/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/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/components/bot-badge.tsx:
--------------------------------------------------------------------------------
1 | import { Badge } from "@zenml-io/react-component-library/components/server";
2 |
3 | export function BotBadge() {
4 | return (
5 |
6 | bot
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/deployments/playground/preview-visualization/services/string-threshold.ts:
--------------------------------------------------------------------------------
1 | import { COLLAPSE_STRING_THRESHOLD_CHARACTERS } from "@/lib/constants";
2 |
3 | export function shouldCollapseString(value: string) {
4 | return value.length > COLLAPSE_STRING_THRESHOLD_CHARACTERS;
5 | }
6 |
--------------------------------------------------------------------------------
/src/assets/icons/minus.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/src/app/snapshots/[snapshotId]/_layout/header/use-active-tab.ts:
--------------------------------------------------------------------------------
1 | import { useRouteSegment } from "@/hooks/use-route-segment";
2 |
3 | type Tabs = "overview" | "runs";
4 |
5 | export function useActiveTab() {
6 | const activeTab = (useRouteSegment(4) || "overview") as Tabs;
7 | return activeTab;
8 | }
9 |
--------------------------------------------------------------------------------
/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/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/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/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/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/app/deployments/[deploymentId]/_layout/header/not-available-tag.tsx:
--------------------------------------------------------------------------------
1 | import { Tag } from "@zenml-io/react-component-library";
2 |
3 | export function NotAvailableTag() {
4 | return (
5 |
6 | Not available
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/app/pipelines/[pipelineId]/_layout/use-active-tab.ts:
--------------------------------------------------------------------------------
1 | import { useRouteSegment } from "@/hooks/use-route-segment";
2 |
3 | type Tabs = "runs" | "snapshots" | "deployments";
4 |
5 | export function useActiveTab() {
6 | const activeTab = (useRouteSegment(4) || "runs") as Tabs;
7 | return activeTab;
8 | }
9 |
--------------------------------------------------------------------------------
/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/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/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/app/deployments/[deploymentId]/_layout/header/use-active-tab.ts:
--------------------------------------------------------------------------------
1 | import { useRouteSegment } from "@/hooks/use-route-segment";
2 |
3 | type Tabs = "overview" | "runs" | "playground";
4 |
5 | export function useActiveTab() {
6 | const activeTab = (useRouteSegment(4) || "overview") as Tabs;
7 | return activeTab;
8 | }
9 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/.github/teams.yml:
--------------------------------------------------------------------------------
1 | ---
2 | internal:
3 | - '@htahir1'
4 | - '@hamza-zenml'
5 | - '@bcdurak'
6 | - '@baris-zenml'
7 | - '@michael-zenml'
8 | - '@schustmi'
9 | - '@strickvl'
10 | - '@alex-zenml'
11 | - '@alexej-zenml'
12 | - '@AlexejPenner'
13 | - '@stefannica'
14 | - '@safoinme'
15 | - '@Cahllagerfeld'
16 |
--------------------------------------------------------------------------------
/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/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/lib/name.ts:
--------------------------------------------------------------------------------
1 | import { uniqueNamesGenerator, colors, animals } from "unique-names-generator";
2 |
3 | const blockedAnimals = ["booby", "swallow"];
4 |
5 | export function generateUniqueName() {
6 | return uniqueNamesGenerator({
7 | dictionaries: [colors, animals.filter((el) => !blockedAnimals.includes(el))]
8 | });
9 | }
10 |
--------------------------------------------------------------------------------
/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/types/steps.ts:
--------------------------------------------------------------------------------
1 | import { components, operations } from "./core";
2 |
3 | export type Step = components["schemas"]["StepRunResponse"];
4 |
5 | export type StepDict = Record;
6 |
7 | export type StepLogsQueryParams = NonNullable<
8 | operations["get_step_logs_api_v1_steps__step_id__logs_get"]["parameters"]["query"]
9 | >;
10 |
--------------------------------------------------------------------------------
/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/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/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/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/components/announcements/use-last-seen.ts:
--------------------------------------------------------------------------------
1 | import { useSyncExternalStore } from "react";
2 | import { AnnouncementKey, announcementStore } from "./persist-announcement";
3 |
4 | export function useAnnouncementLastSeen(key: AnnouncementKey) {
5 | return useSyncExternalStore(announcementStore.subscribe(key), () =>
6 | announcementStore.getSnapshot(key)
7 | );
8 | }
9 |
--------------------------------------------------------------------------------
/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/app/pipelines/page.tsx:
--------------------------------------------------------------------------------
1 | import { PipelineSelectorContextProvider } from "./_components/PipelineSelectorContext";
2 | import { PipelinesBody } from "./_components/PipelinesBody";
3 |
4 | export default function PipelinesPage() {
5 | return (
6 |
7 |
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/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/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/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/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/app/pipelines/[pipelineId]/layout.tsx:
--------------------------------------------------------------------------------
1 | import { Outlet } from "react-router-dom";
2 | import { PipelineDetailHeader } from "./_layout/header";
3 |
4 | export default function PipelineDetailLayout() {
5 | return (
6 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/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/components/runs/detail-tabs/LogTab/boundary.tsx:
--------------------------------------------------------------------------------
1 | import { EmptyStateLogs } from "@/components/logs/empty-state-logs";
2 | import { FallbackProps } from "react-error-boundary";
3 |
4 | export function LogsTabBoundary({ error }: FallbackProps) {
5 | if (!(error instanceof Error)) throw error;
6 | return ;
7 | }
8 |
--------------------------------------------------------------------------------
/src/types/onboarding.ts:
--------------------------------------------------------------------------------
1 | export type OnboardingChecklistItemName =
2 | | "device_verified"
3 | | "pipeline_run"
4 | | "pipeline_deployed"
5 | | "oss_onboarding_completed";
6 |
7 | export type OnboardingResponse = OnboardingChecklistItemName[];
8 |
9 | export type OnboardingStep = {
10 | completed: boolean;
11 | active: boolean;
12 | hasDownstreamStep: boolean;
13 | };
14 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ZenML Dashboard
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/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/components/pipeline-snapshots/create/form-schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const createPipelineSnapshotFormSchema = z.object({
4 | name: z.string().min(1, "Name is required"),
5 | pipeline: z.string().uuid("Invalid pipeline ID"),
6 | run: z.string().min(1, "Run is required")
7 | });
8 |
9 | export type CreatePipelineSnapshotFormSchema = z.infer;
10 |
--------------------------------------------------------------------------------
/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/snapshots/create/page.tsx:
--------------------------------------------------------------------------------
1 | import { CreateSnapshotForm } from "@/components/pipeline-snapshots/create/form";
2 | import { useCreateSnapshotBreadcrumbs } from "./breadcrumbs";
3 |
4 | export default function CreateSnapshotPage() {
5 | useCreateSnapshotBreadcrumbs();
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/src/assets/icons/plus.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/slash-divider.svg:
--------------------------------------------------------------------------------
1 |
2 |
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/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/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/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/app/deployments/[deploymentId]/playground/_components/outputs/empty-state.tsx:
--------------------------------------------------------------------------------
1 | import { PlaygroundEmptyState } from "../error";
2 |
3 | export function PlaygroundOutputsEmptyState() {
4 | return (
5 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/announcements/announcement-label.tsx:
--------------------------------------------------------------------------------
1 | import { Tag } from "@zenml-io/react-component-library";
2 | import { getLabelColor, getLabelDisplayName, LabelValue } from "./label-utils";
3 |
4 | export function AnnouncementLabel({ label }: { label: LabelValue }) {
5 | return (
6 |
7 | {getLabelDisplayName(label)}
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/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/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/snapshots/[snapshotId]/runs/page.tsx:
--------------------------------------------------------------------------------
1 | import { RunsSelectorContextProvider } from "@/app/runs/RunsSelectorContext";
2 | import { SnapshotDetailRunsContent } from "./page.content";
3 |
4 | export default function SnapshotDetailRunsPage() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | *.log
3 | npm-debug.log*
4 | yarn-debug.log*
5 | yarn-error.log*
6 | pnpm-debug.log*
7 | lerna-debug.log*
8 |
9 | node_modules
10 | dist
11 | dist-ssr
12 | *.local
13 |
14 | # Editor directories and files
15 | .vscode/*
16 | !.vscode/extensions.json
17 | .idea
18 | .DS_Store
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
25 | .env*
26 | !.env.example
27 |
28 | pnpm-lock.yaml
29 | yarn.lock
30 |
--------------------------------------------------------------------------------
/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/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 | export type PipelineList = components["schemas"]["Page_PipelineResponse_"];
6 |
7 | export type PipelineListParams = NonNullable<
8 | operations["list_pipelines_api_v1_pipelines_get"]["parameters"]["query"]
9 | >;
10 |
--------------------------------------------------------------------------------
/src/app/deployments/[deploymentId]/runs/page.tsx:
--------------------------------------------------------------------------------
1 | import { RunsSelectorContextProvider } from "@/app/runs/RunsSelectorContext";
2 | import { DeploymentRunsContent } from "./page.content";
3 |
4 | export default function DeploymentRunsPage() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/snapshots/page.tsx:
--------------------------------------------------------------------------------
1 | import { SnapshotSelectorContextProvider } from "@/components/pipeline-snapshots/selector-context";
2 | import { GlobalSnapshotsContent } from "./page.content";
3 |
4 | export default function GlobalSnapshotsPage() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/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/app/deployments/page.tsx:
--------------------------------------------------------------------------------
1 | import { DeploymentSelectorContextProvider } from "@/components/deployments/selector-context";
2 | import { GlobalDeploymentsContent } from "./page.content";
3 |
4 | export default function GlobalDeploymentList() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/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/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/assets/icons/services/terraform.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/deployments/[deploymentId]/playground/_components/outputs/footer.tsx:
--------------------------------------------------------------------------------
1 | import { DeleteOutputRun } from "./delete-alert";
2 |
3 | type Props = {
4 | clearOutputs: () => void;
5 | runId?: string;
6 | };
7 |
8 | export function PlaygroundOutputsFooter({ clearOutputs, runId }: Props) {
9 | return (
10 |
11 | {runId && }
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/snapshots/[snapshotId]/layout.tsx:
--------------------------------------------------------------------------------
1 | import { Outlet, useParams } from "react-router-dom";
2 | import { SnapshotDetailHeader } from "./_layout/header";
3 |
4 | export default function SnapshotDetailLayout() {
5 | const { snapshotId } = useParams() as { snapshotId: string };
6 | return (
7 |
8 |
9 |
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/deployments/[deploymentId]/playground/_components/loader.tsx:
--------------------------------------------------------------------------------
1 | import { Spinner } from "@zenml-io/react-component-library";
2 |
3 | type Props = {
4 | subtitle?: string;
5 | };
6 |
7 | export function PlaygroundLoader({ subtitle }: Props) {
8 | return (
9 |
10 |
11 | {subtitle &&
{subtitle}
}
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/pipelines/[pipelineId]/deployments/page.tsx:
--------------------------------------------------------------------------------
1 | import { DeploymentSelectorContextProvider } from "@/components/deployments/selector-context";
2 | import { PipelineDeploymentsContent } from "./page.content";
3 |
4 | export default function PipelineDetailDeploymentsPage() {
5 | return (
6 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/app/settings/mcp/types.ts:
--------------------------------------------------------------------------------
1 | export interface InstallationMethod {
2 | title: string;
3 | type: "automatic" | "docker" | "uv" | "mcpb" | "cli";
4 | hasDeepLink?: boolean;
5 | deepLinkUrl?: string;
6 | config?: string;
7 | bashCommand?: string;
8 | description?: string;
9 | steps: string[];
10 | note?: string;
11 | }
12 |
13 | export interface IDEConfig {
14 | name: string;
15 | value: string;
16 | methods: InstallationMethod[];
17 | troubleshooting?: string;
18 | }
19 |
--------------------------------------------------------------------------------
/src/assets/icons/long-arrow-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/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/assets/icons/check.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/chevron-down.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/src/types/logs.ts:
--------------------------------------------------------------------------------
1 | import { operations, components } from "./core";
2 |
3 | export type LoggingLevel = components["schemas"]["LoggingLevels"];
4 |
5 | export type LogEntry = components["schemas"]["LogEntry"];
6 |
7 | export type LogResponse = LogEntry[];
8 |
9 | export type LogEntryInternal = LogEntry & {
10 | originalEntry: string;
11 | };
12 |
13 | export type RunLogsQueryParams = NonNullable<
14 | operations["run_logs_api_v1_runs__run_id__logs_get"]["parameters"]["query"]
15 | >;
16 |
--------------------------------------------------------------------------------
/src/app/pipelines/[pipelineId]/_layout/header.tsx:
--------------------------------------------------------------------------------
1 | import { PipelineDetailTabs } from "./tabs";
2 | import { PipelineDetailNameSection } from "./name-section";
3 | export function PipelineDetailHeader() {
4 | return (
5 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/runs/run-name.tsx:
--------------------------------------------------------------------------------
1 | import { RunIndexPrefix } from "./run-index-prefix";
2 |
3 | type Props = {
4 | name: string;
5 | index?: number | null;
6 | };
7 |
8 | /**
9 | * Displays a run name with an optional index prefix.
10 | * Renders as a span so the parent can control the wrapper element (h1, h2, p, etc.).
11 | */
12 | export function RunName({ name, index }: Props) {
13 | return (
14 |
15 |
16 | {name}
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/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/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/types/pipeline-deployments.ts:
--------------------------------------------------------------------------------
1 | import { components } from "./core";
2 |
3 | export type StepOutput = components["schemas"]["Step-Output"];
4 | export type StepOutputInput = {
5 | step_name: string;
6 | output_name: string;
7 | };
8 |
9 | export type ExternalArtifactConfig = components["schemas"]["ExternalArtifactConfiguration"];
10 | export type ModelVersionLazyLoader = components["schemas"]["ModelVersionDataLazyLoader"];
11 | export type ClientLazyLoader = components["schemas"]["ClientLazyLoader"];
12 |
--------------------------------------------------------------------------------
/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/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/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/app/pipelines/[pipelineId]/runs/page.tsx:
--------------------------------------------------------------------------------
1 | import { RunsSelectorContextProvider } from "../../../runs/RunsSelectorContext";
2 | import { usePipelineDetailBreadcrumbs } from "./breadcrumb";
3 | import { PipelineRunsContent } from "./content";
4 |
5 | export default function PipelineDetailPage() {
6 | usePipelineDetailBreadcrumbs();
7 |
8 | return (
9 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/src/types/deployments.ts:
--------------------------------------------------------------------------------
1 | // List
2 |
3 | import { operations, components } from "./core";
4 |
5 | export type DeploymentsList = components["schemas"]["Page_DeploymentResponse_"];
6 | export type DeploymentsListQueryParams = NonNullable<
7 | operations["list_deployments_api_v1_deployments_get"]["parameters"]["query"]
8 | >;
9 |
10 | // Detail
11 |
12 | export type Deployment = components["schemas"]["DeploymentResponse"];
13 |
14 | // Common
15 | export type DeploymentStatus = components["schemas"]["DeploymentStatus"];
16 |
--------------------------------------------------------------------------------
/src/app/deployments/[deploymentId]/layout.tsx:
--------------------------------------------------------------------------------
1 | import { Outlet, useParams } from "react-router-dom";
2 | import { DeploymentDetailHeader } from "./_layout/header";
3 |
4 | export default function DeploymentDetailLayout() {
5 | const { deploymentId } = useParams() as { deploymentId: string };
6 | return (
7 |
8 |
9 |
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/runs/[id]/pipeline-viz/timeline/hooks/use-timelie-item.ts:
--------------------------------------------------------------------------------
1 | import { useSheetContext } from "@/components/dag-visualizer/sheet-context";
2 |
3 | export function useTimelineItem(stepId: string | undefined) {
4 | const { openStepSheet, sheetState } = useSheetContext();
5 |
6 | const isSelected = sheetState.lastContent?.id === stepId && sheetState.isOpen;
7 |
8 | function handleClick() {
9 | if (!stepId) return;
10 | openStepSheet(stepId);
11 | }
12 |
13 | return {
14 | isSelected,
15 | handleClick
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/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/app/stacks/DialogItems.tsx:
--------------------------------------------------------------------------------
1 | import { UpdateStackDialogContent } from "@/components/stacks/update-stack-dialog";
2 | import { Dialog } from "@zenml-io/react-component-library";
3 |
4 | type Props = {
5 | open: boolean;
6 | setOpen: (open: boolean) => void;
7 | name: string;
8 | };
9 |
10 | export function UpdateStackDialog({ name, open, setOpen }: Props) {
11 | return (
12 |
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/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/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/assets/icons/dots-horizontal.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/runs/run-index-prefix.tsx:
--------------------------------------------------------------------------------
1 | import { formatRunIndex } from "./runs";
2 |
3 | type Props = {
4 | index?: number | null;
5 | };
6 |
7 | /**
8 | * Displays a run index with leading zeros (4 digits).
9 | * e.g., 1 => #0001-, 42 => #0042-, 1234 => #1234-
10 | *
11 | * Only renders if index is provided and is a valid number.
12 | */
13 | export function RunIndexPrefix({ index }: Props) {
14 | if (index == null) return null;
15 |
16 | return #{formatRunIndex(index)}- ;
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/runs/[id]/pipeline-viz/timeline/services/timeline-empty-state-message.ts:
--------------------------------------------------------------------------------
1 | import { TimelineItem } from "@/lib/timeline/types";
2 |
3 | export function getEmptyStateMessage(timelineItems: TimelineItem[], search: string) {
4 | if (timelineItems.length === 0) {
5 | return {
6 | title: "No steps available",
7 | description: "This pipeline run doesn't contain any steps to display."
8 | };
9 | }
10 |
11 | return {
12 | title: "No steps found",
13 | description: `No steps found that match the search "${search}".`
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/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/snapshots/[snapshotId]/_layout/header/index.tsx:
--------------------------------------------------------------------------------
1 | import { SnapshotDetailInfo } from "./info";
2 | import { SnapshotDetailTabs } from "./tabs";
3 |
4 | export function SnapshotDetailHeader({ snapshotId }: { snapshotId: string }) {
5 | return (
6 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/assets/icons/slash-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/arrow-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/components/tab-icon.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@zenml-io/react-component-library/utilities";
2 | import { ComponentProps, ComponentType } from "react";
3 |
4 | type TabIconProps = {
5 | icon: ComponentType>;
6 | className?: string;
7 | };
8 |
9 | export function TabIcon({ icon: Icon, className = "" }: TabIconProps) {
10 | return (
11 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/triggers/schedule-tag.tsx:
--------------------------------------------------------------------------------
1 | import Clock from "@/assets/icons/clock.svg?react";
2 | import { Tag } from "@zenml-io/react-component-library";
3 | import { ComponentProps } from "react";
4 |
5 | type Props = ComponentProps;
6 |
7 | export function ScheduleTag({ ...props }: Props) {
8 | return (
9 |
10 |
11 | Schedule
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/assets/icons/arrow-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/chevron-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/announcements/announcement-list/get-filtered-items.ts:
--------------------------------------------------------------------------------
1 | import { Announcement } from "@/types/announcements";
2 |
3 | type getFilteredAnnouncementsParams = {
4 | announcements: Announcement[];
5 | searchQuery: string;
6 | };
7 |
8 | export function getFilteredAnnouncements({
9 | announcements,
10 | searchQuery
11 | }: getFilteredAnnouncementsParams) {
12 | const filtered = announcements.filter((announcement) => {
13 | return announcement.title.toLowerCase().includes(searchQuery.toLowerCase());
14 | });
15 | return filtered;
16 | }
17 |
--------------------------------------------------------------------------------
/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/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/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/snapshots/[snapshotId]/page.tsx:
--------------------------------------------------------------------------------
1 | import { SnapshotCodeCollapsible } from "./_components/code";
2 | import { SnapshotDetails } from "./_components/snapshot-detail";
3 | import { SnapshotStack } from "./_components/snapshot-stack";
4 |
5 | export default function SnapshotDetailPage() {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/assets/icons/chevron-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/runs/detail-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", "logs", "code"])
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/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/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/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/components/EmptyState.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@zenml-io/react-component-library";
2 | import { PropsWithChildren, ReactNode } from "react";
3 |
4 | type Props = {
5 | icon?: ReactNode;
6 | className?: string;
7 | };
8 |
9 | export function EmptyState({ children, icon, className }: PropsWithChildren) {
10 | return (
11 |
17 | {icon}
18 | {children}
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/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/components/stacks/info/stack-info-full.tsx:
--------------------------------------------------------------------------------
1 | import { Stack } from "@/types/stack";
2 | import { StackInfoCollapsible } from "./stack-info-collapsible";
3 | import { StackInfoComponentList } from "./stack-info-component-list";
4 |
5 | type Props = {
6 | stack: Stack;
7 | objectConfig: Record;
8 | };
9 |
10 | export function StackInfoFull({ stack, objectConfig }: Props) {
11 | return (
12 |
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/src/types/pipeline-snapshots.ts:
--------------------------------------------------------------------------------
1 | import { components, operations } from "./core";
2 |
3 | // Detail
4 | export type PipelineSnapshot = components["schemas"]["PipelineSnapshotResponse"];
5 |
6 | // List
7 |
8 | export type PipelineSnapshotList = components["schemas"]["Page_PipelineSnapshotResponse_"];
9 |
10 | export type PipelineSnapshotListQueryParams = NonNullable<
11 | operations["list_pipeline_snapshots_api_v1_pipeline_snapshots_get"]["parameters"]["query"]
12 | >;
13 |
14 | // Update
15 | export type PipelineSnapshotUpdate = components["schemas"]["PipelineSnapshotUpdate"];
16 |
--------------------------------------------------------------------------------
/src/app/deployments/[deploymentId]/_layout/header/index.tsx:
--------------------------------------------------------------------------------
1 | import { DeploymentDetailHeaderInfo } from "./info";
2 | import { DeploymentDetailTabs } from "./tabs";
3 |
4 | export function DeploymentDetailHeader({ deploymentId }: { deploymentId: string }) {
5 | return (
6 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/app/pipelines/[pipelineId]/snapshots/page.tsx:
--------------------------------------------------------------------------------
1 | import { SnapshotSelectorContextProvider } from "@/components/pipeline-snapshots/selector-context";
2 | import { PipelineSnapshotsContent } from "./page.content";
3 | import { usePipelineDetailSnapshotsBreadcrumbs } from "./use-breadcrumbs";
4 |
5 | export default function PipelineDetailPage() {
6 | usePipelineDetailSnapshotsBreadcrumbs();
7 |
8 | return (
9 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/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/components/collapsible-chevron.tsx:
--------------------------------------------------------------------------------
1 | import ChevronDown from "@/assets/icons/chevron-down.svg?react";
2 | import { cn } from "@zenml-io/react-component-library/utilities";
3 |
4 | type Props = {
5 | open: boolean;
6 | className?: string;
7 | };
8 |
9 | export function CollapsibleChevron({ open, className }: Props) {
10 | return (
11 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/src/layouts/GradientLayout.tsx:
--------------------------------------------------------------------------------
1 | import { Outlet } from "react-router-dom";
2 | import Gradient from "@/assets/images/gradient_bg.webp";
3 | import ZenML from "@/assets/icons/zenml.svg?react";
4 |
5 | export function GradientLayout() {
6 | return (
7 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/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/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/components/artifacts/MarkdownVisualization.tsx:
--------------------------------------------------------------------------------
1 | import { Codesnippet } from "../CodeSnippet";
2 | import { Markdown } from "../Markdown";
3 | import { Props } from "./Visualization";
4 |
5 | type MarkdownVisualizationProps = Props & {
6 | markdownMode?: "markdown" | "raw";
7 | };
8 |
9 | export default function MarkdownVisualization({
10 | content,
11 | markdownMode
12 | }: MarkdownVisualizationProps) {
13 | if (markdownMode === "raw") {
14 | return ;
15 | }
16 | return ;
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/logs/use-loglevel-filter.ts:
--------------------------------------------------------------------------------
1 | import { LogEntryInternal, LoggingLevel } from "@/types/logs";
2 | import { useMemo, useState } from "react";
3 |
4 | export function useLogLevelFilter(logs: LogEntryInternal[]) {
5 | const [selectedLogLevel, setSelectedLogLevel] = useState(20);
6 |
7 | const filteredLogs = useMemo(() => {
8 | return logs.filter((log) => {
9 | return log.level && log.level >= selectedLogLevel;
10 | });
11 | }, [logs, selectedLogLevel]);
12 |
13 | return {
14 | selectedLogLevel,
15 | filteredLogs,
16 | setSelectedLogLevel
17 | };
18 | }
19 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | *.log
3 | npm-debug.log*
4 | yarn-debug.log*
5 | yarn-error.log*
6 | pnpm-debug.log*
7 | lerna-debug.log*
8 |
9 | node_modules
10 | dist
11 | dist-ssr
12 | *.local
13 |
14 | # Editor directories and files
15 | .vscode/*
16 | !.vscode/extensions.json
17 | .idea
18 | .DS_Store
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 | /test-results/
25 | /playwright-report/
26 | /blob-report/
27 | /playwright/.cache/
28 |
29 | .env*
30 | !.env.example
31 |
32 | urls.txt
33 |
34 | # Claude Code
35 | .claude/*
36 | !.claude/output-styles/
37 |
38 | # X-Squad
39 | design/
40 |
--------------------------------------------------------------------------------
/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/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/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/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 | "deployer",
9 | "container_registry",
10 | "step_operator",
11 | "model_deployer",
12 | "feature_store",
13 | "experiment_tracker",
14 | "alerter",
15 | "annotator",
16 | "data_validator",
17 | "image_builder",
18 | "model_registry",
19 | "log_store"
20 | ] as const;
21 |
22 | export const COLLAPSE_STRING_THRESHOLD_CHARACTERS = 100; // characters
23 |
--------------------------------------------------------------------------------
/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/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/components/runs/detail-tabs/Overview/index.tsx:
--------------------------------------------------------------------------------
1 | import { AlertPanels } from "./AlertPanels";
2 | import { Details } from "./Details";
3 | import { OrchestratorCollapsible } from "./Orchestrator";
4 | import { ScheduleCollapsible } from "./schedule";
5 | type Props = {
6 | runId: string;
7 | };
8 |
9 | export function OverviewTab({ runId }: Props) {
10 | return (
11 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/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/assets/icons/spinner.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/clock.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/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/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/snapshots/create/breadcrumbs.ts:
--------------------------------------------------------------------------------
1 | import { snapshotBreadcrumb } from "@/components/breadcrumbs/library";
2 | import { useBreadcrumbsContext } from "@/layouts/AuthenticatedLayout/BreadcrumbsContext";
3 | import { routes } from "@/router/routes";
4 |
5 | import { useEffect } from "react";
6 |
7 | export function useCreateSnapshotBreadcrumbs() {
8 | const { setBreadcrumbs } = useBreadcrumbsContext();
9 |
10 | useEffect(() => {
11 | setBreadcrumbs([
12 | snapshotBreadcrumb,
13 | {
14 | label: "Create",
15 | href: routes.projects.snapshots.create
16 | }
17 | ]);
18 | }, [setBreadcrumbs]);
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/announcements/announcement-highlight/page-indicator.tsx:
--------------------------------------------------------------------------------
1 | type Props = {
2 | currentPage: number;
3 | totalPages: number;
4 | };
5 |
6 | export function AnnouncementHighlightPageIndicator({ currentPage, totalPages }: Props) {
7 | if (totalPages <= 1) return null;
8 |
9 | return (
10 |
11 | {Array.from({ length: totalPages }, (_, index) => (
12 |
16 | ))}
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/logs/empty-state-logs.tsx:
--------------------------------------------------------------------------------
1 | import Logs from "@/assets/icons/logs.svg?react";
2 | import { EmptyState } from "@/components/EmptyState";
3 |
4 | type Props = {
5 | title: string;
6 | subtitle: string;
7 | };
8 |
9 | export function EmptyStateLogs({ title, subtitle }: Props) {
10 | return (
11 | }>
12 |
13 |
{title}
14 |
{subtitle}
15 |
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/runs/runs.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Formats a run index as a 4-digit zero-padded string.
3 | * e.g., 1 => "0001", 42 => "0042", 1234 => "1234"
4 | */
5 | export function formatRunIndex(index: number): string {
6 | return String(index).padStart(4, "0");
7 | }
8 |
9 | /**
10 | * Formats a run name with an optional index prefix.
11 | * e.g., formatRunName("my-run", 1) => "#0001-my-run"
12 | * e.g., formatRunName("my-run") => "my-run"
13 | */
14 | export function formatRunName(name: string, index?: number | null): string {
15 | if (index == null) return name;
16 | return `#${formatRunIndex(index)}-${name}`;
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/tables/action-cell.tsx:
--------------------------------------------------------------------------------
1 | import { HTMLAttributes, ReactNode } from "react";
2 | import clsx from "clsx";
3 |
4 | interface ActionCellProps extends HTMLAttributes {
5 | children: ReactNode;
6 | }
7 |
8 | /**
9 | * Wrapper component for table action cells that aligns content to the right.
10 | * Used consistently across column definitions for action dropdowns.
11 | */
12 | export function ActionCell({ children, className, ...props }: ActionCellProps) {
13 | return (
14 |
15 | {children}
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/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/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/components/announcements/announcement-image.tsx:
--------------------------------------------------------------------------------
1 | import { generateProjectImageUrl } from "@/lib/images";
2 | import { cn } from "@zenml-io/react-component-library";
3 |
4 | type Props = {
5 | imageUrl?: string;
6 | title: string;
7 | className?: string;
8 | };
9 |
10 | export function AnnouncementImage({ imageUrl, title, className }: Props) {
11 | const src = imageUrl ?? generateProjectImageUrl(title);
12 |
13 | return (
14 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/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/assets/icons/info.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/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/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/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/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/app/deployments/[deploymentId]/playground/_components/outputs/header.tsx:
--------------------------------------------------------------------------------
1 | import { PlaygroundOutputsSwitcher } from "./outputs-switcher";
2 | import { PlaygroundOutputsView } from "./types";
3 |
4 | type Props = {
5 | activeView: PlaygroundOutputsView;
6 | setActiveView: (view: PlaygroundOutputsView) => void;
7 | };
8 |
9 | export function PlaygroundOutputsHeader({ activeView, setActiveView }: Props) {
10 | return (
11 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/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/assets/icons/expand.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/app/runs/ButtonGroup.tsx:
--------------------------------------------------------------------------------
1 | import { pluralize } from "@/lib/strings";
2 | import { DeleteRunAlert } from "./DeleteRunAlert";
3 | import { useRunsSelectorContext } from "./RunsSelectorContext";
4 |
5 | export function RunsButtonGroup() {
6 | const { selectedRowCount } = useRunsSelectorContext();
7 | return (
8 |
9 |
{`${selectedRowCount} ${pluralize(selectedRowCount, "Run")} selected`}
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/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/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/components/deployments/playground/preview-visualization/components/expand-collapse-icon.tsx:
--------------------------------------------------------------------------------
1 | import ExpandLines from "@/assets/icons/expand_lines.svg?react";
2 | import CollapseLines from "@/assets/icons/collapse_lines.svg?react";
3 | import { cn } from "@zenml-io/react-component-library/utilities";
4 |
5 | type Props = {
6 | className?: string;
7 | expanded: boolean;
8 | };
9 |
10 | export function ExpandCollapseLineIcon({ expanded, className }: Props) {
11 | const classNames = cn("size-3 shrink-0", className);
12 | return expanded ? (
13 |
14 | ) : (
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/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/layouts/AuthenticatedLayout/use-announcement.ts:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { announcementStore } from "@/components/announcements/persist-announcement";
3 |
4 | export function useAnnouncement() {
5 | const [open, setOpen] = useState(false);
6 |
7 | function openAnnouncement() {
8 | announcementStore.setAnnouncementLastSeen("lastSeen");
9 | setOpen(true);
10 | }
11 |
12 | function setIsAnnouncementOpen(open: boolean) {
13 | announcementStore.setAnnouncementLastSeen("lastSeen");
14 | setOpen(open);
15 | }
16 |
17 | return {
18 | isAnnouncementOpen: open,
19 | openAnnouncement,
20 | setIsAnnouncementOpen
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/assets/icons/side-collapse.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/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/assets/icons/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/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/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/monaco-setup.ts:
--------------------------------------------------------------------------------
1 | import { loader } from "@monaco-editor/react";
2 | import * as monaco from "monaco-editor";
3 | import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
4 | import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
5 | import { githubLight } from "./lib/monaco-editor/github-light";
6 |
7 | self.MonacoEnvironment = {
8 | getWorker(_, label) {
9 | if (label === "json") {
10 | return new jsonWorker();
11 | }
12 |
13 | return new editorWorker();
14 | }
15 | };
16 |
17 | loader.config({ monaco });
18 |
19 | loader.init().then((monaco) => {
20 | monaco.editor.defineTheme("github-light", githubLight);
21 | });
22 |
--------------------------------------------------------------------------------
/src/app/settings/service-accounts/[service-account-id]/page.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@zenml-io/react-component-library/components/server";
2 | import { ApiKeySelectorContextProvider } from "./SelectorContext";
3 | import ServiceAccountDetailTable from "./Table";
4 | import { APIKeyHeader } from "./header";
5 |
6 | export default function ServiceAccountDetail() {
7 | return (
8 |
9 |
13 |
14 |
15 |
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/runs/[id]/pipeline-viz/timeline/components/timeline-empty-state.tsx:
--------------------------------------------------------------------------------
1 | import { Box } from "@zenml-io/react-component-library/components/server";
2 |
3 | type Props = {
4 | title: string;
5 | description: string;
6 | };
7 |
8 | export function TimelineEmptyState({ title, description }: Props) {
9 | return (
10 |
11 |
12 |
13 |
{title}
14 |
{description}
15 |
16 |
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/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/assets/icons/alert-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/collapse.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/side-expand.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/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/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/assets/icons/cloud.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/app/deployments/[deploymentId]/_components/common.tsx:
--------------------------------------------------------------------------------
1 | import AlertCircle from "@/assets/icons/alert-circle.svg?react";
2 | import { EmptyState } from "@/components/EmptyState";
3 |
4 | type Props = {
5 | title: string;
6 | subtitle?: string;
7 | };
8 |
9 | export function AlertEmptyState({ title, subtitle }: Props) {
10 | return (
11 | }
14 | >
15 |
16 |
{title}
17 | {subtitle &&
{subtitle}
}
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/deployments/list/button-group.tsx:
--------------------------------------------------------------------------------
1 | import { ButtonGroupBasic } from "@/components/button-group/button-group-basic";
2 | import { useDeploymentSelectorContext } from "@/components/deployments/selector-context";
3 |
4 | export function PipelineDeploymentsButtonGroup() {
5 | const { selectedRowCount } = useDeploymentSelectorContext();
6 | // const { bulkDelete } = useSnapshotBulkDelete();
7 |
8 | async function handleBulkDeleteSelected() {
9 | // await bulkDelete(selectedRowIDs);
10 | }
11 |
12 | return (
13 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/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/assets/icons/search.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/components/announcements/announcement-video.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@zenml-io/react-component-library";
2 |
3 | type Props = {
4 | videoUrl: string;
5 | className?: string;
6 | };
7 |
8 | export function AnnouncementVideo({ videoUrl, className }: Props) {
9 | return (
10 |
11 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/form/common.tsx:
--------------------------------------------------------------------------------
1 | import { FieldHint } from "./FieldHint";
2 | import { JSONSchemaDefinition } from "@/types/forms";
3 |
4 | type Props = {
5 | label: string;
6 | isOptional: boolean;
7 | schema?: JSONSchemaDefinition;
8 | fieldName?: string;
9 | };
10 |
11 | export function RendererHeadline({ label, isOptional, schema, fieldName }: Props) {
12 | return (
13 |
14 |
15 | {label}
16 | {!isOptional && (
17 |
18 | *
19 |
20 | )}
21 |
22 | {schema && fieldName &&
}
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/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/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 |
18 | export function generateProjectImageUrl(projectName: string): string {
19 | return `https://public-flavor-logos.s3.eu-central-1.amazonaws.com/projects/${generateNumberFromSalt(
20 | projectName
21 | )}.jpg`;
22 | }
23 |
--------------------------------------------------------------------------------
/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/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/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/assets/icons/alert-triangle.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
8 |
9 |
--------------------------------------------------------------------------------
/src/components/tabs/scrolling-tabs-list.tsx:
--------------------------------------------------------------------------------
1 | import type { TabsListProps } from "@radix-ui/react-tabs";
2 | import { TabsList } from "@zenml-io/react-component-library";
3 | import { cn, ScrollArea, ScrollBar } from "@zenml-io/react-component-library";
4 | import { forwardRef } from "react";
5 |
6 | export const ScrollingTabsList = forwardRef(
7 | ({ children, className, ...props }, ref) => {
8 | return (
9 |
10 |
11 | {children}
12 |
13 |
14 |
15 | );
16 | }
17 | );
18 |
19 | ScrollingTabsList.displayName = "ScrollingTabsList";
20 |
--------------------------------------------------------------------------------
/src/layouts/AuthenticatedLayout/whats-new-button.tsx:
--------------------------------------------------------------------------------
1 | import Speaker from "@/assets/icons/announcement.svg?react";
2 | import { AnnouncementIndicator } from "@/components/announcements/announcement-indicator";
3 | import { DropdownMenuItem } from "@zenml-io/react-component-library/components/client";
4 |
5 | type Props = {
6 | openDialog: () => void;
7 | };
8 |
9 | export default function WhatsNewButton({ openDialog }: Props) {
10 | return (
11 | openDialog()}
13 | className="flex items-start gap-0.5 hover:cursor-pointer data-[highlighted]:bg-theme-surface-tertiary"
14 | icon={ }
15 | >
16 | What's new
17 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/src/assets/icons/tick-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/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/deployments/playground/preview-visualization/components/boolean-value.tsx:
--------------------------------------------------------------------------------
1 | import SlashCircle from "@/assets/icons/slash-circle.svg?react";
2 | import CheckCircle from "@/assets/icons/check-circle.svg?react";
3 | import { cn } from "@zenml-io/react-component-library";
4 |
5 | export function BooleanValue({ value, className }: { value: boolean; className?: string }) {
6 | return (
7 |
8 | {value === true ? (
9 |
10 | ) : (
11 |
12 | )}
13 |
14 | {value ? "True" : "False"}
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/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/app/pipelines/_components/ButtonGroup.tsx:
--------------------------------------------------------------------------------
1 | import { pluralize } from "@/lib/strings";
2 | import { DeletePipelineAlert } from "./DeletePipelineAlert";
3 | import { usePipelineSelectorContext } from "./PipelineSelectorContext";
4 |
5 | export function PipelinesButtonGroup() {
6 | const { selectedRowCount } = usePipelineSelectorContext();
7 | const plural = pluralize(selectedRowCount, "Pipeline");
8 |
9 | return (
10 |
11 |
{`${selectedRowCount} ${plural} selected`}
12 |
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/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/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 | import { usePageTitle } from "@/hooks/usePageTitle";
6 |
7 | export function RootLayout() {
8 | const navigate = useNavigate();
9 | const { data } = useServerInfo({ throwOnError: true });
10 |
11 | // Set browser titles for both public and authenticated routes.
12 | usePageTitle();
13 |
14 | useEffect(() => {
15 | if (data && data.active === false) {
16 | navigate(routes.activateServer + `?redirect=${routes.onboarding}`, { replace: true });
17 | }
18 | }, [data, navigate]);
19 |
20 | return ;
21 | }
22 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/app/settings/service-accounts/ButtonGroup.tsx:
--------------------------------------------------------------------------------
1 | import { pluralize } from "@/lib/strings";
2 | import { DeleteServiceAccountAlert } from "./DeleteAlert";
3 | import { useServiceAccountSelectorContext } from "./SelectorContext";
4 |
5 | export function ServiceAccountsButtonGroup() {
6 | const { selectedRowCount } = useServiceAccountSelectorContext();
7 | return (
8 |
9 |
{`${
10 | selectedRowCount
11 | } ${pluralize(selectedRowCount, "Service Account")} selected`}
12 |
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/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/components/pipeline-snapshots/list/button-group.tsx:
--------------------------------------------------------------------------------
1 | import { ButtonGroupBasic } from "@/components/button-group/button-group-basic";
2 | import {
3 | useSnapshotBulkDelete,
4 | useSnapshotSelectorContext
5 | } from "@/components/pipeline-snapshots/selector-context";
6 |
7 | export function PipelineSnapshotsButtonGroup() {
8 | const { selectedRowCount, selectedRowIDs } = useSnapshotSelectorContext();
9 | const { bulkDelete } = useSnapshotBulkDelete();
10 |
11 | async function handleBulkDeleteSelected() {
12 | await bulkDelete(selectedRowIDs);
13 | }
14 |
15 | return (
16 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src/lib/timeline/types.ts:
--------------------------------------------------------------------------------
1 | import { RawArtifactNode, RawStepNode } from "@/types/dag-visualizer";
2 | import { Node } from "@/types/dag-visualizer";
3 |
4 | export type TimelineItem = {
5 | triggeredRuns: RawStepNode[];
6 | step: RawStepNode;
7 | inputs: RawArtifactNode[];
8 | outputs: RawArtifactNode[];
9 | startTimeMs?: number; // Parsed timestamp in milliseconds
10 | };
11 |
12 | export type VirtualizedStep = {
13 | type: "timeline";
14 | item: TimelineItem;
15 | };
16 |
17 | export type VirtualizedPlaceholder = {
18 | type: "placeholder";
19 | item: Node;
20 | };
21 |
22 | export type VirtualizedSeparator = {
23 | type: "separator";
24 | };
25 |
26 | export type VirtualizedItem = VirtualizedStep | VirtualizedPlaceholder | VirtualizedSeparator;
27 |
--------------------------------------------------------------------------------
/src/app/pipelines/[pipelineId]/runs/breadcrumb.ts:
--------------------------------------------------------------------------------
1 | import { pipelineBreadcrumb } from "@/components/breadcrumbs/library";
2 | import { useBreadcrumbsContext } from "@/layouts/AuthenticatedLayout/BreadcrumbsContext";
3 | import { routes } from "@/router/routes";
4 | import { Pipeline } from "@/types/pipelines";
5 | import { useEffect } from "react";
6 |
7 | export function usePipelineDetailBreadcrumbs(pipeline?: Pipeline) {
8 | const { setBreadcrumbs } = useBreadcrumbsContext();
9 |
10 | useEffect(() => {
11 | if (pipeline) {
12 | setBreadcrumbs([
13 | pipelineBreadcrumb,
14 | {
15 | label: pipeline.name,
16 | href: routes.projects.pipelines.detail.runs(pipeline.id)
17 | }
18 | ]);
19 | }
20 | }, [setBreadcrumbs, pipeline]);
21 | }
22 |
--------------------------------------------------------------------------------
/src/assets/icons/stopped.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/data/deployments/index.ts:
--------------------------------------------------------------------------------
1 | import { DeploymentsListQueryParams } from "@/types/deployments";
2 | import { queryOptions } from "@tanstack/react-query";
3 | import { fetchDeploymentsList } from "./fetch-list";
4 | import { fetchDeploymentDetail } from "./fetch-detail";
5 |
6 | export const deploymentQueries = {
7 | all: ["deployments"] as const,
8 | detail: (deploymentId: string) =>
9 | queryOptions({
10 | queryKey: [...deploymentQueries.all, deploymentId],
11 | queryFn: async () => fetchDeploymentDetail({ deploymentId })
12 | }),
13 | list: (queryParams: DeploymentsListQueryParams) =>
14 | queryOptions({
15 | queryKey: [...deploymentQueries.all, queryParams],
16 | queryFn: async () => fetchDeploymentsList({ params: queryParams })
17 | })
18 | };
19 |
--------------------------------------------------------------------------------
/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/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/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/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 | primary_role?: string;
17 | overview_tour_done?: boolean;
18 | ai_types_working_with?: string[];
19 | biggest_ai_challenges?: string[];
20 | finished_onboarding_survey?: boolean;
21 | };
22 |
--------------------------------------------------------------------------------
/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/assets/icons/expand-full.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/components/service-connectors/connector-tag.tsx:
--------------------------------------------------------------------------------
1 | import Transform from "@/assets/icons/transform.svg?react";
2 | import { routes } from "@/router/routes";
3 | import { Tag } from "@zenml-io/react-component-library/components/server";
4 | import { Link } from "react-router-dom";
5 |
6 | type Props = {
7 | connectorName: string;
8 | connectorId: string;
9 | };
10 |
11 | export function ConnectorTag({ connectorName, connectorId }: Props) {
12 | return (
13 |
14 |
15 |
16 | {connectorName}
17 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/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/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/components/announcements/announcement-indicator.tsx:
--------------------------------------------------------------------------------
1 | import { useAnnouncements } from "@/data/announcements/announcements";
2 | import { cn } from "@zenml-io/react-component-library";
3 | import { useNewAnnouncements } from "./use-new-announcements";
4 |
5 | type Props = {
6 | className?: string;
7 | };
8 |
9 | export function AnnouncementIndicator({ className }: Props) {
10 | const announcementsQuery = useAnnouncements();
11 | const newPublishedItems = useNewAnnouncements("lastSeen", announcementsQuery.data, false);
12 |
13 | const hasNewAnnouncements = newPublishedItems.length > 0;
14 |
15 | if (!hasNewAnnouncements) return null;
16 |
17 | return (
18 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src/data/analytics/event.ts:
--------------------------------------------------------------------------------
1 | import { AnalyticsEvent } from "@/types/analytics";
2 | import { fetcher } from "../fetch";
3 | import { analyticsServerUrl } from "@/lib/analytics";
4 | import { useMutation, UseMutationOptions } from "@tanstack/react-query";
5 |
6 | async function performAnalyticsEvent(event: AnalyticsEvent) {
7 | fetcher(analyticsServerUrl, {
8 | method: "POST",
9 | credentials: "omit",
10 | headers: {
11 | "Content-Type": "application/json"
12 | },
13 | body: JSON.stringify([event])
14 | });
15 | }
16 |
17 | export function useAnalyticsEvent(options?: UseMutationOptions) {
18 | return useMutation({
19 | ...options,
20 | mutationFn: async (event: AnalyticsEvent) => {
21 | return performAnalyticsEvent(event);
22 | }
23 | });
24 | }
25 |
--------------------------------------------------------------------------------
/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/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/components/CodeHighlighter.tsx:
--------------------------------------------------------------------------------
1 | import Prism from "prismjs";
2 | import "prismjs/components/prism-python";
3 | import "prismjs/components/prism-typescript";
4 | import "prismjs/components/prism-docker";
5 | import "prismjs/components/prism-bash";
6 | import "prismjs/components/prism-json";
7 | import "@/assets/styles/prism-github-light.css";
8 | import { useEffect } from "react";
9 | import type { CodeLanguage } from "@/types/code-highlighting";
10 |
11 | type Props = {
12 | code: string;
13 | language?: CodeLanguage;
14 | };
15 |
16 | export function CodeHighlighter({ code, language = "python" }: Props) {
17 | const languageClass = `language-${language}`;
18 | useEffect(() => {
19 | Prism.highlightAll();
20 | }, [languageClass]);
21 | return {code};
22 | }
23 |
--------------------------------------------------------------------------------
/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/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/components/runs/stop-group/use-stop-run.tsx:
--------------------------------------------------------------------------------
1 | import { useStopPipelineRun } from "@/data/pipeline-runs/stop-run";
2 | import { useQueryClient } from "@tanstack/react-query";
3 | import { useToast } from "@zenml-io/react-component-library";
4 |
5 | export function useStopRun(closeAlert: () => void) {
6 | const { toast } = useToast();
7 | const queryClient = useQueryClient();
8 |
9 | const stopRunQuery = useStopPipelineRun({
10 | onSuccess: () => {
11 | queryClient.invalidateQueries({ queryKey: ["runs"] });
12 | closeAlert();
13 | },
14 | onError: (err) => {
15 | toast({
16 | rounded: true,
17 | status: "error",
18 | emphasis: "subtle",
19 | description: {err.message}
20 | });
21 | closeAlert();
22 | }
23 | });
24 |
25 | return { stopRunQuery };
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/stacks/info/sort-component.ts:
--------------------------------------------------------------------------------
1 | import { StackComponent } from "@/types/components";
2 | import { StackComponentType } from "@/types/components";
3 | import { stackComponentTypes } from "@/lib/constants";
4 |
5 | export function sortComponents(components: StackComponent[]) {
6 | return components.sort((a, b) => {
7 | const typeA = a.body?.type ?? "";
8 | const typeB = b.body?.type ?? "";
9 | const indexA = stackComponentTypes.indexOf(typeA as StackComponentType);
10 | const indexB = stackComponentTypes.indexOf(typeB as StackComponentType);
11 | // If type not found in array, put it at the end
12 | const orderA = indexA === -1 ? stackComponentTypes.length : indexA;
13 | const orderB = indexB === -1 ? stackComponentTypes.length : indexB;
14 | return orderA - orderB;
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/.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@v5
17 |
18 | - name: Set up Node.js
19 | uses: actions/setup-node@v6
20 | with:
21 | node-version: "24"
22 |
23 | - name: Install pnpm
24 | uses: pnpm/action-setup@v4
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 |
--------------------------------------------------------------------------------
/src/app/settings/service-accounts/[service-account-id]/ButtonGroup.tsx:
--------------------------------------------------------------------------------
1 | import { pluralize } from "@/lib/strings";
2 | import { DeleteApiKey } from "./DeleteApiKeyDialog";
3 | import { useApiKeySelectorContext } from "./SelectorContext";
4 |
5 | export function ApiKeyButtonGroup({ serviceAccountId }: { serviceAccountId: string }) {
6 | const { selectedRowCount } = useApiKeySelectorContext();
7 | return (
8 |
9 |
{`${
10 | selectedRowCount
11 | } ${pluralize(selectedRowCount, "Api Key")} selected`}
12 |
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/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/app/runs/[id]/create-snapshot/page.tsx:
--------------------------------------------------------------------------------
1 | import { CreateSnapshotForm } from "@/components/pipeline-snapshots/create/form";
2 | import { usePipelineRun } from "@/data/pipeline-runs/pipeline-run-detail-query";
3 | import { useParams } from "react-router-dom";
4 | import { useCreateSnapshotFromRunBreadcrumbs } from "./breadcrumb";
5 |
6 | export default function CreateSnapshotFromRunPage() {
7 | const { runId } = useParams() as { runId: string };
8 | const runQuery = usePipelineRun({ runId });
9 |
10 | useCreateSnapshotFromRunBreadcrumbs(runQuery.data);
11 |
12 | if (runQuery.isPending) return Loading...
;
13 | if (runQuery.isError) return Error
;
14 |
15 | return (
16 |
17 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/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/pipelines/[pipelineId]/deployments/columns.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | createDeploymentAuthorColumn,
3 | createDeploymentCreatedColumn,
4 | createDeploymentNameColumn,
5 | createDeploymentSnapshotColumn,
6 | createDeploymentStatusColumn
7 | } from "@/components/deployments/list/column-definitions";
8 | import { Deployment } from "@/types/deployments";
9 | import { ColumnDef } from "@tanstack/react-table";
10 | import { useMemo } from "react";
11 |
12 | export function usePipelineDeploymentColumns(): ColumnDef[] {
13 | return useMemo[]>(
14 | () => [
15 | createDeploymentNameColumn(),
16 | createDeploymentStatusColumn(),
17 | createDeploymentSnapshotColumn(),
18 | createDeploymentAuthorColumn(),
19 | createDeploymentCreatedColumn()
20 | ],
21 |
22 | []
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/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/stacks/DeleteStackModal.tsx:
--------------------------------------------------------------------------------
1 | import { DeleteAlertContent, DeleteAlertContentBody } from "@/components/DeleteAlertDialog";
2 | import { AlertDialog } from "@zenml-io/react-component-library/components/client";
3 |
4 | type DeleteStackComponentAlertProps = {
5 | deleteHandler: () => void;
6 | open: boolean;
7 | setOpen: (open: boolean) => void;
8 | };
9 |
10 | export function DeleteStackAlert({ deleteHandler, open, setOpen }: DeleteStackComponentAlertProps) {
11 | return (
12 |
13 |
14 |
15 | Are you sure?
16 | This action cannot be undone.
17 |
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/deployments/selector-context.ts:
--------------------------------------------------------------------------------
1 | import { createDataTableConsumerContext } from "@/components/tables/helper";
2 |
3 | export const {
4 | Context: DeploymentSelectorContext,
5 | ContextProvider: DeploymentSelectorContextProvider,
6 | useContext: useDeploymentSelectorContext
7 | } = createDataTableConsumerContext();
8 |
9 | // export function useSnapshotBulkDelete() {
10 | // const { setRowSelection } = useSnapshotSelectorContext();
11 |
12 | // const { mutateAsync } = useUpdateSnapshot();
13 |
14 | // async function handleDelete(id: string) {
15 | // await mutateAsync({ snapshotId: id, payload: { name: false } });
16 | // }
17 |
18 | // return useBulkDelete({
19 | // deleteFn: handleDelete,
20 | // queryKeyToInvalidate: pipelineSnapshotQueries.all,
21 | // setRowSelection
22 | // });
23 | // }
24 |
--------------------------------------------------------------------------------
/src/app/overview/page.tsx:
--------------------------------------------------------------------------------
1 | import { useSearchParams } from "react-router-dom";
2 | import { SuccessBanner } from "./success-banner";
3 | import { SupportResources } from "./support-resources";
4 | import { Templates } from "./templates";
5 | import { NextSteps } from "./next-steps";
6 | import { Welcome } from "./welcome";
7 | import { PipelinesGridCollapsible } from "./pipelines-grid";
8 |
9 | export default function OverviewPage() {
10 | const [queryParams] = useSearchParams();
11 |
12 | const displayBanner = queryParams.get("success") === "true";
13 |
14 | return (
15 |
16 | {displayBanner &&
}
17 |
18 |
19 |
20 |
21 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/src/assets/icons/refresh.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/data/pipeline-snapshots/index.ts:
--------------------------------------------------------------------------------
1 | import { PipelineSnapshotListQueryParams } from "@/types/pipeline-snapshots";
2 | import { queryOptions } from "@tanstack/react-query";
3 | import { fetchPipelineSnapshotDetail } from "./fetch-snapshot-detail";
4 | import { fetchSnapshotList } from "./list-snapshots";
5 |
6 | export const pipelineSnapshotQueries = {
7 | all: ["pipeline_snapshots"] as const,
8 | detail: (snapshotId: string) =>
9 | queryOptions({
10 | queryKey: [...pipelineSnapshotQueries.all, snapshotId],
11 | queryFn: async () => fetchPipelineSnapshotDetail({ snapshotId })
12 | }),
13 | list: (queryParams: PipelineSnapshotListQueryParams) =>
14 | queryOptions({
15 | queryKey: [...pipelineSnapshotQueries.all, queryParams],
16 | queryFn: async () => fetchSnapshotList({ params: queryParams })
17 | })
18 | };
19 |
--------------------------------------------------------------------------------
/src/app/deployments/[deploymentId]/_layout/header/pipeline-tag.tsx:
--------------------------------------------------------------------------------
1 | import PipelineIcon from "@/assets/icons/pipeline.svg?react";
2 | import { routes } from "@/router/routes";
3 | import { Tag } from "@zenml-io/react-component-library";
4 | import { Link } from "react-router-dom";
5 | type Props = {
6 | pipelineId: string;
7 | pipelineName: string;
8 | };
9 |
10 | export function PipelineTag({ pipelineId, pipelineName }: Props) {
11 | return (
12 |
13 |
20 |
21 | {pipelineName}
22 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/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/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 |
22 |
23 | {err.message}
24 |
25 |
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/announcements/announcement-list/announcement-dialog-search-bar.tsx:
--------------------------------------------------------------------------------
1 | import Search from "@/assets/icons/search.svg?react";
2 | import { DebouncedInput } from "@/components/debounced-input";
3 |
4 | type Props = {
5 | searchQuery: string;
6 | setSearchQuery: (value: string) => void;
7 | };
8 |
9 | export function AnnouncementDialogSearchBar({ searchQuery, setSearchQuery }: Props) {
10 | return (
11 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/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/app/pipelines/[pipelineId]/runs/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/components/stacks/info/stack-info-component-list.tsx:
--------------------------------------------------------------------------------
1 | import { Stack, StackComponentsList } from "@/types/stack";
2 | import { StackInfoComponentListItem } from "./stack-info-component-list-item";
3 | import { sortComponents } from "./sort-component";
4 |
5 | type Props = {
6 | stack: Stack;
7 | objectConfig: Record;
8 | };
9 | export function StackInfoComponentList({ stack, objectConfig }: Props) {
10 | const allComponents = sortComponents(
11 | Object.values((stack.metadata?.components as StackComponentsList) || {}).flat()
12 | );
13 |
14 | return (
15 |
16 | {allComponents.map((component) => (
17 |
18 |
19 |
20 | ))}
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/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]: unknown };
26 |
27 | export type TrackEvent = {
28 | type: "track";
29 | user_id: string;
30 | debug: boolean;
31 | event: string;
32 | properties: Record;
33 | };
34 |
35 | export type AnalyticsEvent = PageEvent | TrackEvent;
36 |
--------------------------------------------------------------------------------
/src/app/pipelines/[pipelineId]/snapshots/use-breadcrumbs.ts:
--------------------------------------------------------------------------------
1 | import { pipelineBreadcrumb } from "@/components/breadcrumbs/library";
2 | import { useBreadcrumbsContext } from "@/layouts/AuthenticatedLayout/BreadcrumbsContext";
3 | import { routes } from "@/router/routes";
4 | import { Pipeline } from "@/types/pipelines";
5 | import { useEffect } from "react";
6 |
7 | export function usePipelineDetailSnapshotsBreadcrumbs(pipeline?: Pipeline) {
8 | const { setBreadcrumbs } = useBreadcrumbsContext();
9 |
10 | useEffect(() => {
11 | if (pipeline) {
12 | setBreadcrumbs([
13 | pipelineBreadcrumb,
14 | {
15 | label: pipeline.name,
16 | href: routes.projects.pipelines.detail.runs(pipeline.id)
17 | },
18 | {
19 | label: "Snapshots",
20 | href: "#"
21 | }
22 | ]);
23 | }
24 | }, [setBreadcrumbs, pipeline]);
25 | }
26 |
--------------------------------------------------------------------------------
/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/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/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 | {/* Disable pointer events on node container, re-enable on interactive children */}
13 |
14 | {children}
15 |
16 |
22 | >
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/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/deployments/list/use-deployment-queryparams.ts:
--------------------------------------------------------------------------------
1 | import { DeploymentsListQueryParams } from "@/types/deployments";
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 useDeploymentQueryParams(): DeploymentsListQueryParams {
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/components/pipeline-snapshots/create/_form-components/create-snapshot-form-footer.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@zenml-io/react-component-library";
2 | import { useNavigate } from "react-router-dom";
3 |
4 | type Props = {
5 | isPending: boolean;
6 | };
7 |
8 | export function CreateSnapshotFormFooter({ isPending }: Props) {
9 | const navigate = useNavigate();
10 |
11 | return (
12 |
13 |
navigate(-1)} intent="secondary" size="md">
14 | Cancel
15 |
16 |
17 | {isPending && (
18 |
19 | )}
20 | Create Snapshot
21 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/.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@v5
13 | - uses: actions/setup-node@v6
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 |
--------------------------------------------------------------------------------
/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/assets/icons/dots-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
--------------------------------------------------------------------------------
/src/components/logs/use-logpage-input.ts:
--------------------------------------------------------------------------------
1 | import { zodResolver } from "@hookform/resolvers/zod";
2 | import { useMemo } from "react";
3 | import { useForm } from "react-hook-form";
4 | import { z } from "zod";
5 |
6 | export function useLogPageInput(currentPage: number, totalPages: number) {
7 | const formSchema = useMemo(
8 | () =>
9 | z.object({
10 | page: z.coerce
11 | .number()
12 | .int()
13 | .transform((val) => {
14 | const num = Math.floor(val);
15 | if (isNaN(num) || num < 1) return 1;
16 | if (num > totalPages) return totalPages;
17 | return num;
18 | })
19 | .catch(1)
20 | }),
21 | [totalPages]
22 | );
23 |
24 | const form = useForm({
25 | resolver: zodResolver(formSchema),
26 | defaultValues: {
27 | page: currentPage
28 | }
29 | });
30 |
31 | return {
32 | form
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/src/data/github/pipeline-summary.ts:
--------------------------------------------------------------------------------
1 | import { useQuery, UseQueryOptions } from "@tanstack/react-query";
2 |
3 | async function fetchPipelineContent(pipelineName: string) {
4 | const url = `https://raw.githubusercontent.com/zenml-io/vscode-tutorial-extension/HEAD/pipelines/${pipelineName}/summary.md`;
5 | const response = await fetch(url);
6 | if (!response.ok) {
7 | throw new Error("Failed to fetch pipeline summary");
8 | }
9 | const data = await response.text();
10 | return data;
11 | }
12 |
13 | export function useGithubPipelineSummary(
14 | pipelineName: string,
15 | options?: Omit, "queryKey" | "queryFn">
16 | ) {
17 | return useQuery({
18 | queryKey: ["github", "vscode-tutorial-extension", "pipeline-summary", pipelineName],
19 | queryFn: async () => fetchPipelineContent(pipelineName),
20 | ...options
21 | });
22 | }
23 |
--------------------------------------------------------------------------------
/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 Variable", ...defaultTheme.fontFamily.sans],
15 | mono: ["JetBrains Mono Variable", ...defaultTheme.fontFamily.mono]
16 | }
17 | }
18 | },
19 | plugins: [
20 | require("@tailwindcss/forms")({
21 | strategy: "class"
22 | }),
23 | require("tailwindcss-animate"),
24 | require("@tailwindcss/typography"),
25 | require("@tailwindcss/container-queries")
26 | ],
27 | presets: [zenmlPreset]
28 | };
29 |
--------------------------------------------------------------------------------
/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/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/data/pipelines/pipeline-detail.ts:
--------------------------------------------------------------------------------
1 | import { FetchError } from "@/lib/fetch-error";
2 | import { notFound } from "@/lib/not-found-error";
3 | import { apiPaths, createApiPath } from "../api";
4 | import { fetcher } from "../fetch";
5 | export type PipelineDetailArgs = {
6 | pipelineId: string;
7 | };
8 |
9 | export async function fetchPipelineDetail({ pipelineId }: PipelineDetailArgs) {
10 | const url = createApiPath(apiPaths.pipelines.detail(pipelineId));
11 | const res = await fetcher(url, {
12 | method: "GET",
13 | headers: {
14 | "Content-Type": "application/json"
15 | }
16 | });
17 |
18 | if (res.status === 404) notFound();
19 |
20 | if (!res.ok) {
21 | throw new FetchError({
22 | message: `Error while fetching pipeline ${pipelineId}`,
23 | status: res.status,
24 | statusText: res.statusText
25 | });
26 | }
27 | return res.json();
28 | }
29 |
--------------------------------------------------------------------------------
/src/layouts/AuthenticatedLayout/back-button.tsx:
--------------------------------------------------------------------------------
1 | import ArrowLeft from "@/assets/icons/arrow-left.svg?react";
2 | import { Button } from "@zenml-io/react-component-library/components/server";
3 | import { useLocation, useNavigate } from "react-router-dom";
4 |
5 | export function BackButton() {
6 | const navigate = useNavigate();
7 | const location = useLocation();
8 |
9 | const segments = location.pathname.split("/").filter(Boolean);
10 |
11 | if (segments.length <= 1) {
12 | return null;
13 | }
14 |
15 | return (
16 | navigate(-1)}
21 | >
22 |
23 | Go back
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/deployments/columns.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | createDeploymentAuthorColumn,
3 | createDeploymentCreatedColumn,
4 | createDeploymentNameColumn,
5 | createDeploymentPipelineColumn,
6 | createDeploymentSnapshotColumn,
7 | createDeploymentStatusColumn
8 | } from "@/components/deployments/list/column-definitions";
9 | import { Deployment } from "@/types/deployments";
10 | import { ColumnDef } from "@tanstack/react-table";
11 | import { useMemo } from "react";
12 |
13 | export function useGlobalDeploymentColumns(): ColumnDef[] {
14 | return useMemo[]>(
15 | () => [
16 | createDeploymentNameColumn(),
17 | createDeploymentStatusColumn(),
18 | createDeploymentPipelineColumn(),
19 | createDeploymentSnapshotColumn(),
20 | createDeploymentAuthorColumn(),
21 | createDeploymentCreatedColumn()
22 | ],
23 |
24 | []
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/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/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/assets/icons/check-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/chevron-left-double.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/pipeline-snapshots/list/use-queryparams.ts:
--------------------------------------------------------------------------------
1 | import { PipelineSnapshotListQueryParams } from "@/types/pipeline-snapshots";
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 useSnapshotListQueryParams(): PipelineSnapshotListQueryParams {
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/data/github/pipeline-content.ts:
--------------------------------------------------------------------------------
1 | import { useQuery, UseQueryOptions } from "@tanstack/react-query";
2 |
3 | async function fetchPipelineContent(pipelineName: string) {
4 | const url = `https://raw.githubusercontent.com/zenml-io/vscode-tutorial-extension/HEAD/pipelines/${pipelineName}/${pipelineName}.py`;
5 | const response = await fetch(url);
6 | if (!response.ok) {
7 | throw new Error("Failed to fetch pipeline content");
8 | }
9 | const data = await response.text();
10 | return data;
11 | }
12 |
13 | export function useGithubPipelineContent(
14 | pipelineName: string,
15 | options?: Omit, "queryKey" | "queryFn">
16 | ) {
17 | return useQuery({
18 | queryKey: ["github", "vscode-tutorial-extension", "pipeline-content", pipelineName],
19 | queryFn: async () => fetchPipelineContent(pipelineName),
20 | ...options
21 | });
22 | }
23 |
--------------------------------------------------------------------------------
/.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@v5
26 |
27 | - name: Set up Node.js
28 | uses: actions/setup-node@v6
29 | with:
30 | node-version: "24"
31 |
32 | - name: Check Links
33 | run: bash scripts/check-links.sh
34 |
--------------------------------------------------------------------------------
/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/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/assets/icons/chevron-right-double.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/src/app/deployments/[deploymentId]/playground/_components/outputs/run-card-avatar.tsx:
--------------------------------------------------------------------------------
1 | import { InlineAvatar } from "@/components/InlineAvatar";
2 | import { usePipelineRun } from "@/data/pipeline-runs/pipeline-run-detail-query";
3 | import { Skeleton } from "@zenml-io/react-component-library";
4 |
5 | type Props = {
6 | runId: string;
7 | };
8 |
9 | export function PlaygroundRunCardAvatar({ runId }: Props) {
10 | const runQuery = usePipelineRun({ runId });
11 |
12 | if (runQuery.isPending) return ;
13 | if (runQuery.isError) return
;
14 |
15 | const user = runQuery.data?.resources?.user;
16 | if (!user) return
;
17 |
18 | return (
19 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/deployments/playground/playground-view-switcher-button.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@zenml-io/react-component-library";
2 | import { Button } from "@zenml-io/react-component-library/components";
3 | import { ComponentPropsWithoutRef, ElementRef, forwardRef } from "react";
4 |
5 | export const PlaygroundViewSwitcherButton = forwardRef<
6 | ElementRef,
7 | ComponentPropsWithoutRef
8 | >(({ className, ...rest }, ref) => {
9 | return (
10 |
20 | {rest.children}
21 |
22 | );
23 | });
24 |
25 | PlaygroundViewSwitcherButton.displayName = "PlaygroundViewSwitcherButton";
26 |
--------------------------------------------------------------------------------