Skip to content

feat(site): move history into sidebar #11413

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 11 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
WIP: Add base sidebar structure
  • Loading branch information
BrunoQuaresma committed Jan 3, 2024
commit e7d633dcf7d0f19d48d883dde4bbf5b703c69a19
2 changes: 1 addition & 1 deletion site/src/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,6 @@ export const AppRouter: FC = () => {

{/* In order for the 404 page to work properly the routes that start with
top level parameter must be fully qualified. */}
<Route path="/:username/:workspace" element={<WorkspacePage />} />
<Route
path="/:username/:workspace/builds/:buildNumber"
element={<WorkspaceBuildPage />}
Expand Down Expand Up @@ -408,6 +407,7 @@ export const AppRouter: FC = () => {
</Route>

{/* Pages that don't have the dashboard layout */}
<Route path="/:username/:workspace" element={<WorkspacePage />} />
<Route
path="/templates/:template/versions/:version/edit"
element={<TemplateVersionEditorPage />}
Expand Down
66 changes: 66 additions & 0 deletions site/src/components/FullPageLayout/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Interpolation, Theme, useTheme } from "@mui/material/styles";
import { HTMLAttributes } from "react";
import { Link, LinkProps } from "react-router-dom";

export const Sidebar = (props: HTMLAttributes<HTMLDivElement>) => {
const theme = useTheme();
return (
<div
css={{
width: 260,
borderRight: `1px solid ${theme.palette.divider}`,
height: "100%",
overflow: "auto",
flexShrink: 0,
padding: "8px 0",
display: "flex",
flexDirection: "column",
gap: 1,
}}
{...props}
/>
);
};

export const SidebarLink = (props: LinkProps) => {
return <Link css={styles.sidebarItem} {...props} />;
};

export const SidebarItem = (props: HTMLAttributes<HTMLButtonElement>) => {
Copy link
Member

@Parkreiner Parkreiner Jan 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for future-proofing, do you think it's worth explicitly pulling the type attribute out and setting its default value to button?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I got it... do you have a code sample?

Copy link
Member

@Parkreiner Parkreiner Jan 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, just realized that the comment got garbled by a stray backtick

Now that I'm a little more awake, I think my suggestion would be overkill, but I was talking about this:

export const SidebarItem = ({
  type = "button",
  ...delegatedProps
}: HTMLAttributes<HTMLButtonElement>) => {
  return <button css={styles.sidebarItem} type={type} {...props} />;
};

return <button css={styles.sidebarItem} {...props} />;
};

export const SidebarCaption = (props: HTMLAttributes<HTMLSpanElement>) => {
return (
<span
css={{
fontSize: 10,
lineHeight: 1.2,
padding: "12px 16px",
display: "block",
textTransform: "uppercase",
fontWeight: 500,
letterSpacing: 1,
}}
{...props}
/>
);
};

const styles = {
sidebarItem: (theme: Theme) => ({
fontSize: 14,
lineHeight: 1.2,
color: theme.palette.text.primary,
textDecoration: "none",
padding: "8px 16px",
display: "block",
textAlign: "left",
background: "none",
border: 0,

"&:hover": {
backgroundColor: theme.palette.action.hover,
},
}),
} satisfies Record<string, Interpolation<Theme>>;
76 changes: 76 additions & 0 deletions site/src/components/WorkspaceBuild/WorkspaceBuildData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Interpolation, Theme, useTheme } from "@emotion/react";
import Skeleton from "@mui/material/Skeleton";
import { WorkspaceBuild } from "api/typesGenerated";
import { BuildIcon } from "components/BuildIcon/BuildIcon";
import {
getDisplayWorkspaceBuildStatus,
getDisplayWorkspaceBuildInitiatedBy,
displayWorkspaceBuildDuration,
} from "utils/workspace";

