Files
coder/site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx
T

143 lines
4.6 KiB
TypeScript

import { useActor, useSelector } from "@xstate/react"
import { FeatureNames } from "api/types"
import dayjs from "dayjs"
import { useContext } from "react"
import { Helmet } from "react-helmet-async"
import { useTranslation } from "react-i18next"
import {
getMaxDeadline,
getMaxDeadlineChange,
getMinDeadline,
} from "util/schedule"
import { selectFeatureVisibility } from "xServices/entitlements/entitlementsSelectors"
import { StateFrom } from "xstate"
import { DeleteDialog } from "../../components/Dialogs/DeleteDialog/DeleteDialog"
import {
Workspace,
WorkspaceErrors,
} from "../../components/Workspace/Workspace"
import { pageTitle } from "../../util/page"
import { getFaviconByStatus } from "../../util/workspace"
import { XServiceContext } from "../../xServices/StateContext"
import {
WorkspaceEvent,
workspaceMachine,
} from "../../xServices/workspace/workspaceXService"
interface WorkspaceReadyPageProps {
workspaceState: StateFrom<typeof workspaceMachine>
workspaceSend: (event: WorkspaceEvent) => void
}
export const WorkspaceReadyPage = ({
workspaceState,
workspaceSend,
}: WorkspaceReadyPageProps): JSX.Element => {
const [bannerState, bannerSend] = useActor(
workspaceState.children["scheduleBannerMachine"],
)
const deadline = bannerState.context.deadline
const xServices = useContext(XServiceContext)
const featureVisibility = useSelector(
xServices.entitlementsXService,
selectFeatureVisibility,
)
const [buildInfoState] = useActor(xServices.buildInfoXService)
const {
workspace,
template,
refreshWorkspaceWarning,
builds,
getBuildsError,
buildError,
cancellationError,
applicationsHost,
permissions,
} = workspaceState.context
if (workspace === undefined) {
throw Error("Workspace is undefined")
}
const canUpdateWorkspace = Boolean(permissions?.updateWorkspace)
const { t } = useTranslation("workspacePage")
const favicon = getFaviconByStatus(workspace.latest_build)
return (
<>
<Helmet>
<title>{pageTitle(`${workspace.owner_name}/${workspace.name}`)}</title>
<link
rel="alternate icon"
type="image/png"
href={`/favicons/${favicon}.png`}
/>
<link
rel="icon"
type="image/svg+xml"
href={`/favicons/${favicon}.svg`}
/>
</Helmet>
<Workspace
scheduleProps={{
onDeadlineMinus: (hours: number) => {
bannerSend({
type: "DECREASE_DEADLINE",
hours,
})
},
onDeadlinePlus: (hours: number) => {
bannerSend({
type: "INCREASE_DEADLINE",
hours,
})
},
deadlineMinusEnabled: () => !bannerState.matches("atMinDeadline"),
deadlinePlusEnabled: () => !bannerState.matches("atMaxDeadline"),
maxDeadlineDecrease: deadline
? getMaxDeadlineChange(deadline, getMinDeadline())
: 0,
maxDeadlineIncrease:
deadline && template
? getMaxDeadlineChange(
getMaxDeadline(workspace, template),
deadline,
)
: 0,
}}
isUpdating={workspaceState.hasTag("updating")}
workspace={workspace}
handleStart={() => workspaceSend({ type: "START" })}
handleStop={() => workspaceSend({ type: "STOP" })}
handleDelete={() => workspaceSend({ type: "ASK_DELETE" })}
handleUpdate={() => workspaceSend({ type: "UPDATE" })}
handleCancel={() => workspaceSend({ type: "CANCEL" })}
resources={workspace.latest_build.resources}
builds={builds}
canUpdateWorkspace={canUpdateWorkspace}
hideSSHButton={featureVisibility[FeatureNames.BrowserOnly]}
workspaceErrors={{
[WorkspaceErrors.GET_RESOURCES_ERROR]: refreshWorkspaceWarning,
[WorkspaceErrors.GET_BUILDS_ERROR]: getBuildsError,
[WorkspaceErrors.BUILD_ERROR]: buildError,
[WorkspaceErrors.CANCELLATION_ERROR]: cancellationError,
}}
buildInfo={buildInfoState.context.buildInfo}
applicationsHost={applicationsHost}
template={template}
/>
<DeleteDialog
entity="workspace"
name={workspace.name}
info={t("deleteDialog.info", {
timeAgo: dayjs(workspace.created_at).fromNow(),
})}
isOpen={workspaceState.matches({ ready: { build: "askingDelete" } })}
onCancel={() => workspaceSend({ type: "CANCEL_DELETE" })}
onConfirm={() => {
workspaceSend({ type: "DELETE" })
}}
/>
</>
)
}