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:
Kyle Carberry
2023-06-21 18:23:59 -05:00
committed by GitHub
parent a28d422c35
commit 2a492b7008
31 changed files with 84 additions and 79 deletions

View File

@ -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 {

View File

@ -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">

View File

@ -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.
![Region picker in "Create workspace" screen](../../images/platforms/kubernetes/region-picker.png) ![Region picker in "Create Workspace" screen](../../images/platforms/kubernetes/region-picker.png)
## Option 1) Kubernetes contexts and kubeconfig ## Option 1) Kubernetes contexts and kubeconfig

View File

@ -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.

View File

@ -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
--- ---

View File

@ -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
--- ---

View File

@ -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",
} }

View File

@ -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()

View File

@ -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>
) )

View File

@ -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",
} }

View File

@ -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({})

View File

@ -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}

View File

@ -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,
}, },
) )
} }

View File

@ -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",

View File

@ -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}}.",

View File

@ -1,6 +1,6 @@
{ {
"actions": { "actions": {
"viewSourceCode": "View source code", "viewSourceCode": "View source code",
"useTemplate": "Use template" "useTemplate": "Use Template"
} }
} }

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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."
} }
} }

View File

@ -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"

View File

@ -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>

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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</>,
} }

View File

@ -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>

View File

@ -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>
. .
</> </>

View File

@ -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>

View File

@ -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}

View File

@ -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}