├── 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 |
6 | 7 |
8 | 9 |
10 |
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 |
7 | 8 | 9 | 10 |
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 |
7 | 8 | 9 |
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 |
7 | 8 | 9 |
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 |
7 | 8 |
9 | 10 |
11 |
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 |
7 | 8 | 9 | 10 |
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 |
9 | 10 |
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 |
7 | 8 | 9 |
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 | {"Visualization 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 |
6 | 7 |
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 |
6 |
7 | 8 | 9 |
10 |
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 |
7 |
8 | 9 | 10 |
11 |
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 |
10 | 11 |
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 |
7 | 8 | 9 | 10 |
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 |
6 |
7 | 8 | 9 |
10 |
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 |
10 | 11 | 12 | 13 |
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 |
10 | 11 |
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 |
7 |
8 | 9 | 10 |
11 |
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 | 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 |
7 |
8 | 9 | 10 |
11 |
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 |
10 | 11 | 12 | 13 |
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 |
11 |
12 | 13 | 14 |
15 |
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 | 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 |
12 | 13 |
14 | 15 | 16 |
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 | {`Announcement 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 |
12 |

Output

13 | 14 |
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 |
10 | 11 |

API Keys

12 |
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 |
12 |
13 |
14 | 15 |
16 |
17 |
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 | 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 |
12 |
13 | 19 |
20 | 21 |
22 |
23 |
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 |
  1. i 20 | ? "bg-primary-300" 21 | : "bg-neutral-200" 22 | }`} 23 | >
  2. 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 | 16 | 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 | 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 | 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 |
13 | 14 |
15 | 16 |
17 |
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 | 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 | 22 | ); 23 | }); 24 | 25 | PlaygroundViewSwitcherButton.displayName = "PlaygroundViewSwitcherButton"; 26 | --------------------------------------------------------------------------------