export const WorkspaceBuildData = ({ build }: { build: WorkspaceBuild }) => {
const theme = useTheme();
const statusType = getDisplayWorkspaceBuildStatus(theme, build).type;

return (
<div css={styles.root}>
<BuildIcon
transition={build.transition}
css={{
width: 16,
height: 16,
color: theme.palette[statusType].light,
}}
/>
<div css={{ overflow: "hidden" }}>
<div
css={{
textTransform: "capitalize",
color: theme.palette.text.primary,
textOverflow: "ellipsis",
overflow: "hidden",
whiteSpace: "nowrap",
}}
>
{build.transition} by{" "}
<strong>{getDisplayWorkspaceBuildInitiatedBy(build)}</strong>
</div>
<div
css={{
fontSize: 12,
color: theme.palette.text.secondary,
marginTop: 2,
}}
>
{displayWorkspaceBuildDuration(build)}
</div>
</div>
</div>
);
};

export const WorkspaceBuildDataSkeleton = () => {
return (
<div css={styles.root}>
<Skeleton variant="circular" width={16} height={16} />
<div>
<Skeleton variant="text" width={94} height={16} />
<Skeleton
variant="text"
width={60}
height={14}
css={{ marginTop: 2 }}
/>
</div>
</div>
);
};

const styles = {
root: {
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: 8,
},
} satisfies Record<string, Interpolation<Theme>>;
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
TopbarDivider,
TopbarIconButton,
} from "components/FullPageLayout/Topbar";
import { Sidebar } from "components/FullPageLayout/Sidebar";

type Tab = "logs" | "resources" | undefined; // Undefined is to hide the tab

Expand Down Expand Up @@ -301,13 +302,7 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
</div>
)}

