Skip to content

refactor(site): Improve workspaces filtering #7681

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 25 commits into from
May 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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: 4 additions & 2 deletions coderd/apidoc/docs.go

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

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

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

3 changes: 3 additions & 0 deletions codersdk/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -1682,6 +1682,9 @@ const (
// https://github.com/coder/coder/milestone/19
ExperimentWorkspaceActions Experiment = "workspace_actions"

// New workspace filter
ExperimentWorkspaceFilter Experiment = "workspace_filter"

// Add new experiments here!
// ExperimentExample Experiment = "example"
)
Expand Down
1 change: 1 addition & 0 deletions docs/api/schemas.md
Original file line number Diff line number Diff line change
Expand Up @@ -2559,6 +2559,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a
| ------------------- |
| `moons` |
| `workspace_actions` |
| `workspace_filter` |

## codersdk.Feature

Expand Down
1 change: 1 addition & 0 deletions site/.eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ rules:
"object-curly-spacing": "off"
react-hooks/exhaustive-deps: warn
react-hooks/rules-of-hooks: error
react/display-name: "off"
react/jsx-no-script-url:
- error
- - name: Link
Expand Down
4 changes: 2 additions & 2 deletions site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@
"@testing-library/user-event": "14.4.3",
"@types/jest": "29.4.0",
"@types/node": "14.18.22",
"@types/react": "18.0.15",
"@types/react-dom": "18.0.6",
"@types/react": "18.2.6",
"@types/react-dom": "18.2.4",
"@types/react-helmet": "6.1.5",
"@types/react-syntax-highlighter": "15.5.5",
"@types/react-virtualized-auto-sizer": "1.0.1",
Expand Down
File renamed without changes.
10 changes: 10 additions & 0 deletions site/src/@types/i18n.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import "i18next"

// https://github.com/i18next/react-i18next/issues/1543#issuecomment-1528679591
declare module "i18next" {
interface TypeOptions {
returnNull: false
allowObjectInHTMLChildren: false
}
export function t<T>(s: string): T
}
File renamed without changes.
3 changes: 3 additions & 0 deletions site/src/api/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export const isApiValidationError = (error: unknown): error is ApiError => {
return isApiError(error) && hasApiFieldErrors(error)
}

export const hasError = (error: unknown) =>
error !== undefined && error !== null

