+ {resource.daily_cost > 0 && (
+
+
+ cost
+
+
+ {resource.daily_cost}
+
+
+ )}
{visibleMetadata.map((meta) => {
return (
diff --git a/site/src/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx
index 093c35e7e76c1..9247688be332e 100644
--- a/site/src/components/Workspace/Workspace.tsx
+++ b/site/src/components/Workspace/Workspace.tsx
@@ -55,6 +55,7 @@ export interface WorkspaceProps {
buildInfo?: TypesGen.BuildInfoResponse
applicationsHost?: string
template?: TypesGen.Template
+ quota_budget?: number
}
/**
@@ -77,6 +78,7 @@ export const Workspace: FC
> = ({
buildInfo,
applicationsHost,
template,
+ quota_budget,
}) => {
const { t } = useTranslation("workspacePage")
const styles = useStyles()
@@ -187,7 +189,11 @@ export const Workspace: FC> = ({
handleClick={() => navigate(`/templates`)}
/>
-
+
{isTransitioning !== undefined && isTransitioning && (
= (args) => (
-
-)
-
-export const Example = Template.bind({})
-Example.args = {
- quota: {
- user_workspace_count: 1,
- user_workspace_limit: 3,
- },
-}
-
-export const LimitOf1 = Template.bind({})
-LimitOf1.args = {
- quota: {
- user_workspace_count: 1,
- user_workspace_limit: 1,
- },
-}
-
-export const Loading = Template.bind({})
-Loading.args = {
- quota: undefined,
-}
-
-export const Error = Template.bind({})
-Error.args = {
- quota: undefined,
- error: {
- response: {
- data: {
- message: "Failed to fetch workspace quotas!",
- },
- },
- isAxiosError: true,
- },
-}
-
-export const Disabled = Template.bind({})
-Disabled.args = {
- quota: {
- user_workspace_count: 1,
- user_workspace_limit: 0,
- },
-}
diff --git a/site/src/components/WorkspaceQuota/WorkspaceQuota.tsx b/site/src/components/WorkspaceQuota/WorkspaceQuota.tsx
deleted file mode 100644
index 442c262f2ee04..0000000000000
--- a/site/src/components/WorkspaceQuota/WorkspaceQuota.tsx
+++ /dev/null
@@ -1,123 +0,0 @@
-import Box from "@material-ui/core/Box"
-import LinearProgress from "@material-ui/core/LinearProgress"
-import { makeStyles } from "@material-ui/core/styles"
-import Skeleton from "@material-ui/lab/Skeleton"
-import { AlertBanner } from "components/AlertBanner/AlertBanner"
-import { Stack } from "components/Stack/Stack"
-import { FC } from "react"
-import * as TypesGen from "../../api/typesGenerated"
-
-export const Language = {
- of: "of",
- workspace: "workspace",
- workspaces: "workspaces",
-}
-
-export interface WorkspaceQuotaProps {
- quota?: TypesGen.WorkspaceQuota
- error: Error | unknown
-}
-
-export const WorkspaceQuota: FC = ({ quota, error }) => {
- const styles = useStyles()
-
- // error state
- if (error !== undefined) {
- return (
-
-
- Usage Quota
-
-
-
- )
- }
-
- // loading
- if (quota === undefined) {
- return (
-
-
- Usage quota
-
-
-
-
-
-
- )
- }
-
- // don't show if limit is 0, this means the feature is disabled.
- if (quota.user_workspace_limit === 0) {
- return null
- }
-
- let value = Math.round(
- (quota.user_workspace_count / quota.user_workspace_limit) * 100,
- )
- // we don't want to round down to zero if the count is > 0
- if (quota.user_workspace_count > 0 && value === 0) {
- value = 1
- }
-
- return (
-
-
-
- Usage Quota
-
-
- {quota.user_workspace_count}
- {" "}
- {Language.of}{" "}
-
- {quota.user_workspace_limit}
- {" "}
- {quota.user_workspace_limit === 1
- ? Language.workspace
- : Language.workspaces}
- {" used"}
-
-
- = quota.user_workspace_limit
- ? styles.maxProgress
- : undefined
- }
- value={value}
- variant="determinate"
- />
-
-
- )
-}
-
-const useStyles = makeStyles((theme) => ({
- stack: {
- paddingTop: theme.spacing(2.5),
- },
- maxProgress: {
- "& .MuiLinearProgress-colorPrimary": {
- backgroundColor: theme.palette.error.main,
- },
- "& .MuiLinearProgress-barColorPrimary": {
- backgroundColor: theme.palette.error.main,
- },
- },
- title: {
- fontSize: 16,
- },
- label: {
- fontSize: 14,
- display: "block",
- color: theme.palette.text.secondary,
- },
- labelHighlight: {
- color: theme.palette.text.primary,
- },
- skeleton: {
- minWidth: "150px",
- },
-}))
diff --git a/site/src/components/WorkspaceStats/WorkspaceStats.tsx b/site/src/components/WorkspaceStats/WorkspaceStats.tsx
index 8fac5bede3b4c..b68bb33431e87 100644
--- a/site/src/components/WorkspaceStats/WorkspaceStats.tsx
+++ b/site/src/components/WorkspaceStats/WorkspaceStats.tsx
@@ -13,19 +13,22 @@ const Language = {
templateLabel: "Template",
statusLabel: "Workspace Status",
versionLabel: "Version",
- lastBuiltLabel: "Last Built",
+ lastBuiltLabel: "Last built",
outdated: "Outdated",
upToDate: "Up to date",
byLabel: "Last built by",
+ costLabel: "Daily cost",
}
export interface WorkspaceStatsProps {
workspace: Workspace
+ quota_budget?: number
handleUpdate: () => void
}
export const WorkspaceStats: FC = ({
workspace,
+ quota_budget,
handleUpdate,
}) => {
const styles = useStyles()
@@ -74,6 +77,14 @@ export const WorkspaceStats: FC = ({
{Language.byLabel}:
{initiatedBy}
+ {workspace.latest_build.daily_cost > 0 && (
+
+ {Language.costLabel}:
+
+ {workspace.latest_build.daily_cost} / {quota_budget}
+
+
+ )}
)
}
diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx
index abb522209ba7a..5a6d790ed61aa 100644
--- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx
+++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx
@@ -1,12 +1,10 @@
-import { shallowEqual, useActor, useMachine, useSelector } from "@xstate/react"
-import { FeatureNames } from "api/types"
+import { useActor, useMachine } from "@xstate/react"
import { useOrganizationId } from "hooks/useOrganizationId"
import { FC, useContext } from "react"
import { Helmet } from "react-helmet-async"
import { useNavigate, useParams } from "react-router-dom"
import { pageTitle } from "util/page"
import { createWorkspaceMachine } from "xServices/createWorkspace/createWorkspaceXService"
-import { selectFeatureVisibility } from "xServices/entitlements/entitlementsSelectors"
import { XServiceContext } from "xServices/StateContext"
import {
CreateWorkspaceErrors,
@@ -19,19 +17,12 @@ const CreateWorkspacePage: FC = () => {
const { template } = useParams()
const templateName = template ? template : ""
const navigate = useNavigate()
- const featureVisibility = useSelector(
- xServices.entitlementsXService,
- selectFeatureVisibility,
- shallowEqual,
- )
- const workspaceQuotaEnabled = featureVisibility[FeatureNames.WorkspaceQuota]
const [authState] = useActor(xServices.authXService)
const { me } = authState.context
const [createWorkspaceState, send] = useMachine(createWorkspaceMachine, {
context: {
organizationId,
templateName,
- workspaceQuotaEnabled,
owner: me ?? null,
},
actions: {
@@ -49,8 +40,6 @@ const CreateWorkspacePage: FC = () => {
getTemplatesError,
createWorkspaceError,
permissions,
- workspaceQuota,
- getWorkspaceQuotaError,
owner,
} = createWorkspaceState.context
@@ -70,14 +59,11 @@ const CreateWorkspacePage: FC = () => {
templates={templates}
selectedTemplate={selectedTemplate}
templateSchema={templateSchema}
- workspaceQuota={workspaceQuota}
createWorkspaceErrors={{
[CreateWorkspaceErrors.GET_TEMPLATES_ERROR]: getTemplatesError,
[CreateWorkspaceErrors.GET_TEMPLATE_SCHEMA_ERROR]:
getTemplateSchemaError,
[CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR]: createWorkspaceError,
- [CreateWorkspaceErrors.GET_WORKSPACE_QUOTA_ERROR]:
- getWorkspaceQuotaError,
}}
canCreateForUser={permissions?.createWorkspaceForUser}
owner={owner}
diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx
index 3a5a78e3c1ad9..610603f5fb35a 100644
--- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx
+++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx
@@ -4,7 +4,6 @@ import { FormFooter } from "components/FormFooter/FormFooter"
import { ParameterInput } from "components/ParameterInput/ParameterInput"
import { Stack } from "components/Stack/Stack"
import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete"
-import { WorkspaceQuota } from "components/WorkspaceQuota/WorkspaceQuota"
import { FormikContextType, FormikTouched, useFormik } from "formik"
import { i18n } from "i18n"
import { FC, useState } from "react"
@@ -20,7 +19,6 @@ export enum CreateWorkspaceErrors {
GET_TEMPLATES_ERROR = "getTemplatesError",
GET_TEMPLATE_SCHEMA_ERROR = "getTemplateSchemaError",
CREATE_WORKSPACE_ERROR = "createWorkspaceError",
- GET_WORKSPACE_QUOTA_ERROR = "getWorkspaceQuotaError",
}
export interface CreateWorkspacePageViewProps {
@@ -32,7 +30,6 @@ export interface CreateWorkspacePageViewProps {
templates?: TypesGen.Template[]
selectedTemplate?: TypesGen.Template
templateSchema?: TypesGen.ParameterSchema[]
- workspaceQuota?: TypesGen.WorkspaceQuota
createWorkspaceErrors: Partial