<div
css={{
width: 240,
borderRight: `1px solid ${theme.palette.divider}`,
flexShrink: 0,
}}
>
<Sidebar>
<div
css={{
height: 42,
Expand Down Expand Up @@ -409,7 +404,7 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
onRename={(file) => setRenameFileOpen(file)}
activePath={activePath}
/>
</div>
</Sidebar>

<div
css={{
Expand Down
99 changes: 15 additions & 84 deletions site/src/pages/WorkspaceBuildPage/WorkspaceBuildPageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,14 @@ import {
} from "components/PageHeader/FullWidthPageHeader";
import { Link } from "react-router-dom";
import { Stats, StatsItem } from "components/Stats/Stats";
import {
displayWorkspaceBuildDuration,
getDisplayWorkspaceBuildInitiatedBy,
getDisplayWorkspaceBuildStatus,
} from "utils/workspace";
import { displayWorkspaceBuildDuration } from "utils/workspace";
import { Sidebar, SidebarCaption, SidebarItem } from "./Sidebar";
import { BuildIcon } from "components/BuildIcon/BuildIcon";
import Skeleton from "@mui/material/Skeleton";
import { Alert } from "components/Alert/Alert";
import { DashboardFullPage } from "components/Dashboard/DashboardLayout";
import {
WorkspaceBuildData,
WorkspaceBuildDataSkeleton,
} from "components/WorkspaceBuild/WorkspaceBuildData";

const sortLogsByCreatedAt = (logs: ProvisionerJobLog[]) => {
return [...logs].sort(
Expand Down Expand Up @@ -112,15 +110,20 @@ export const WorkspaceBuildPageView: FC<WorkspaceBuildPageViewProps> = ({
<SidebarCaption>Builds</SidebarCaption>
{!builds &&
Array.from({ length: 15 }, (_, i) => (
<BuildSidebarItemSkeleton key={i} />
<SidebarItem key={i}>
<WorkspaceBuildDataSkeleton />
</SidebarItem>
))}

{builds?.map((build) => (
<BuildSidebarItem
<Link
key={build.id}
build={build}
active={build.build_number === activeBuildNumber}
/>
to={`/@${build.workspace_owner_name}/${build.workspace_name}/builds/${build.build_number}`}
>
<SidebarItem active={build.build_number === activeBuildNumber}>
<WorkspaceBuildData build={build} />
</SidebarItem>
</Link>
))}
</Sidebar>

Expand Down Expand Up @@ -167,78 +170,6 @@ export const WorkspaceBuildPageView: FC<WorkspaceBuildPageViewProps> = ({
);
};

interface BuildSidebarItemProps {
build: WorkspaceBuild;
active: boolean;
}

const BuildSidebarItem: FC<BuildSidebarItemProps> = ({ build, active }) => {
const theme = useTheme();
const statusType = getDisplayWorkspaceBuildStatus(theme, build).type;

return (
<Link
key={build.id}
to={`/@${build.workspace_owner_name}/${build.workspace_name}/builds/${build.build_number}`}
>
<SidebarItem active={active}>
<div css={{ display: "flex", alignItems: "start", gap: 8 }}>
<BuildIcon
transition={build.transition}
css={{
width: 16,
height: 16,
color: theme.palette[statusType].light,
}}
/>
<div css={{ overflow: "hidden" }}>
<div
css={{
textTransform: "capitalize",
color: theme.palette.text.primary,
textOverflow: "ellipsis",
overflow: "hidden",
whiteSpace: "nowrap",
}}
>
{build.transition} by{" "}
<strong>{getDisplayWorkspaceBuildInitiatedBy(build)}</strong>
</div>
<div
css={{
fontSize: 12,
color: theme.palette.text.secondary,
marginTop: 2,
}}
>
{displayWorkspaceBuildDuration(build)}
</div>
</div>
</div>
</SidebarItem>
</Link>
);
};

const BuildSidebarItemSkeleton: FC = () => {
return (
<SidebarItem>
<div css={{ display: "flex", alignItems: "start", gap: 8 }}>
<Skeleton variant="circular" width={16} height={16} />
<div>
<Skeleton variant="text" width={94} height={16} />
<Skeleton
variant="text"
width={60}
height={14}
css={{ marginTop: 2 }}
/>
</div>
</div>
</SidebarItem>
);
};

const styles = {
stats: (theme) => ({
padding: 0,
Expand Down
44 changes: 44 additions & 0 deletions site/src/pages/WorkspacePage/HistorySidebarContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { infiniteWorkspaceBuilds } from "api/queries/workspaceBuilds";
import { Workspace } from "api/typesGenerated";
import {
SidebarCaption,
SidebarItem,
SidebarLink,
} from "components/FullPageLayout/Sidebar";
import {
WorkspaceBuildData,
WorkspaceBuildDataSkeleton,
} from "components/WorkspaceBuild/WorkspaceBuildData";
import { useInfiniteQuery } from "react-query";

export const HistorySidebarContent = ({
workspace,
}: {
workspace: Workspace;
}) => {
const buildsQuery = useInfiniteQuery({
...infiniteWorkspaceBuilds(workspace?.id ?? ""),
enabled: workspace !== undefined,
});
const builds = buildsQuery.data?.pages.flat();

return (
<>
<SidebarCaption>History</SidebarCaption>
{builds
? builds.map((build) => (
<SidebarLink
key={build.id}
to={`/@${build.workspace_owner_name}/${build.workspace_name}/builds/${build.build_number}`}
>
<WorkspaceBuildData build={build} />
</SidebarLink>
))
: Array.from({ length: 15 }, (_, i) => (
<SidebarItem key={i}>
<WorkspaceBuildDataSkeleton />
</SidebarItem>
))}
</>
);
};
29 changes: 29 additions & 0 deletions site/src/pages/WorkspacePage/ResourcesSidebarContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useTheme } from "@mui/material/styles";
import { Workspace } from "api/typesGenerated";
import { SidebarLink, SidebarCaption } from "components/FullPageLayout/Sidebar";

export const ResourcesSidebarContent = ({
workspace,
}: {
workspace: Workspace;
}) => {
const theme = useTheme();

return (
<>
<SidebarCaption>Resources</SidebarCaption>
{workspace.latest_build.resources.map((r) => (
<SidebarLink
key={r.id}
to={{ search: `r=${r.id}` }}
css={{ display: "flex", flexDirection: "column", lineHeight: 1.6 }}
>
<span css={{ fontWeight: 500 }}>{r.name}</span>
<span css={{ fontSize: 13, color: theme.palette.text.secondary }}>
{r.type}
</span>
</SidebarLink>
))}
</>
);
};
Loading