Skip to content

feat(site): make workspace batch deletion GA #9313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions codersdk/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -1939,9 +1939,6 @@ const (
// Deployment health page
ExperimentDeploymentHealthPage Experiment = "deployment_health_page"

// Workspaces batch actions
ExperimentWorkspacesBatchActions Experiment = "workspaces_batch_actions"

// Add new experiments here!
// ExperimentExample Experiment = "example"
)
Expand All @@ -1952,7 +1949,6 @@ const (
// not be included here and will be essentially hidden.
var ExperimentsAll = Experiments{
ExperimentDeploymentHealthPage,
ExperimentWorkspacesBatchActions,
}

// Experiments is a list of experiments that are enabled for the deployment.
Expand Down
1 change: 0 additions & 1 deletion docs/api/schemas.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions site/src/api/typesGenerated.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 20 additions & 30 deletions site/src/components/TableLoader/TableLoader.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { makeStyles } from "@mui/styles"
import TableCell from "@mui/material/TableCell"
import TableRow from "@mui/material/TableRow"
import Skeleton from "@mui/material/Skeleton"
import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton"
import { FC } from "react"
import TableRow, { TableRowProps } from "@mui/material/TableRow"
import { FC, ReactNode, cloneElement, isValidElement } from "react"
import { Loader } from "../Loader/Loader"

export const TableLoader: FC = () => {
Expand All @@ -25,35 +23,27 @@ const useStyles = makeStyles((theme) => ({
},
}))

export const TableLoaderSkeleton: FC<{
columns: number
export const TableLoaderSkeleton = ({
rows = 4,
children,
}: {
rows?: number
useAvatarData?: boolean
}> = ({ columns, rows = 4, useAvatarData = false }) => {
const placeholderColumns = Array(columns).fill(undefined)
const placeholderRows = Array(rows).fill(undefined)

children: ReactNode
}) => {
if (!isValidElement(children)) {
throw new Error(
"TableLoaderSkeleton children must be a valid React element",
)
}
return (
<>
{placeholderRows.map((_, rowIndex) => (
<TableRow key={rowIndex} role="progressbar" data-testid="loader">
{placeholderColumns.map((_, columnIndex) => {
if (useAvatarData && columnIndex === 0) {
return (
<TableCell key={columnIndex}>
<AvatarDataSkeleton />
</TableCell>
)
}

return (
<TableCell key={columnIndex}>
<Skeleton variant="text" width="25%" />
</TableCell>
)
})}
</TableRow>
))}
{Array.from({ length: rows }, (_, i) =>
cloneElement(children, { key: i }),
)}
</>
)
}

export const TableRowSkeleton = (props: TableRowProps) => {
return <TableRow role="progressbar" data-testid="loader" {...props} />
}
35 changes: 33 additions & 2 deletions site/src/components/UsersTable/UsersTableBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ import * as TypesGen from "../../api/typesGenerated"
import { combineClasses } from "../../utils/combineClasses"
import { AvatarData } from "../AvatarData/AvatarData"
import { EmptyState } from "../EmptyState/EmptyState"
import { TableLoaderSkeleton } from "../TableLoader/TableLoader"
import {
TableLoaderSkeleton,
TableRowSkeleton,
} from "../TableLoader/TableLoader"
import { TableRowMenu } from "../TableRowMenu/TableRowMenu"
import { EditRolesButton } from "components/EditRolesButton/EditRolesButton"
import { Stack } from "components/Stack/Stack"
import { EnterpriseBadge } from "components/DeploySettingsLayout/Badges"
import Skeleton from "@mui/material/Skeleton"
import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton"

const isOwnerRole = (role: TypesGen.Role): boolean => {
return role.name === "owner"
Expand Down Expand Up @@ -80,7 +85,7 @@ export const UsersTableBody: FC<
return (
<ChooseOne>
<Cond condition={Boolean(isLoading)}>
<TableLoaderSkeleton columns={5} useAvatarData />
<TableLoader />
</Cond>
<Cond condition={!users || users.length === 0}>
<ChooseOne>
Expand Down Expand Up @@ -252,3 +257,29 @@ const useStyles = makeStyles((theme) => ({
borderColor: theme.palette.info.light,
},
}))

const TableLoader = () => {
return (
<TableLoaderSkeleton>
<TableRowSkeleton>
<TableCell>
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
<AvatarDataSkeleton />
</Box>
</TableCell>
<TableCell>
<Skeleton variant="text" width="25%" />
</TableCell>
<TableCell>
<Skeleton variant="text" width="25%" />
</TableCell>
<TableCell>
<Skeleton variant="text" width="25%" />
</TableCell>
<TableCell>
<Skeleton variant="text" width="25%" />
</TableCell>
</TableRowSkeleton>
</TableLoaderSkeleton>
)
}
30 changes: 28 additions & 2 deletions site/src/pages/GroupsPage/GroupsPageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@ import { AvatarData } from "components/AvatarData/AvatarData"
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"
import { EmptyState } from "components/EmptyState/EmptyState"
import { Stack } from "components/Stack/Stack"
import { TableLoaderSkeleton } from "components/TableLoader/TableLoader"
import {
TableLoaderSkeleton,
TableRowSkeleton,
} from "components/TableLoader/TableLoader"
import { UserAvatar } from "components/UserAvatar/UserAvatar"
import { FC } from "react"
import { Link as RouterLink, useNavigate } from "react-router-dom"
import { Paywall } from "components/Paywall/Paywall"
import { Group } from "api/typesGenerated"
import { GroupAvatar } from "components/GroupAvatar/GroupAvatar"
import { docs } from "utils/docs"
import Skeleton from "@mui/material/Skeleton"
import { Box } from "@mui/system"
import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton"

export type GroupsPageViewProps = {
groups: Group[] | undefined
Expand Down Expand Up @@ -83,7 +89,7 @@ export const GroupsPageView: FC<GroupsPageViewProps> = ({
<TableBody>
<ChooseOne>
<Cond condition={isLoading}>
<TableLoaderSkeleton columns={3} useAvatarData />
<TableLoader />
</Cond>

<Cond condition={isEmpty}>
Expand Down Expand Up @@ -184,6 +190,26 @@ export const GroupsPageView: FC<GroupsPageViewProps> = ({
)
}

const TableLoader = () => {
return (
<TableLoaderSkeleton>
<TableRowSkeleton>
<TableCell>
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
<AvatarDataSkeleton />
</Box>
</TableCell>
<TableCell>
<Skeleton variant="text" width="25%" />
</TableCell>
<TableCell>
<Skeleton variant="text" width="25%" />
</TableCell>
</TableRowSkeleton>
</TableLoaderSkeleton>
)
}

const useStyles = makeStyles((theme) => ({
clickableTableRow: {
cursor: "pointer",
Expand Down
36 changes: 34 additions & 2 deletions site/src/pages/TemplatesPage/TemplatesPageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ import {
PageHeaderTitle,
} from "../../components/PageHeader/PageHeader"
import { Stack } from "../../components/Stack/Stack"
import { TableLoaderSkeleton } from "../../components/TableLoader/TableLoader"
import {
TableLoaderSkeleton,
TableRowSkeleton,
} from "../../components/TableLoader/TableLoader"
import {
HelpTooltip,
HelpTooltipLink,
Expand All @@ -42,6 +45,9 @@ import ArrowForwardOutlined from "@mui/icons-material/ArrowForwardOutlined"
import { Avatar } from "components/Avatar/Avatar"
import { ErrorAlert } from "components/Alert/ErrorAlert"
import { docs } from "utils/docs"
import Skeleton from "@mui/material/Skeleton"
import { Box } from "@mui/system"
import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton"

export const Language = {
developerCount: (activeCount: number): string => {
Expand Down Expand Up @@ -196,7 +202,7 @@ export const TemplatesPageView: FC<
</TableHead>
<TableBody>
<Maybe condition={isLoading}>
<TableLoaderSkeleton columns={5} useAvatarData />
<TableLoader />
</Maybe>

<ChooseOne>
Expand All @@ -222,6 +228,32 @@ export const TemplatesPageView: FC<
)
}

const TableLoader = () => {
return (
<TableLoaderSkeleton>
<TableRowSkeleton>
<TableCell>
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
<AvatarDataSkeleton />
</Box>
</TableCell>
<TableCell>
<Skeleton variant="text" width="25%" />
</TableCell>
<TableCell>
<Skeleton variant="text" width="25%" />
</TableCell>
<TableCell>
<Skeleton variant="text" width="25%" />
</TableCell>
<TableCell>
<Skeleton variant="text" width="25%" />
</TableCell>
</TableRowSkeleton>
</TableLoaderSkeleton>
)
}

const useStyles = makeStyles((theme) => ({
templateIconWrapper: {
// Same size then the avatar component
Expand Down
12 changes: 2 additions & 10 deletions site/src/pages/WorkspacesPage/WorkspacesPage.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { usePagination } from "hooks/usePagination"
import { Workspace } from "api/typesGenerated"
import {
useDashboard,
useIsWorkspaceActionsEnabled,
} from "components/Dashboard/DashboardProvider"
import { useIsWorkspaceActionsEnabled } from "components/Dashboard/DashboardProvider"
import { FC, useEffect, useState } from "react"
import { Helmet } from "react-helmet-async"
import { pageTitle } from "utils/page"
Expand Down Expand Up @@ -68,10 +65,6 @@ const WorkspacesPage: FC = () => {
const [checkedWorkspaces, setCheckedWorkspaces] = useState<Workspace[]>([])
const [isDeletingAll, setIsDeletingAll] = useState(false)
const [urlSearchParams] = searchParamsResult
const dashboard = useDashboard()
const isWorkspaceBatchActionsEnabled =
dashboard.experiments.includes("workspaces_batch_actions") ||
process.env.NODE_ENV === "development"

// We want to uncheck the selected workspaces always when the url changes
// because of filtering or pagination
Expand All @@ -86,7 +79,6 @@ const WorkspacesPage: FC = () => {
</Helmet>

<WorkspacesPageView
isWorkspaceBatchActionsEnabled={isWorkspaceBatchActionsEnabled}
checkedWorkspaces={checkedWorkspaces}
onCheckChange={setCheckedWorkspaces}
workspaces={data?.workspaces}
Expand Down Expand Up @@ -198,7 +190,7 @@ const BatchDeleteConfirmation = ({
const confirmDeletion = async () => {
setConfirmError(false)

if (confirmValue.toLowerCase() !== "delete") {
if (confirmValue !== "DELETE") {
setConfirmError(true)
return
}
Expand Down
5 changes: 1 addition & 4 deletions site/src/pages/WorkspacesPage/WorkspacesPageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export interface WorkspacesPageViewProps {
filterProps: ComponentProps<typeof WorkspacesFilter>
page: number
limit: number
isWorkspaceBatchActionsEnabled?: boolean
onPageChange: (page: number) => void
onUpdateWorkspace: (workspace: Workspace) => void
onCheckChange: (checkedWorkspaces: Workspace[]) => void
Expand All @@ -64,7 +63,6 @@ export const WorkspacesPageView: FC<
onUpdateWorkspace,
page,
checkedWorkspaces,
isWorkspaceBatchActionsEnabled,
onCheckChange,
onDeleteAll,
}) => {
Expand Down Expand Up @@ -131,7 +129,7 @@ export const WorkspacesPageView: FC<
startIcon={<DeleteOutlined />}
onClick={onDeleteAll}
>
Delete all
Delete selected
</Button>
</Box>
</>
Expand All @@ -151,7 +149,6 @@ export const WorkspacesPageView: FC<
onUpdateWorkspace={onUpdateWorkspace}
checkedWorkspaces={checkedWorkspaces}
onCheckChange={onCheckChange}
isWorkspaceBatchActionsEnabled={isWorkspaceBatchActionsEnabled}
/>
{count !== undefined && (
<PaginationWidgetBase
Expand Down
Loading