Skip to content

Commit

Permalink
feat: Add selection of presets for DevPod Pro workspace creation (#1443)
Browse files Browse the repository at this point in the history
* feat: Add selection of presets for DevPod Pro workspace creation

* fix: Fix review issues
  • Loading branch information
PRTTMPRPHT authored Dec 5, 2024
1 parent a399a52 commit d31fb83
Show file tree
Hide file tree
Showing 17 changed files with 670 additions and 96 deletions.
9 changes: 9 additions & 0 deletions desktop/src/Theme/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ export const theme = extendTheme({
800: "#8E00EB",
900: "#40006A",
},
text: {
secondary: "#465E75",
tertiary: "#5C7997",
},
divider: {
main: "#B0C3D6",
light: "#DCE5EE",
dark: "#465E75",
},
background: {
darkest: "rgb(16, 18, 20)",
},
Expand Down
3 changes: 3 additions & 0 deletions desktop/src/contexts/DevPodContext/Pro/useTemplates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { useQuery, UseQueryResult } from "@tanstack/react-query"
import { QueryKeys } from "@/queryKeys"
import { ManagementV1DevPodWorkspaceTemplate } from "@loft-enterprise/client/gen/models/managementV1DevPodWorkspaceTemplate"
import { ManagementV1DevPodEnvironmentTemplate } from "@loft-enterprise/client/gen/models/managementV1DevPodEnvironmentTemplate"
import { ManagementV1DevPodWorkspacePreset } from "@loft-enterprise/client/gen/models/managementV1DevPodWorkspacePreset"

type TTemplates = Readonly<{
default: ManagementV1DevPodWorkspaceTemplate | undefined
workspace: readonly ManagementV1DevPodWorkspaceTemplate[]
environment: readonly ManagementV1DevPodEnvironmentTemplate[]
presets: readonly ManagementV1DevPodWorkspacePreset[]
}>
export function useTemplates(): UseQueryResult<TTemplates> {
const { host, currentProject, client } = useProContext()
Expand All @@ -31,6 +33,7 @@ export function useTemplates(): UseQueryResult<TTemplates> {
default: defaultTemplate,
workspace: projectTemplates?.devPodWorkspaceTemplates ?? [],
environment: projectTemplates?.devPodEnvironmentTemplates ?? [],
presets: projectTemplates?.devPodWorkspacePresets ?? [],
}
},
enabled: !!currentProject,
Expand Down
11 changes: 11 additions & 0 deletions desktop/src/icons/Gold.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createIcon } from "@chakra-ui/react"
import { defaultProps } from "./defaultProps"

export const Gold = createIcon({
displayName: "Gold",
viewBox: "0 0 16 16",
defaultProps,
path: (
<path d="M5.34375 7.375H10.6875C10.6938 7.375 10.7016 7.375 10.7078 7.37344C10.7766 7.3625 10.8219 7.29844 10.8109 7.22969L10.1828 3.35469C10.1734 3.29375 10.1203 3.25 10.0594 3.25H5.97188C5.91094 3.25 5.85781 3.29375 5.84844 3.35469L5.22031 7.22969C5.21875 7.23594 5.21875 7.24375 5.21875 7.25C5.21875 7.31875 5.275 7.375 5.34375 7.375ZM6.76875 4.3125H9.26094L9.58438 6.3125H6.44375L6.76875 4.3125ZM6.80781 8.72969C6.79844 8.66875 6.74531 8.625 6.68437 8.625H2.59687C2.53594 8.625 2.48281 8.66875 2.47344 8.72969L1.84531 12.6047C1.84375 12.6109 1.84375 12.6187 1.84375 12.625C1.84375 12.6938 1.9 12.75 1.96875 12.75H7.3125C7.31875 12.75 7.32656 12.75 7.33281 12.7484C7.40156 12.7375 7.44688 12.6734 7.43594 12.6047L6.80781 8.72969ZM3.07031 11.6875L3.39375 9.6875H5.88594L6.20937 11.6875H3.07031ZM14.1547 12.6047L13.5266 8.72969C13.5172 8.66875 13.4641 8.625 13.4031 8.625H9.31563C9.25469 8.625 9.20156 8.66875 9.19219 8.72969L8.56406 12.6047C8.5625 12.6109 8.5625 12.6187 8.5625 12.625C8.5625 12.6938 8.61875 12.75 8.6875 12.75H14.0312C14.0375 12.75 14.0453 12.75 14.0516 12.7484C14.1187 12.7375 14.1656 12.6734 14.1547 12.6047ZM9.78906 11.6875L10.1125 9.6875H12.6047L12.9281 11.6875H9.78906Z" />
),
})
14 changes: 12 additions & 2 deletions desktop/src/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export const Routes = {
PRO: "/pro",
PRO_INSTANCE: "/pro/:host",
PRO_WORKSPACE: "/pro/:host/:workspace",
PRO_WORKSPACE_SELECT_PRESET: "/pro/:host/select-preset",
PRO_WORKSPACE_CREATE: "/pro/:host/new",
PRO_SETTINGS: "/pro/:host/settings",
toProInstance(host: string): string {
Expand All @@ -89,10 +90,15 @@ export const Routes = {

return `${base}/${instanceID}`
},
toProWorkspaceCreate(host: string): string {
toProWorkspaceCreate(host: string, fromPreset?: string): string {
const base = this.toProInstance(host)

return `${base}/new`
return `${base}/new${fromPreset ? `?fromPreset=${fromPreset}` : ""}`
},
toProSelectPreset(host: string): string {
const base = this.toProInstance(host)

return `${base}/select-preset`
},
toProWorkspaceDetail(host: string, instanceID: string, detail: TProInstanceDetail): string {
const base = this.toProInstance(host)
Expand Down Expand Up @@ -137,6 +143,10 @@ export const router = createBrowserRouter([
path: Routes.PRO_WORKSPACE_CREATE,
element: <Pro.CreateWorkspace />,
},
{
path: Routes.PRO_WORKSPACE_SELECT_PRESET,
element: <Pro.SelectPreset />,
},
{ path: Routes.PRO_SETTINGS, element: <Pro.Settings /> },
],
},
Expand Down
96 changes: 71 additions & 25 deletions desktop/src/views/Pro/CreateWorkspace/CreateWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,31 @@ import {
ProWorkspaceInstance,
ProWorkspaceStore,
useProContext,
useTemplates,
useWorkspace,
useWorkspaceStore,
} from "@/contexts"
import {
Annotations,
Failed,
Labels,
randomString,
Result,
Return,
Source,
randomString,
safeMaxName,
Source,
} from "@/lib"
import { Routes } from "@/routes"
import { Box, HStack, Heading, VStack } from "@chakra-ui/react"
import { NewResource, Resources, getProjectNamespace } from "@loft-enterprise/client"
import { Box, Heading, HStack, VStack } from "@chakra-ui/react"
import { getProjectNamespace, NewResource, Resources } from "@loft-enterprise/client"
import { ManagementV1DevPodWorkspaceInstance } from "@loft-enterprise/client/gen/models/managementV1DevPodWorkspaceInstance"
import * as jsyaml from "js-yaml"
import { useState } from "react"
import { useEffect, useMemo, useState } from "react"
import { useNavigate } from "react-router-dom"
import { BackToWorkspaces } from "../BackToWorkspaces"
import { CreateWorkspaceForm } from "./CreateWorkspaceForm"
import { TFormValues } from "./types"
import { useLocation } from "react-router"

export function CreateWorkspace() {
const workspace = useWorkspace<ProWorkspaceInstance>(undefined)
Expand All @@ -34,6 +36,31 @@ export function CreateWorkspace() {
const { host, currentProject, managementSelfQuery, client } = useProContext()
const navigate = useNavigate()

const { data: templates, isLoading: isTemplatesLoading } = useTemplates()

const presets = templates?.presets

const [presetId, setPresetId] = useState<string | undefined>(undefined)
const routerLocation = useLocation()

useEffect(() => {
const searchParams = new URLSearchParams(routerLocation.search)
const fromPreset = searchParams.get("fromPreset")

if (fromPreset && !presetId) {
setPresetId(fromPreset)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

const preset = useMemo(() => {
if (!presetId) {
return undefined
}

return (presets ?? []).find((p) => p.metadata?.name === presetId)
}, [presetId, presets])

const handleReset = () => {
setGlobalError(null)
navigate(Routes.toProInstance(host))
Expand All @@ -44,7 +71,8 @@ export function CreateWorkspace() {
const instanceRes = await buildWorkspaceInstance(
values,
currentProject?.metadata?.name,
managementSelfQuery.data?.status?.projectNamespacePrefix
managementSelfQuery.data?.status?.projectNamespacePrefix,
presetId
)
if (instanceRes.err) {
setGlobalError(instanceRes.val)
Expand Down Expand Up @@ -74,22 +102,31 @@ export function CreateWorkspace() {
}

return (
<Box h="full" mb="40">
<Box mb="40">
<VStack align="start">
<BackToWorkspaces />
<HStack align="center" justify="space-between" mb="8">
<HStack align="center" justify="space-between" mb="8" mt={"2"}>
<Heading fontWeight="thin">Create Workspace</Heading>
</HStack>
</VStack>
<CreateWorkspaceForm onReset={handleReset} onSubmit={handleSubmit} error={globalError} />
<CreateWorkspaceForm
onReset={handleReset}
onSubmit={handleSubmit}
loading={isTemplatesLoading}
error={globalError}
preset={preset}
presets={presets}
setPreset={setPresetId}
/>
</Box>
)
}

async function buildWorkspaceInstance(
values: TFormValues,
currentProject: string | undefined,
projectNamespacePrefix: string | undefined
projectNamespacePrefix: string | undefined,
preset: string | undefined
): Promise<Result<{ workspaceID: string; instance: ManagementV1DevPodWorkspaceInstance }>> {
const instance = NewResource(Resources.ManagementV1DevPodWorkspaceInstance)
const workspaceSource = new Source(values.sourceType, values.source)
Expand Down Expand Up @@ -136,31 +173,40 @@ async function buildWorkspaceInstance(
instance.metadata.annotations[Annotations.WorkspaceSource] = workspaceSource.stringify()
instance.spec.displayName = displayName

// Template, version and parameters
const { workspaceTemplate: template, workspaceTemplateVersion, ...parameters } = values.options
let templateVersion = workspaceTemplateVersion
if (templateVersion === "latest") {
templateVersion = ""
}
instance.spec.templateRef = {
name: template,
version: templateVersion,
}

instance.spec.runnerRef = {
runner: values.runner,
}

// Template, version and parameters
const { workspaceTemplate: template, workspaceTemplateVersion, ...parameters } = values.options

try {
instance.spec.parameters = jsyaml.dump(parameters)
} catch (err) {
return Return.Failed(err as any)
}

// Environment template
if (values.devcontainerType === "external") {
instance.spec.environmentRef = {
name: values.devcontainerJSON,
if (preset) {
instance.spec.presetRef = { name: preset }
} else {
let templateVersion = workspaceTemplateVersion
if (templateVersion === "latest") {
templateVersion = ""
}
instance.spec.templateRef = {
name: template,
version: templateVersion,
}

// Environment template
if (values.devcontainerType === "external") {
instance.spec.environmentRef = {
name: values.devcontainerJSON,
}

if (values.envTemplateVersion !== "latest") {
instance.spec.environmentRef.version = values.envTemplateVersion
}
}
}

Expand Down
Loading

0 comments on commit d31fb83

Please sign in to comment.