mirror of
https://github.com/coder/coder.git
synced 2025-07-13 21:36:50 +00:00
chore: improve the setup experience with various fixes (#8130)
* Move updatecheck logs to debug mode This was causing logs to be emitted immediately after initial setup, which seemed odd for the user. * Fix setup page text to say "Create" instead of "Setup" account * Improve copy on the empty workspaces page - This view can be seen after the user creates their first workspace, so saying first is bad. - It should describe what a workspace is, so I modified the description. - The create from template button wasn't helpful! * Improve the copy for the empty templates view - This didn't describe what a template actually is. - The title had the same problem as workspaces, where first makes no sense. * Improve text consistency on the Create Template page * Fix "View activity" displaying for non-Enterprise users This was causing an exception to be thrown. * Improve messaging of empty groups view * Appropriately capitalize Workspace and Template * Improve Docker template taglines * Fix types
This commit is contained in:
@ -145,10 +145,10 @@ func (c *Checker) start() {
|
|||||||
|
|
||||||
diff := time.Until(r.Checked.Add(c.opts.Interval))
|
diff := time.Until(r.Checked.Add(c.opts.Interval))
|
||||||
if diff > 0 {
|
if diff > 0 {
|
||||||
c.log.Info(c.ctx, "time until next update check", slog.F("duration", diff))
|
c.log.Debug(c.ctx, "time until next update check", slog.F("duration", diff))
|
||||||
t.Reset(diff)
|
t.Reset(diff)
|
||||||
} else {
|
} else {
|
||||||
c.log.Info(c.ctx, "time until next update check", slog.F("duration", c.opts.Interval))
|
c.log.Debug(c.ctx, "time until next update check", slog.F("duration", c.opts.Interval))
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -164,7 +164,7 @@ func (c *Checker) start() {
|
|||||||
c.notifyIfNewer(r, rr)
|
c.notifyIfNewer(r, rr)
|
||||||
r = rr
|
r = rr
|
||||||
}
|
}
|
||||||
c.log.Info(c.ctx, "time until next update check", slog.F("duration", c.opts.Interval))
|
c.log.Debug(c.ctx, "time until next update check", slog.F("duration", c.opts.Interval))
|
||||||
t.Reset(c.opts.Interval)
|
t.Reset(c.opts.Interval)
|
||||||
case <-c.ctx.Done():
|
case <-c.ctx.Done():
|
||||||
return
|
return
|
||||||
@ -176,7 +176,7 @@ func (c *Checker) update() (r Result, err error) {
|
|||||||
ctx, cancel := context.WithTimeout(c.ctx, c.opts.UpdateTimeout)
|
ctx, cancel := context.WithTimeout(c.ctx, c.opts.UpdateTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
c.log.Info(c.ctx, "checking for update")
|
c.log.Debug(c.ctx, "checking for update")
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.opts.URL, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.opts.URL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return r, xerrors.Errorf("new request: %w", err)
|
return r, xerrors.Errorf("new request: %w", err)
|
||||||
@ -203,7 +203,7 @@ func (c *Checker) update() (r Result, err error) {
|
|||||||
Version: rr.GetTagName(),
|
Version: rr.GetTagName(),
|
||||||
URL: rr.GetHTMLURL(),
|
URL: rr.GetHTMLURL(),
|
||||||
}
|
}
|
||||||
c.log.Info(ctx, "update check result", slog.F("latest_version", r.Version))
|
c.log.Debug(ctx, "update check result", slog.F("latest_version", r.Version))
|
||||||
|
|
||||||
b, err := json.Marshal(r)
|
b, err := json.Marshal(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -54,7 +54,7 @@ Coder with Docker has the following advantages:
|
|||||||
|
|
||||||
<img src="../images/platforms/docker/login.png">
|
<img src="../images/platforms/docker/login.png">
|
||||||
|
|
||||||
Then navigate to `Templates > docker > Create workspace`
|
Then navigate to `Templates > docker > Create Workspace`
|
||||||
|
|
||||||
<img src="../images/platforms/docker/create-workspace.png">
|
<img src="../images/platforms/docker/create-workspace.png">
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
With Coder, you can deploy workspaces in additional Kubernetes clusters using different [authentication methods](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs#authentication) in the Terraform provider.
|
With Coder, you can deploy workspaces in additional Kubernetes clusters using different [authentication methods](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs#authentication) in the Terraform provider.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Option 1) Kubernetes contexts and kubeconfig
|
## Option 1) Kubernetes contexts and kubeconfig
|
||||||
|
|
||||||
|
2
docs/templates/open-in-coder.md
vendored
2
docs/templates/open-in-coder.md
vendored
@ -88,7 +88,7 @@ To support any infrastructure and software stack, Coder provides a generic appro
|
|||||||
|
|
||||||
> Be sure to replace `YOUR_ACCESS_URL` with your Coder access url (e.g. https://coder.example.com) and `YOUR_TEMPLATE` with the name of your template.
|
> Be sure to replace `YOUR_ACCESS_URL` with your Coder access url (e.g. https://coder.example.com) and `YOUR_TEMPLATE` with the name of your template.
|
||||||
|
|
||||||
1. Optional: pre-fill parameter values in the "Create workspace" page
|
1. Optional: pre-fill parameter values in the "Create Workspace" page
|
||||||
|
|
||||||
This can be used to pre-fill the git repo URL, disk size, image, etc.
|
This can be used to pre-fill the git repo URL, disk size, image, etc.
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Develop in Docker with a dotfiles URL
|
name: Develop in Docker with a dotfiles URL
|
||||||
description: Run workspaces on a Docker host using registry images
|
description: Develop inside Docker containers using your local daemon
|
||||||
tags: [local, docker]
|
tags: [local, docker]
|
||||||
icon: /icon/docker.png
|
icon: /icon/docker.png
|
||||||
---
|
---
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Develop in Docker
|
name: Develop in Docker
|
||||||
description: Run workspaces on a Docker host using registry images
|
description: Develop inside Docker containers using your local daemon
|
||||||
tags: [local, docker]
|
tags: [local, docker]
|
||||||
icon: /icon/docker.png
|
icon: /icon/docker.png
|
||||||
---
|
---
|
||||||
|
@ -2,12 +2,12 @@ import { Page } from "@playwright/test"
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
||||||
export const buttons = {
|
export const buttons = {
|
||||||
starterTemplates: "Starter templates",
|
starterTemplates: "Starter Templates",
|
||||||
dockerTemplate: "Develop in Docker",
|
dockerTemplate: "Develop in Docker",
|
||||||
useTemplate: "Use template",
|
useTemplate: "Create Workspace",
|
||||||
createTemplate: "Create template",
|
createTemplate: "Create Template",
|
||||||
createWorkspace: "Create workspace",
|
createWorkspace: "Create Workspace",
|
||||||
submitCreateWorkspace: "Create workspace",
|
submitCreateWorkspace: "Create Workspace",
|
||||||
stopWorkspace: "Stop",
|
stopWorkspace: "Stop",
|
||||||
startWorkspace: "Start",
|
startWorkspace: "Start",
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import { MouseEvent, useState } from "react"
|
|||||||
export interface TableRowMenuProps<TData> {
|
export interface TableRowMenuProps<TData> {
|
||||||
data: TData
|
data: TData
|
||||||
menuItems: Array<{
|
menuItems: Array<{
|
||||||
label: string
|
label: React.ReactNode
|
||||||
disabled: boolean
|
disabled: boolean
|
||||||
onClick: (data: TData) => void
|
onClick: (data: TData) => void
|
||||||
}>
|
}>
|
||||||
@ -45,9 +45,9 @@ export const TableRowMenu = <T,>({
|
|||||||
open={Boolean(anchorEl)}
|
open={Boolean(anchorEl)}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
>
|
>
|
||||||
{menuItems.map((item) => (
|
{menuItems.map((item, index) => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={item.label}
|
key={index}
|
||||||
disabled={item.disabled}
|
disabled={item.disabled}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleClose()
|
handleClose()
|
||||||
|
@ -106,7 +106,7 @@ const CreateWorkspaceButton: FC<{
|
|||||||
component={RouterLink}
|
component={RouterLink}
|
||||||
to={`/templates/${templateName}/workspace`}
|
to={`/templates/${templateName}/workspace`}
|
||||||
>
|
>
|
||||||
Create workspace
|
Create Workspace
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ const Language = {
|
|||||||
workspaceTooltipTitle: "What is a workspace?",
|
workspaceTooltipTitle: "What is a workspace?",
|
||||||
workspaceTooltipText:
|
workspaceTooltipText:
|
||||||
"A workspace is your development environment in the cloud. It includes the infrastructure and tools you need to work on your project.",
|
"A workspace is your development environment in the cloud. It includes the infrastructure and tools you need to work on your project.",
|
||||||
workspaceTooltipLink1: "Create workspaces",
|
workspaceTooltipLink1: "Create Workspaces",
|
||||||
workspaceTooltipLink2: "Connect with SSH",
|
workspaceTooltipLink2: "Connect with SSH",
|
||||||
workspaceTooltipLink3: "Editors and IDEs",
|
workspaceTooltipLink3: "Editors and IDEs",
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ Editable.args = {
|
|||||||
users: [MockUser, MockUser2],
|
users: [MockUser, MockUser2],
|
||||||
roles: MockAssignableSiteRoles,
|
roles: MockAssignableSiteRoles,
|
||||||
canEditUsers: true,
|
canEditUsers: true,
|
||||||
|
canViewActivity: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Empty = Template.bind({})
|
export const Empty = Template.bind({})
|
||||||
|
@ -22,6 +22,7 @@ export interface UsersTableProps {
|
|||||||
roles?: TypesGen.AssignableRoles[]
|
roles?: TypesGen.AssignableRoles[]
|
||||||
isUpdatingUserRoles?: boolean
|
isUpdatingUserRoles?: boolean
|
||||||
canEditUsers?: boolean
|
canEditUsers?: boolean
|
||||||
|
canViewActivity?: boolean
|
||||||
isLoading?: boolean
|
isLoading?: boolean
|
||||||
onSuspendUser: (user: TypesGen.User) => void
|
onSuspendUser: (user: TypesGen.User) => void
|
||||||
onActivateUser: (user: TypesGen.User) => void
|
onActivateUser: (user: TypesGen.User) => void
|
||||||
@ -49,6 +50,7 @@ export const UsersTable: FC<React.PropsWithChildren<UsersTableProps>> = ({
|
|||||||
onUpdateUserRoles,
|
onUpdateUserRoles,
|
||||||
isUpdatingUserRoles,
|
isUpdatingUserRoles,
|
||||||
canEditUsers,
|
canEditUsers,
|
||||||
|
canViewActivity,
|
||||||
isLoading,
|
isLoading,
|
||||||
isNonInitialPage,
|
isNonInitialPage,
|
||||||
actorID,
|
actorID,
|
||||||
@ -78,6 +80,7 @@ export const UsersTable: FC<React.PropsWithChildren<UsersTableProps>> = ({
|
|||||||
roles={roles}
|
roles={roles}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
canEditUsers={canEditUsers}
|
canEditUsers={canEditUsers}
|
||||||
|
canViewActivity={canViewActivity}
|
||||||
isUpdatingUserRoles={isUpdatingUserRoles}
|
isUpdatingUserRoles={isUpdatingUserRoles}
|
||||||
onActivateUser={onActivateUser}
|
onActivateUser={onActivateUser}
|
||||||
onDeleteUser={onDeleteUser}
|
onDeleteUser={onDeleteUser}
|
||||||
|
@ -15,6 +15,7 @@ import { TableLoaderSkeleton } from "../TableLoader/TableLoader"
|
|||||||
import { TableRowMenu } from "../TableRowMenu/TableRowMenu"
|
import { TableRowMenu } from "../TableRowMenu/TableRowMenu"
|
||||||
import { EditRolesButton } from "components/EditRolesButton/EditRolesButton"
|
import { EditRolesButton } from "components/EditRolesButton/EditRolesButton"
|
||||||
import { Stack } from "components/Stack/Stack"
|
import { Stack } from "components/Stack/Stack"
|
||||||
|
import { EnterpriseBadge } from "components/DeploySettingsLayout/Badges"
|
||||||
|
|
||||||
const isOwnerRole = (role: TypesGen.Role): boolean => {
|
const isOwnerRole = (role: TypesGen.Role): boolean => {
|
||||||
return role.name === "owner"
|
return role.name === "owner"
|
||||||
@ -34,6 +35,7 @@ interface UsersTableBodyProps {
|
|||||||
isUpdatingUserRoles?: boolean
|
isUpdatingUserRoles?: boolean
|
||||||
canEditUsers?: boolean
|
canEditUsers?: boolean
|
||||||
isLoading?: boolean
|
isLoading?: boolean
|
||||||
|
canViewActivity?: boolean
|
||||||
onSuspendUser: (user: TypesGen.User) => void
|
onSuspendUser: (user: TypesGen.User) => void
|
||||||
onDeleteUser: (user: TypesGen.User) => void
|
onDeleteUser: (user: TypesGen.User) => void
|
||||||
onListWorkspaces: (user: TypesGen.User) => void
|
onListWorkspaces: (user: TypesGen.User) => void
|
||||||
@ -62,6 +64,7 @@ export const UsersTableBody: FC<
|
|||||||
onUpdateUserRoles,
|
onUpdateUserRoles,
|
||||||
isUpdatingUserRoles,
|
isUpdatingUserRoles,
|
||||||
canEditUsers,
|
canEditUsers,
|
||||||
|
canViewActivity,
|
||||||
isLoading,
|
isLoading,
|
||||||
isNonInitialPage,
|
isNonInitialPage,
|
||||||
actorID,
|
actorID,
|
||||||
@ -167,14 +170,18 @@ export const UsersTableBody: FC<
|
|||||||
(user.status === "active"
|
(user.status === "active"
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
label: t("suspendMenuItem"),
|
label: t(
|
||||||
|
"suspendMenuItem",
|
||||||
|
) as React.ReactNode,
|
||||||
onClick: onSuspendUser,
|
onClick: onSuspendUser,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
label: t("activateMenuItem"),
|
label: t(
|
||||||
|
"activateMenuItem",
|
||||||
|
) as React.ReactNode,
|
||||||
onClick: onActivateUser,
|
onClick: onActivateUser,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
@ -196,9 +203,14 @@ export const UsersTableBody: FC<
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "View activity",
|
label: (
|
||||||
|
<>
|
||||||
|
View activity
|
||||||
|
{!canViewActivity && <EnterpriseBadge />}
|
||||||
|
</>
|
||||||
|
),
|
||||||
onClick: onViewActivity,
|
onClick: onViewActivity,
|
||||||
disabled: false,
|
disabled: !canViewActivity,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
"title": "Create Template",
|
"title": "Create Template",
|
||||||
"form": {
|
"form": {
|
||||||
"generalInfo": {
|
"generalInfo": {
|
||||||
"title": "General info",
|
"title": "General",
|
||||||
"description": "The name is used to identify the template in URLs and the API. It must be unique within your organization."
|
"description": "The name is used to identify the template in URLs and the API."
|
||||||
},
|
},
|
||||||
"displayInfo": {
|
"displayInfo": {
|
||||||
"title": "Display info",
|
"title": "Display",
|
||||||
"description": "Give your template a friendly name, description, and icon."
|
"description": "A friendly name, description, and icon to help developers identify your template."
|
||||||
},
|
},
|
||||||
"schedule": {
|
"schedule": {
|
||||||
"title": "Schedule",
|
"title": "Schedule",
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"templateLabel": "Template",
|
"templateLabel": "Template",
|
||||||
"nameLabel": "Workspace Name",
|
"nameLabel": "Workspace Name",
|
||||||
"ownerLabel": "Owner",
|
"ownerLabel": "Owner",
|
||||||
"createWorkspace": "Create workspace",
|
"createWorkspace": "Create Workspace",
|
||||||
"validationNumberLesserThan": "Value must be greater than {{min}}.",
|
"validationNumberLesserThan": "Value must be greater than {{min}}.",
|
||||||
"validationNumberGreaterThan": "Value must be lesser than {{max}}.",
|
"validationNumberGreaterThan": "Value must be lesser than {{max}}.",
|
||||||
"validationNumberNotInRange": "Value must be between {{min}} and {{max}}.",
|
"validationNumberNotInRange": "Value must be between {{min}} and {{max}}.",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"actions": {
|
"actions": {
|
||||||
"viewSourceCode": "View source code",
|
"viewSourceCode": "View source code",
|
||||||
"useTemplate": "Use template"
|
"useTemplate": "Use Template"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"title": "Starter Templates",
|
"title": "Starter Templates",
|
||||||
"subtitle": "Pick one of the built-in templates to start using Coder",
|
"subtitle": "Import a built-in template to start developing in the cloud",
|
||||||
"filterCaption": "Filter",
|
"filterCaption": "Filter",
|
||||||
"tags": {
|
"tags": {
|
||||||
"all": "All templates",
|
"all": "All templates",
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"generalInfo": {
|
"generalInfo": {
|
||||||
"title": "General info",
|
"title": "General",
|
||||||
"description": "The name is used to identify the template in URLs and the API. It must be unique."
|
"description": "The name is used to identify the template in URLs and the API. It must be unique."
|
||||||
},
|
},
|
||||||
"displayInfo": {
|
"displayInfo": {
|
||||||
"title": "Display info",
|
"title": "Display",
|
||||||
"description": "Give your template a friendly name, description, and icon."
|
"description": "A friendly name, description, and icon to help developers identify your template."
|
||||||
},
|
},
|
||||||
"schedule": {
|
"schedule": {
|
||||||
"title": "Schedule",
|
"title": "Schedule",
|
||||||
|
@ -33,11 +33,11 @@
|
|||||||
"allowUsersCancelHelperText": "If checked, users may be able to corrupt their workspace.",
|
"allowUsersCancelHelperText": "If checked, users may be able to corrupt their workspace.",
|
||||||
"generalInfo": {
|
"generalInfo": {
|
||||||
"title": "General info",
|
"title": "General info",
|
||||||
"description": "The name is used to identify the template in URLs and the API. It must be unique within your organization."
|
"description": "The name is used to identify the template in URLs and the API."
|
||||||
},
|
},
|
||||||
"displayInfo": {
|
"displayInfo": {
|
||||||
"title": "Display info",
|
"title": "Display info",
|
||||||
"description": "Give your template a friendly name, description, and icon."
|
"description": "A friendly name, description, and icon to help developers identify your template."
|
||||||
},
|
},
|
||||||
"schedule": {
|
"schedule": {
|
||||||
"title": "Schedule",
|
"title": "Schedule",
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"getTemplatesError": "Something went wrong fetching templates."
|
"getTemplatesError": "Something went wrong fetching templates."
|
||||||
},
|
},
|
||||||
"empty": {
|
"empty": {
|
||||||
"message": "Create your first template",
|
"message": "Create a Template",
|
||||||
"descriptionWithoutPermissions": "Contact your Coder administrator to create a template. You can share the code below."
|
"descriptionWithoutPermissions": "Contact your Coder administrator to create a template. You can share the code below."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"emptyCreateWorkspaceMessage": "Create your first workspace",
|
"emptyCreateWorkspaceMessage": "Create a Workspace",
|
||||||
"emptyCreateWorkspaceDescription": "Start editing your source code and building your software",
|
"emptyCreateWorkspaceDescription": "A workspace is your personal, customizable development environment in the cloud",
|
||||||
"createFromTemplateButton": "Create from template",
|
"createFromTemplateButton": "Select a Template",
|
||||||
"emptyResultsMessage": "No results matched your search",
|
"emptyResultsMessage": "No results matched your search",
|
||||||
"emptyPageMessage": "No results on this page",
|
"emptyPageMessage": "No results on this page",
|
||||||
"updateVersionError": "Error on update workspace version"
|
"updateVersionError": "Error on update workspace version"
|
||||||
|
@ -223,8 +223,8 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = ({
|
|||||||
<HorizontalForm onSubmit={form.handleSubmit}>
|
<HorizontalForm onSubmit={form.handleSubmit}>
|
||||||
{/* General info */}
|
{/* General info */}
|
||||||
<FormSection
|
<FormSection
|
||||||
title="General info"
|
title="General"
|
||||||
description="The name is used to identify the template in URLs and the API. It must be unique within your organization."
|
description="The name is used to identify the template in URLs and the API."
|
||||||
>
|
>
|
||||||
<FormFields>
|
<FormFields>
|
||||||
{starterTemplate ? (
|
{starterTemplate ? (
|
||||||
@ -255,8 +255,8 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = ({
|
|||||||
|
|
||||||
{/* Display info */}
|
{/* Display info */}
|
||||||
<FormSection
|
<FormSection
|
||||||
title="Display info"
|
title="Display"
|
||||||
description="Give your template a friendly name, description, and icon."
|
description="A friendly name, description, and icon to help developers identify your template."
|
||||||
>
|
>
|
||||||
<FormFields>
|
<FormFields>
|
||||||
<TextField
|
<TextField
|
||||||
@ -349,7 +349,7 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = ({
|
|||||||
/>
|
/>
|
||||||
<Stack spacing={0.5}>
|
<Stack spacing={0.5}>
|
||||||
<strong>
|
<strong>
|
||||||
Allow users to autostart workspaces on a schedule.
|
Allow users to automatically start workspaces on a schedule.
|
||||||
</strong>
|
</strong>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
@ -141,7 +141,7 @@ describe("CreateWorkspacePage", () => {
|
|||||||
renderCreateWorkspacePage()
|
renderCreateWorkspacePage()
|
||||||
await waitForLoaderToBeRemoved()
|
await waitForLoaderToBeRemoved()
|
||||||
|
|
||||||
const element = await screen.findByText("Create workspace")
|
const element = await screen.findByText("Create Workspace")
|
||||||
expect(element).toBeDefined()
|
expect(element).toBeDefined()
|
||||||
const secondParameter = await screen.findByText(
|
const secondParameter = await screen.findByText(
|
||||||
MockTemplateVersionParameter2.description,
|
MockTemplateVersionParameter2.description,
|
||||||
|
@ -184,7 +184,7 @@ export const CreateWorkspacePageView: FC<
|
|||||||
|
|
||||||
{/* General info */}
|
{/* General info */}
|
||||||
<FormSection
|
<FormSection
|
||||||
title="General info"
|
title="General"
|
||||||
description="The template and name of your new workspace."
|
description="The template and name of your new workspace."
|
||||||
>
|
>
|
||||||
<FormFields>
|
<FormFields>
|
||||||
@ -206,8 +206,8 @@ export const CreateWorkspacePageView: FC<
|
|||||||
{/* Workspace owner */}
|
{/* Workspace owner */}
|
||||||
{props.canCreateForUser && (
|
{props.canCreateForUser && (
|
||||||
<FormSection
|
<FormSection
|
||||||
title="Workspace owner"
|
title="Workspace Owner"
|
||||||
description="The user that is going to own this workspace. If you are admin, you can create workspace for others."
|
description="Only admins can create workspace for other users."
|
||||||
>
|
>
|
||||||
<FormFields>
|
<FormFields>
|
||||||
<UserAutocomplete
|
<UserAutocomplete
|
||||||
|
@ -44,8 +44,8 @@ export const GroupsPageView: FC<GroupsPageViewProps> = ({
|
|||||||
<ChooseOne>
|
<ChooseOne>
|
||||||
<Cond condition={!isTemplateRBACEnabled}>
|
<Cond condition={!isTemplateRBACEnabled}>
|
||||||
<Paywall
|
<Paywall
|
||||||
message="User groups"
|
message="Groups"
|
||||||
description="Organize the users into groups and manage their permissions. To use this feature, you have to upgrade your account."
|
description="Organize users into groups with restricted access to templates. You need an Enterprise license to use this feature."
|
||||||
cta={
|
cta={
|
||||||
<Stack direction="row" alignItems="center">
|
<Stack direction="row" alignItems="center">
|
||||||
<Button
|
<Button
|
||||||
@ -55,7 +55,7 @@ export const GroupsPageView: FC<GroupsPageViewProps> = ({
|
|||||||
startIcon={<ArrowRightAltOutlined />}
|
startIcon={<ArrowRightAltOutlined />}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
See how to upgrade
|
Learn about Enterprise
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
|
@ -19,7 +19,7 @@ export const Language = {
|
|||||||
emailInvalid: "Please enter a valid email address.",
|
emailInvalid: "Please enter a valid email address.",
|
||||||
emailRequired: "Please enter an email address.",
|
emailRequired: "Please enter an email address.",
|
||||||
passwordRequired: "Please enter a password.",
|
passwordRequired: "Please enter a password.",
|
||||||
create: "Setup account",
|
create: "Create account",
|
||||||
welcomeMessage: <>Welcome to Coder</>,
|
welcomeMessage: <>Welcome to Coder</>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,7 +259,7 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
|||||||
/>
|
/>
|
||||||
<Stack spacing={0.5}>
|
<Stack spacing={0.5}>
|
||||||
<strong>
|
<strong>
|
||||||
Allow users to autostart workspaces on a schedule.
|
Allow users to automatically start workspaces on a schedule.
|
||||||
</strong>
|
</strong>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
@ -50,17 +50,15 @@ export const EmptyTemplates: FC<{
|
|||||||
message={t("empty.message")}
|
message={t("empty.message")}
|
||||||
description={
|
description={
|
||||||
<>
|
<>
|
||||||
You can create a template using our starter templates or{" "}
|
Templates are written in Terraform and describe the infrastructure
|
||||||
<Link component={RouterLink} to="/new">
|
for workspaces (e.g., docker_container, aws_instance,
|
||||||
uploading a template
|
kubernetes_pod). Select a starter template below or
|
||||||
</Link>
|
|
||||||
. You can also{" "}
|
|
||||||
<Link
|
<Link
|
||||||
href="https://coder.com/docs/coder-oss/latest/templates#add-a-template"
|
href="https://coder.com/docs/coder-oss/latest/templates#add-a-template"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
use the CLI
|
create your own
|
||||||
</Link>
|
</Link>
|
||||||
.
|
.
|
||||||
</>
|
</>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import Button from "@mui/material/Button"
|
import Button from "@mui/material/Button"
|
||||||
import Link from "@mui/material/Link"
|
|
||||||
import { makeStyles } from "@mui/styles"
|
import { makeStyles } from "@mui/styles"
|
||||||
import Table from "@mui/material/Table"
|
import Table from "@mui/material/Table"
|
||||||
import TableBody from "@mui/material/TableBody"
|
import TableBody from "@mui/material/TableBody"
|
||||||
@ -127,7 +126,7 @@ const TemplateRow: FC<{ template: Template }> = ({ template }) => {
|
|||||||
navigate(`/templates/${template.name}/workspace`)
|
navigate(`/templates/${template.name}/workspace`)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Use template
|
Create Workspace
|
||||||
</Button>
|
</Button>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@ -151,7 +150,7 @@ export const TemplatesPageView: FC<
|
|||||||
actions={
|
actions={
|
||||||
<Maybe condition={permissions.createTemplates}>
|
<Maybe condition={permissions.createTemplates}>
|
||||||
<Button component={RouterLink} to="/starter-templates">
|
<Button component={RouterLink} to="/starter-templates">
|
||||||
Starter templates
|
Starter Templates
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<AddIcon />}
|
startIcon={<AddIcon />}
|
||||||
@ -159,7 +158,7 @@ export const TemplatesPageView: FC<
|
|||||||
to="new"
|
to="new"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
Add template
|
Create Template
|
||||||
</Button>
|
</Button>
|
||||||
</Maybe>
|
</Maybe>
|
||||||
}
|
}
|
||||||
@ -172,21 +171,7 @@ export const TemplatesPageView: FC<
|
|||||||
</PageHeaderTitle>
|
</PageHeaderTitle>
|
||||||
<Maybe condition={Boolean(templates && templates.length > 0)}>
|
<Maybe condition={Boolean(templates && templates.length > 0)}>
|
||||||
<PageHeaderSubtitle>
|
<PageHeaderSubtitle>
|
||||||
Choose a template to create a new workspace
|
Select a template to create a workspace.
|
||||||
{permissions.createTemplates ? (
|
|
||||||
<>
|
|
||||||
, or{" "}
|
|
||||||
<Link
|
|
||||||
href="https://coder.com/docs/coder-oss/latest/templates#add-a-template"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
manage templates
|
|
||||||
</Link>{" "}
|
|
||||||
from the CLI.
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
"."
|
|
||||||
)}
|
|
||||||
</PageHeaderSubtitle>
|
</PageHeaderSubtitle>
|
||||||
</Maybe>
|
</Maybe>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
@ -19,6 +19,7 @@ import { pageTitle } from "../../utils/page"
|
|||||||
import { UsersPageView } from "./UsersPageView"
|
import { UsersPageView } from "./UsersPageView"
|
||||||
import { useStatusFilterMenu } from "./UsersFilter"
|
import { useStatusFilterMenu } from "./UsersFilter"
|
||||||
import { useFilter } from "components/Filter/filter"
|
import { useFilter } from "components/Filter/filter"
|
||||||
|
import { useDashboard } from "components/Dashboard/DashboardProvider"
|
||||||
|
|
||||||
export const Language = {
|
export const Language = {
|
||||||
suspendDialogTitle: "Suspend user",
|
suspendDialogTitle: "Suspend user",
|
||||||
@ -35,6 +36,7 @@ const getSelectedUser = (id: string, users?: User[]) =>
|
|||||||
export const UsersPage: FC<{ children?: ReactNode }> = () => {
|
export const UsersPage: FC<{ children?: ReactNode }> = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const searchParamsResult = useSearchParams()
|
const searchParamsResult = useSearchParams()
|
||||||
|
const { entitlements } = useDashboard()
|
||||||
const [searchParams, setSearchParams] = searchParamsResult
|
const [searchParams, setSearchParams] = searchParamsResult
|
||||||
const filter = searchParams.get("filter") ?? ""
|
const filter = searchParams.get("filter") ?? ""
|
||||||
const [usersState, usersSend] = useMachine(usersMachine, {
|
const [usersState, usersSend] = useMachine(usersMachine, {
|
||||||
@ -148,6 +150,7 @@ export const UsersPage: FC<{ children?: ReactNode }> = () => {
|
|||||||
isUpdatingUserRoles={usersState.matches("updatingUserRoles")}
|
isUpdatingUserRoles={usersState.matches("updatingUserRoles")}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
canEditUsers={canEditUsers}
|
canEditUsers={canEditUsers}
|
||||||
|
canViewActivity={entitlements.features.audit_log.enabled}
|
||||||
paginationRef={paginationRef}
|
paginationRef={paginationRef}
|
||||||
isNonInitialPage={nonInitialPage(searchParams)}
|
isNonInitialPage={nonInitialPage(searchParams)}
|
||||||
actorID={me.id}
|
actorID={me.id}
|
||||||
|
@ -16,6 +16,7 @@ export interface UsersPageViewProps {
|
|||||||
roles?: TypesGen.AssignableRoles[]
|
roles?: TypesGen.AssignableRoles[]
|
||||||
isUpdatingUserRoles?: boolean
|
isUpdatingUserRoles?: boolean
|
||||||
canEditUsers?: boolean
|
canEditUsers?: boolean
|
||||||
|
canViewActivity?: boolean
|
||||||
isLoading?: boolean
|
isLoading?: boolean
|
||||||
onSuspendUser: (user: TypesGen.User) => void
|
onSuspendUser: (user: TypesGen.User) => void
|
||||||
onDeleteUser: (user: TypesGen.User) => void
|
onDeleteUser: (user: TypesGen.User) => void
|
||||||
@ -46,6 +47,7 @@ export const UsersPageView: FC<React.PropsWithChildren<UsersPageViewProps>> = ({
|
|||||||
onUpdateUserRoles,
|
onUpdateUserRoles,
|
||||||
isUpdatingUserRoles,
|
isUpdatingUserRoles,
|
||||||
canEditUsers,
|
canEditUsers,
|
||||||
|
canViewActivity,
|
||||||
isLoading,
|
isLoading,
|
||||||
filterProps,
|
filterProps,
|
||||||
paginationRef,
|
paginationRef,
|
||||||
@ -75,6 +77,7 @@ export const UsersPageView: FC<React.PropsWithChildren<UsersPageViewProps>> = ({
|
|||||||
onUpdateUserRoles={onUpdateUserRoles}
|
onUpdateUserRoles={onUpdateUserRoles}
|
||||||
isUpdatingUserRoles={isUpdatingUserRoles}
|
isUpdatingUserRoles={isUpdatingUserRoles}
|
||||||
canEditUsers={canEditUsers}
|
canEditUsers={canEditUsers}
|
||||||
|
canViewActivity={canViewActivity}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
isNonInitialPage={isNonInitialPage}
|
isNonInitialPage={isNonInitialPage}
|
||||||
actorID={actorID}
|
actorID={actorID}
|
||||||
|
Reference in New Issue
Block a user