Skip to content

Commit

Permalink
fix(desktop): show provider options for workspaces with old pro provider
Browse files Browse the repository at this point in the history
  • Loading branch information
pascalbreuninger committed Nov 14, 2024
1 parent 3127fb9 commit 37e66ce
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 5 deletions.
172 changes: 168 additions & 4 deletions desktop/src/views/Workspaces/WorkspaceCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import { Card, CardHeader, Text } from "@chakra-ui/react"
import { useCallback, useMemo, useState } from "react"
import {
Box,
Card,
CardHeader,
Icon,
List,
ListItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalOverlay,
Text,
useDisclosure,
} from "@chakra-ui/react"
import { useCallback, useMemo, useRef, useState } from "react"
import { useNavigate } from "react-router"
import { WorkspaceCardHeader } from "../../components"
import { IconTag, WorkspaceCardHeader } from "../../components"
import {
TActionID,
useProvider,
Expand All @@ -18,10 +33,15 @@ import {
useStopWorkspaceModal,
} from "../../lib"
import { Routes } from "../../routes"
import { TWorkspace, TWorkspaceID } from "../../types"
import { TProvider, TWorkspace, TWorkspaceID } from "../../types"
import { useIDEs } from "../../useIDEs"
import { WorkspaceControls } from "./WorkspaceControls"
import { WorkspaceStatusBadge } from "./WorkspaceStatusBadge"
import { ConfigureProviderOptionsForm } from "../Providers"
import { Template } from "@/icons"
import { HiServerStack } from "react-icons/hi2"
import { TOptionWithID, mergeOptionDefinitions } from "../Providers/helpers"
import { processDisplayOptions } from "../Providers/AddProvider/useProviderOptions"

type TWorkspaceCardProps = Readonly<{
workspaceID: TWorkspaceID
Expand All @@ -30,6 +50,7 @@ type TWorkspaceCardProps = Readonly<{
}>

export function WorkspaceCard({ workspaceID, isSelected, onSelectionChange }: TWorkspaceCardProps) {
const changeOptionsModalBodyRef = useRef<HTMLDivElement>(null)
const settings = useSettings()
const navigate = useNavigate()
const { ides, defaultIDE } = useIDEs()
Expand Down Expand Up @@ -85,6 +106,11 @@ export function WorkspaceCard({ workspaceID, isSelected, onSelectionChange }: TW
[navigateToAction, workspace]
)
)
const {
isOpen: isChangeOptionsOpen,
onOpen: handleChangeOptionsClicked,
onClose: onChangeOptionsClose,
} = useDisclosure()

const [provider] = useProvider(workspace.data?.provider?.name)
const [ideName, setIdeName] = useState<string | undefined>(() => {
Expand Down Expand Up @@ -134,12 +160,37 @@ export function WorkspaceCard({ workspaceID, isSelected, onSelectionChange }: TW
return undefined
}, [navigateToAction, workspace, workspaceActions])

const handleChangeOptionsFinishClicked = (extraProviderOptions: Record<string, string>) => {
// diff against current workspace options
let changedOptions: Record<string, string> | undefined = undefined
if (Object.keys(extraProviderOptions).length > 0) {
changedOptions = {}
const workspaceOptions = workspace.data?.provider?.options ?? {}
for (const [k, v] of Object.entries(extraProviderOptions)) {
// check if current workspace option doesn't contain option or it does but value is different
if (!workspaceOptions[k] || workspaceOptions[k]?.value !== v) {
changedOptions[k] = v
}
}
}
const actionID = workspace.start({
id: workspaceID,
providerConfig: { options: changedOptions },
})
onChangeOptionsClose()
navigateToAction(actionID)
}

const isLoading = workspace.current?.status === "pending"

if (workspace.data === undefined) {
return null
}

const maybeRunnerName = getRunnerName(workspace.data, provider)
const maybeTemplate = getTemplate(workspace.data, provider)
const maybeTemplateOptions = getTemplateOptions(workspace.data, provider)

return (
<>
<Card
Expand Down Expand Up @@ -195,11 +246,47 @@ export function WorkspaceCard({ workspaceID, isSelected, onSelectionChange }: TW
onDeleteClicked={openDeleteModal}
onStopClicked={openStopModal}
onLogsClicked={handleLogsClicked}
onChangeOptionsClicked={handleChangeOptionsClicked}
/>
}>
<WorkspaceCardHeader.Provider name={workspace.data.provider?.name ?? undefined} />
<WorkspaceCardHeader.IDE name={ideDisplayName} />
<WorkspaceCardHeader.LastUsed timestamp={workspace.data.lastUsed} />
{maybeTemplate && (
<IconTag
icon={<Template />}
label={maybeTemplate}
info={
<Box width="full">
Using {maybeTemplate} template with options: <br />
{maybeTemplateOptions.length > 0 ? (
<List mt="2" width="full">
{maybeTemplateOptions.map((opt) => (
<ListItem
key={opt.id}
width="full"
display="flex"
flexFlow="row nowrap"
alignItems="space-between">
<Text fontWeight="bold">{opt.value}</Text>
<Text ml="4">({opt.displayName || opt.id})</Text>
</ListItem>
))}
</List>
) : (
"No options configured"
)}
</Box>
}
/>
)}
{maybeRunnerName && (
<IconTag
icon={<Icon as={HiServerStack} />}
label={maybeRunnerName}
info={`Running on ${maybeRunnerName}`}
/>
)}
</WorkspaceCardHeader>
</CardHeader>
</Card>
Expand All @@ -208,6 +295,83 @@ export function WorkspaceCard({ workspaceID, isSelected, onSelectionChange }: TW
{rebuildModal}
{deleteModal}
{stopModal}

<Modal
onClose={onChangeOptionsClose}
isOpen={isChangeOptionsOpen}
isCentered
size="4xl"
scrollBehavior="inside">
<ModalOverlay />
<ModalContent>
<ModalHeader>Change Options</ModalHeader>
<ModalCloseButton />
<ModalBody
ref={changeOptionsModalBodyRef}
overflowX="hidden"
overflowY="auto"
paddingBottom="0">
{workspace.data.provider?.name ? (
<ConfigureProviderOptionsForm
workspace={workspace.data}
showBottomActionBar
isModal
submitTitle="Update &amp; Open"
containerRef={changeOptionsModalBodyRef}
reuseMachine={false}
providerID={workspace.data.provider.name}
onFinish={handleChangeOptionsFinishClicked}
/>
) : (
<>Unable to find provider for this workspace</>
)}
</ModalBody>
</ModalContent>
</Modal>
</>
)
}

function getRunnerName(workspace: TWorkspace, provider: TProvider | undefined): string | undefined {
const options = mergeOptionDefinitions(
workspace.provider?.options ?? {},
provider?.config?.options ?? {}
)
const maybeRunnerOption = options["LOFT_RUNNER"]
if (!maybeRunnerOption) {
return undefined
}
const value = maybeRunnerOption.value

return maybeRunnerOption.enum?.find((e) => e.value === value)?.displayName ?? value ?? undefined
}

function getTemplate(workspace: TWorkspace, provider: TProvider | undefined): string | undefined {
const options = mergeOptionDefinitions(
workspace.provider?.options ?? {},
provider?.config?.options ?? {}
)
const maybeTemplateOption = options["LOFT_TEMPLATE"]
if (!maybeTemplateOption) {
return undefined
}
const value = maybeTemplateOption.value

return maybeTemplateOption.enum?.find((e) => e.value === value)?.displayName ?? value ?? undefined
}

function getTemplateOptions(
workspace: TWorkspace,
provider: TProvider | undefined
): readonly TOptionWithID[] {
const options = mergeOptionDefinitions(
workspace.provider?.options ?? {},
provider?.config?.options ?? {}
)
const displayOptions = processDisplayOptions(options, [], true)

// shouldn't have groups here as we passed in empty array earlier
return [...displayOptions.required, ...displayOptions.other].filter(
(opt) => opt.id !== "LOFT_TEMPLATE"
)
}
23 changes: 22 additions & 1 deletion desktop/src/views/Workspaces/WorkspaceControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,16 @@ import { HiOutlineCode, HiShare } from "react-icons/hi"
import { client } from "../../client"
import { IDEIcon } from "../../components"
import { TActionID, useProInstances } from "../../contexts"
import { ArrowCycle, ArrowPath, CommandLine, Ellipsis, Pause, Play, Trash } from "../../icons"
import {
ArrowCycle,
ArrowPath,
CommandLine,
Ellipsis,
Pause,
Play,
Stack3D,
Trash,
} from "../../icons"
import { getIDEDisplayName, useHover } from "../../lib"
import { TIDE, TIDEs, TProInstance, TProvider, TWorkspace, TWorkspaceID } from "../../types"

Expand All @@ -42,6 +51,7 @@ type TWorkspaceControlsProps = Readonly<{
onDeleteClicked: VoidFunction
onStopClicked: VoidFunction
onLogsClicked: VoidFunction
onChangeOptionsClicked?: VoidFunction
}>
export function WorkspaceControls({
id,
Expand All @@ -58,6 +68,7 @@ export function WorkspaceControls({
onDeleteClicked,
onStopClicked,
onLogsClicked,
onChangeOptionsClicked,
}: TWorkspaceControlsProps) {
const [[proInstances]] = useProInstances()
const proInstance = useMemo<TProInstance | undefined>(() => {
Expand Down Expand Up @@ -89,6 +100,8 @@ export function WorkspaceControls({
"Cannot open this workspace because it is busy. If this doesn't change, try to force delete and recreate it."
const [isStartWithHovering, startWithRef] = useHover()
const [isPopoverHovering, popoverContentRef] = useHover()
const isChangeOptionsEnabled =
workspace.data?.provider?.options != null && proInstance !== undefined

return (
<HStack spacing="2" width="full" justifyContent="end">
Expand Down Expand Up @@ -176,6 +189,14 @@ export function WorkspaceControls({
isDisabled={isOpenDisabled || isLoading}>
Reset
</MenuItem>
{isChangeOptionsEnabled && (
<MenuItem
icon={<Stack3D boxSize={4} />}
onClick={onChangeOptionsClicked}
isDisabled={isOpenDisabled || isLoading}>
Change Options
</MenuItem>
)}
{isShareEnabled && (
<MenuItem icon={<Icon as={HiShare} boxSize={4} />} onClick={handleShareClicked}>
Share
Expand Down

0 comments on commit 37e66ce

Please sign in to comment.