export const mapApiErrorToFieldErrors = (
apiErrorResponse: ApiErrorResponse,
): FieldErrors => {
Expand Down
8 changes: 6 additions & 2 deletions site/src/api/typesGenerated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1385,8 +1385,12 @@ export const Entitlements: Entitlement[] = [
]

// From codersdk/deployment.go
export type Experiment = "moons" | "workspace_actions"
export const Experiments: Experiment[] = ["moons", "workspace_actions"]
export type Experiment = "moons" | "workspace_actions" | "workspace_filter"
export const Experiments: Experiment[] = [
"moons",
"workspace_actions",
"workspace_filter",
]

// From codersdk/deployment.go
export type FeatureName =
Expand Down
4 changes: 2 additions & 2 deletions site/src/components/DeploymentBanner/DeploymentBannerView.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { DeploymentStats, WorkspaceStatus } from "api/typesGenerated"
import { FC, useMemo, useEffect, useState } from "react"
import prettyBytes from "pretty-bytes"
import { getStatus } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge"
import BuildingIcon from "@mui/icons-material/Build"
import { makeStyles } from "@mui/styles"
import { RocketIcon } from "components/Icons/RocketIcon"
Expand All @@ -19,6 +18,7 @@ import dayjs from "dayjs"
import CollectedIcon from "@mui/icons-material/Compare"
import RefreshIcon from "@mui/icons-material/Refresh"
import Button from "@mui/material/Button"
import { getDisplayWorkspaceStatus } from "utils/workspace"

export const bannerHeight = 36

Expand Down Expand Up @@ -218,7 +218,7 @@ const WorkspaceBuildValue: FC<{
count?: number
}> = ({ status, count }) => {
const styles = useStyles()
const displayStatus = getStatus(status)
const displayStatus = getDisplayWorkspaceStatus(status)
let statusText = displayStatus.text
let icon = displayStatus.icon
if (status === "starting") {
Expand Down
14 changes: 6 additions & 8 deletions site/src/components/UserAvatar/UserAvatar.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import { Avatar } from "components/Avatar/Avatar"
import { Avatar, AvatarProps } from "components/Avatar/Avatar"
import { FC } from "react"

export interface UserAvatarProps {
export type UserAvatarProps = {
username: string
avatarURL?: string
// It is needed to work with the AvatarGroup so it can pass the
// MuiAvatarGroup-avatar className
className?: string
}
} & AvatarProps

export const UserAvatar: FC<UserAvatarProps> = ({
username,
avatarURL,
className,

...avatarProps
}) => {
return (
<Avatar title={username} src={avatarURL} className={className}>
<Avatar title={username} src={avatarURL} {...avatarProps}>
{username}
</Avatar>
)
Expand Down
93 changes: 8 additions & 85 deletions site/src/components/WorkspaceStatusBadge/WorkspaceStatusBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import CircularProgress from "@mui/material/CircularProgress"
import ErrorIcon from "@mui/icons-material/ErrorOutline"
import StopIcon from "@mui/icons-material/StopOutlined"
import PlayIcon from "@mui/icons-material/PlayArrowOutlined"
import QueuedIcon from "@mui/icons-material/HourglassEmpty"
import { Workspace, WorkspaceBuild } from "api/typesGenerated"
import { Workspace } from "api/typesGenerated"
import { Pill } from "components/Pill/Pill"
import i18next from "i18next"
import { FC, PropsWithChildren } from "react"
import { makeStyles } from "@mui/styles"
import { combineClasses } from "utils/combineClasses"
Expand All @@ -14,82 +8,7 @@ import {
ImpendingDeletionBadge,
ImpendingDeletionText,
} from "components/WorkspaceDeletion"

const LoadingIcon: FC = () => {
return <CircularProgress size={10} style={{ color: "#FFF" }} />
}

export const getStatus = (buildStatus: WorkspaceBuild["status"]) => {
const { t } = i18next

switch (buildStatus) {
case undefined:
return {
text: t("workspaceStatus.loading", { ns: "common" }),
icon: <LoadingIcon />,
} as const
case "running":
return {
type: "success",
text: t("workspaceStatus.running", { ns: "common" }),
icon: <PlayIcon />,
} as const
case "starting":
return {
type: "success",
text: t("workspaceStatus.starting", { ns: "common" }),
icon: <LoadingIcon />,
} as const
case "stopping":
return {
type: "warning",
text: t("workspaceStatus.stopping", { ns: "common" }),
icon: <LoadingIcon />,
} as const
case "stopped":
return {
type: "warning",
text: t("workspaceStatus.stopped", { ns: "common" }),
icon: <StopIcon />,
} as const
case "deleting":
return {
type: "warning",
text: t("workspaceStatus.deleting", { ns: "common" }),
icon: <LoadingIcon />,
} as const
case "deleted":
return {
type: "error",
text: t("workspaceStatus.deleted", { ns: "common" }),
icon: <ErrorIcon />,
} as const
case "canceling":
return {
type: "warning",
text: t("workspaceStatus.canceling", { ns: "common" }),
icon: <LoadingIcon />,
} as const
case "canceled":
return {
type: "warning",
text: t("workspaceStatus.canceled", { ns: "common" }),
icon: <ErrorIcon />,
} as const
case "failed":
return {
type: "error",
text: t("workspaceStatus.failed", { ns: "common" }),
icon: <ErrorIcon />,
} as const
case "pending":
return {
type: "info",
text: t("workspaceStatus.pending", { ns: "common" }),
icon: <QueuedIcon />,
} as const
}
}
import { getDisplayWorkspaceStatus } from "utils/workspace"

export type WorkspaceStatusBadgeProps = {
workspace: Workspace
Expand All @@ -99,7 +18,9 @@ export type WorkspaceStatusBadgeProps = {
export const WorkspaceStatusBadge: FC<
PropsWithChildren<WorkspaceStatusBadgeProps>
> = ({ workspace, className }) => {
const { text, icon, type } = getStatus(workspace.latest_build.status)
const { text, icon, type } = getDisplayWorkspaceStatus(
workspace.latest_build.status,
)
return (
<ChooseOne>
{/* <ImpendingDeletionBadge/> determines its own visibility */}
Expand All @@ -117,7 +38,9 @@ export const WorkspaceStatusText: FC<
PropsWithChildren<WorkspaceStatusBadgeProps>
> = ({ workspace, className }) => {
const styles = useStyles()
const { text, type } = getStatus(workspace.latest_build.status)
const { text, type } = getDisplayWorkspaceStatus(
workspace.latest_build.status,
)

return (
<ChooseOne>
Expand Down
1 change: 0 additions & 1 deletion site/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ export * from "./useClickable"
export * from "./useClickableTableRow"
export * from "./useClipboard"
export * from "./useFeatureVisibility"
export * from "./useFilter"
export * from "./useLocalStorage"
export * from "./useMe"
export * from "./useOrganizationId"
Expand Down
21 changes: 0 additions & 21 deletions site/src/hooks/useFilter.ts

This file was deleted.

16 changes: 7 additions & 9 deletions site/src/hooks/usePagination.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { DEFAULT_RECORDS_PER_PAGE } from "components/PaginationWidget/utils"
import { useSearchParams } from "react-router-dom"

type UsePaginationResult = {
page: number
limit: number
goToPage: (page: number) => void
}

export const usePagination = (): UsePaginationResult => {
const [searchParams, setSearchParams] = useSearchParams()
const page = searchParams.get("page") ? Number(searchParams.get("page")) : 0
export const usePagination = ({
searchParamsResult,
}: {
searchParamsResult: ReturnType<typeof useSearchParams>
}) => {
const [searchParams, setSearchParams] = searchParamsResult
const page = searchParams.get("page") ? Number(searchParams.get("page")) : 1
const limit = DEFAULT_RECORDS_PER_PAGE

const goToPage = (page: number) => {
Expand Down
16 changes: 8 additions & 8 deletions site/src/pages/UserSettingsPage/TokensPage/TokensPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FC, PropsWithChildren, useState } from "react"
import { Section } from "components/SettingsLayout/Section"
import { TokensPageView } from "./TokensPageView"
import makeStyles from "@mui/styles/makeStyles"
import { useTranslation, Trans } from "react-i18next"
import { useTranslation } from "react-i18next"
import { useTokensData } from "./hooks"
import { ConfirmDeleteDialog } from "./components"
import { Stack } from "components/Stack/Stack"
Expand All @@ -16,12 +16,6 @@ export const TokensPage: FC<PropsWithChildren<unknown>> = () => {
const { t } = useTranslation("tokensPage")

const cliCreateCommand = "coder tokens create"
const description = (
<Trans t={t} i18nKey="description" values={{ cliCreateCommand }}>
Tokens are used to authenticate with the Coder API. You can create a token
with the Coder CLI using the <code>{{ cliCreateCommand }}</code> command.
</Trans>
)

const TokenActions = () => (
<Stack direction="row" justifyContent="end" className={styles.tokenActions}>
Expand Down Expand Up @@ -52,7 +46,13 @@ export const TokensPage: FC<PropsWithChildren<unknown>> = () => {
<Section
title={t("title")}
className={styles.section}
description={description}
description={
<>
Tokens are used to authenticate with the Coder API. You can create a
token with the Coder CLI using the <code>{cliCreateCommand}</code>{" "}
command.
</>
}
layout="fluid"
>
<TokenActions />
Expand Down
Loading