mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
chore: use emotion for styling (pt. 7) (#10431)
This commit is contained in:
@ -1,6 +1,8 @@
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import { watchAgentMetadata } from "api/api";
|
||||
import { WorkspaceAgent, WorkspaceAgentMetadata } from "api/typesGenerated";
|
||||
import type {
|
||||
WorkspaceAgent,
|
||||
WorkspaceAgentMetadata,
|
||||
} from "api/typesGenerated";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import dayjs from "dayjs";
|
||||
import {
|
||||
@ -13,17 +15,15 @@ import {
|
||||
} from "react";
|
||||
import Skeleton from "@mui/material/Skeleton";
|
||||
import { MONOSPACE_FONT_FAMILY } from "theme/constants";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Box, { BoxProps } from "@mui/material/Box";
|
||||
import { type Interpolation, type Theme } from "@emotion/react";
|
||||
|
||||
type ItemStatus = "stale" | "valid" | "loading";
|
||||
|
||||
export const WatchAgentMetadataContext = createContext(watchAgentMetadata);
|
||||
|
||||
const MetadataItem: FC<{ item: WorkspaceAgentMetadata }> = ({ item }) => {
|
||||
const styles = useStyles();
|
||||
|
||||
if (item.result === undefined) {
|
||||
throw new Error("Metadata item result is undefined");
|
||||
}
|
||||
@ -56,41 +56,29 @@ const MetadataItem: FC<{ item: WorkspaceAgentMetadata }> = ({ item }) => {
|
||||
// could be buggy. But, how common is that anyways?
|
||||
const value =
|
||||
status === "loading" ? (
|
||||
<Skeleton
|
||||
width={65}
|
||||
height={12}
|
||||
variant="text"
|
||||
className={styles.skeleton}
|
||||
/>
|
||||
<Skeleton width={65} height={12} variant="text" css={styles.skeleton} />
|
||||
) : status === "stale" ? (
|
||||
<Tooltip title="This data is stale and no longer up to date">
|
||||
<StaticWidth
|
||||
className={combineClasses([
|
||||
styles.metadataValue,
|
||||
styles.metadataStale,
|
||||
])}
|
||||
>
|
||||
<StaticWidth css={[styles.metadataValue, styles.metadataStale]}>
|
||||
{item.result.value}
|
||||
</StaticWidth>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<StaticWidth
|
||||
className={combineClasses([
|
||||
css={[
|
||||
styles.metadataValue,
|
||||
item.result.error.length === 0
|
||||
? styles.metadataValueSuccess
|
||||
: styles.metadataValueError,
|
||||
])}
|
||||
]}
|
||||
>
|
||||
{item.result.value}
|
||||
</StaticWidth>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.metadata}>
|
||||
<div className={styles.metadataLabel}>
|
||||
{item.description.display_name}
|
||||
</div>
|
||||
<div css={styles.metadata}>
|
||||
<div css={styles.metadataLabel}>{item.description.display_name}</div>
|
||||
<Box>{value}</Box>
|
||||
</div>
|
||||
);
|
||||
@ -101,12 +89,11 @@ export interface AgentMetadataViewProps {
|
||||
}
|
||||
|
||||
export const AgentMetadataView: FC<AgentMetadataViewProps> = ({ metadata }) => {
|
||||
const styles = useStyles();
|
||||
if (metadata.length === 0) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<div css={styles.root}>
|
||||
<Stack alignItems="baseline" direction="row" spacing={6}>
|
||||
{metadata.map((m) => {
|
||||
if (m.description === undefined) {
|
||||
@ -127,7 +114,6 @@ export const AgentMetadata: FC<{
|
||||
WorkspaceAgentMetadata[] | undefined
|
||||
>(undefined);
|
||||
const watchAgentMetadata = useContext(WatchAgentMetadataContext);
|
||||
const styles = useStyles();
|
||||
|
||||
useEffect(() => {
|
||||
if (storybookMetadata !== undefined) {
|
||||
@ -166,7 +152,7 @@ export const AgentMetadata: FC<{
|
||||
|
||||
if (metadata === undefined) {
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<div css={styles.root}>
|
||||
<AgentMetadataSkeleton />
|
||||
</div>
|
||||
);
|
||||
@ -176,21 +162,19 @@ export const AgentMetadata: FC<{
|
||||
};
|
||||
|
||||
export const AgentMetadataSkeleton: FC = () => {
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<Stack alignItems="baseline" direction="row" spacing={6}>
|
||||
<div className={styles.metadata}>
|
||||
<div css={styles.metadata}>
|
||||
<Skeleton width={40} height={12} variant="text" />
|
||||
<Skeleton width={65} height={14} variant="text" />
|
||||
</div>
|
||||
|
||||
<div className={styles.metadata}>
|
||||
<div css={styles.metadata}>
|
||||
<Skeleton width={40} height={12} variant="text" />
|
||||
<Skeleton width={65} height={14} variant="text" />
|
||||
</div>
|
||||
|
||||
<div className={styles.metadata}>
|
||||
<div css={styles.metadata}>
|
||||
<Skeleton width={40} height={12} variant="text" />
|
||||
<Skeleton width={65} height={14} variant="text" />
|
||||
</div>
|
||||
@ -219,16 +203,16 @@ const StaticWidth = (props: BoxProps) => {
|
||||
|
||||
// These are more or less copied from
|
||||
// site/src/components/Resources/ResourceCard.tsx
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
const styles = {
|
||||
root: (theme) => ({
|
||||
padding: theme.spacing(2.5, 4),
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
background: theme.palette.background.paper,
|
||||
overflowX: "auto",
|
||||
scrollPadding: theme.spacing(0, 4),
|
||||
},
|
||||
}),
|
||||
|
||||
metadata: {
|
||||
metadata: (theme) => ({
|
||||
fontSize: 12,
|
||||
lineHeight: "normal",
|
||||
display: "flex",
|
||||
@ -240,15 +224,15 @@ const useStyles = makeStyles((theme) => ({
|
||||
"&:last-child": {
|
||||
paddingRight: theme.spacing(4),
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
metadataLabel: {
|
||||
metadataLabel: (theme) => ({
|
||||
color: theme.palette.text.secondary,
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
fontWeight: 500,
|
||||
},
|
||||
}),
|
||||
|
||||
metadataValue: {
|
||||
textOverflow: "ellipsis",
|
||||
@ -258,29 +242,29 @@ const useStyles = makeStyles((theme) => ({
|
||||
fontSize: 14,
|
||||
},
|
||||
|
||||
metadataValueSuccess: {
|
||||
metadataValueSuccess: (theme) => ({
|
||||
color: theme.palette.success.light,
|
||||
},
|
||||
}),
|
||||
|
||||
metadataValueError: {
|
||||
metadataValueError: (theme) => ({
|
||||
color: theme.palette.error.main,
|
||||
},
|
||||
}),
|
||||
|
||||
metadataStale: {
|
||||
metadataStale: (theme) => ({
|
||||
color: theme.palette.text.disabled,
|
||||
cursor: "pointer",
|
||||
},
|
||||
}),
|
||||
|
||||
skeleton: {
|
||||
skeleton: (theme) => ({
|
||||
marginTop: theme.spacing(0.5),
|
||||
},
|
||||
}),
|
||||
|
||||
inlineCommand: {
|
||||
inlineCommand: (theme) => ({
|
||||
fontFamily: MONOSPACE_FONT_FAMILY,
|
||||
display: "inline-block",
|
||||
fontWeight: 600,
|
||||
margin: 0,
|
||||
borderRadius: 4,
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
}));
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
@ -1,9 +1,9 @@
|
||||
import Collapse from "@mui/material/Collapse";
|
||||
import Skeleton from "@mui/material/Skeleton";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { type Interpolation, type Theme } from "@emotion/react";
|
||||
import * as API from "api/api";
|
||||
import {
|
||||
import type {
|
||||
Workspace,
|
||||
WorkspaceAgent,
|
||||
WorkspaceAgentLogSource,
|
||||
@ -30,7 +30,6 @@ import {
|
||||
import AutoSizer from "react-virtualized-auto-sizer";
|
||||
import { FixedSizeList as List, ListOnScrollProps } from "react-window";
|
||||
import { colors } from "theme/colors";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
import { Stack } from "../Stack/Stack";
|
||||
import { AgentLatency } from "./AgentLatency";
|
||||
import { AgentMetadata } from "./AgentMetadata";
|
||||
@ -75,7 +74,6 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
sshPrefix,
|
||||
storybookLogs,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
const hasAppsToDisplay = !hideVSCodeDesktopButton || agent.apps.length > 0;
|
||||
const shouldDisplayApps =
|
||||
showApps &&
|
||||
@ -159,28 +157,26 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
key={agent.id}
|
||||
direction="column"
|
||||
spacing={0}
|
||||
className={combineClasses([
|
||||
css={[
|
||||
styles.agentRow,
|
||||
styles[`agentRow-${agent.status}`],
|
||||
styles[`agentRow-lifecycle-${agent.lifecycle_state}`],
|
||||
])}
|
||||
]}
|
||||
>
|
||||
<div className={styles.agentInfo}>
|
||||
<div className={styles.agentNameAndStatus}>
|
||||
<div className={styles.agentNameAndInfo}>
|
||||
<div css={styles.agentInfo}>
|
||||
<div css={styles.agentNameAndStatus}>
|
||||
<div css={styles.agentNameAndInfo}>
|
||||
<AgentStatus agent={agent} />
|
||||
<div className={styles.agentName}>{agent.name}</div>
|
||||
<div css={styles.agentName}>{agent.name}</div>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={2}
|
||||
alignItems="baseline"
|
||||
className={styles.agentDescription}
|
||||
css={styles.agentDescription}
|
||||
>
|
||||
{agent.status === "connected" && (
|
||||
<>
|
||||
<span className={styles.agentOS}>
|
||||
{agent.operating_system}
|
||||
</span>
|
||||
<span css={styles.agentOS}>{agent.operating_system}</span>
|
||||
<AgentVersion
|
||||
agent={agent}
|
||||
serverVersion={serverVersion}
|
||||
@ -200,7 +196,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
</div>
|
||||
|
||||
{agent.status === "connected" && (
|
||||
<div className={styles.agentButtons}>
|
||||
<div css={styles.agentButtons}>
|
||||
{shouldDisplayApps && (
|
||||
<>
|
||||
{(agent.display_apps.includes("vscode") ||
|
||||
@ -258,18 +254,18 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
)}
|
||||
|
||||
{agent.status === "connecting" && (
|
||||
<div className={styles.agentButtons}>
|
||||
<div css={styles.agentButtons}>
|
||||
<Skeleton
|
||||
width={80}
|
||||
height={32}
|
||||
variant="rectangular"
|
||||
className={styles.buttonSkeleton}
|
||||
css={styles.buttonSkeleton}
|
||||
/>
|
||||
<Skeleton
|
||||
width={110}
|
||||
height={32}
|
||||
variant="rectangular"
|
||||
className={styles.buttonSkeleton}
|
||||
css={styles.buttonSkeleton}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@ -278,7 +274,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
<AgentMetadata storybookMetadata={storybookAgentMetadata} agent={agent} />
|
||||
|
||||
{hasStartupFeatures && (
|
||||
<div className={styles.logsPanel}>
|
||||
<div css={styles.logsPanel}>
|
||||
<Collapse in={showLogs}>
|
||||
<AutoSizer disableHeight>
|
||||
{({ width }) => (
|
||||
@ -289,7 +285,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
itemCount={startupLogs.length}
|
||||
itemSize={logLineHeight}
|
||||
width={width}
|
||||
className={styles.startupLogs}
|
||||
css={styles.startupLogs}
|
||||
onScroll={handleLogScroll}
|
||||
>
|
||||
{({ index, style }) => {
|
||||
@ -323,7 +319,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
alt=""
|
||||
width={16}
|
||||
height={16}
|
||||
style={{
|
||||
css={{
|
||||
marginRight: 8,
|
||||
}}
|
||||
/>
|
||||
@ -331,7 +327,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
} else {
|
||||
icon = (
|
||||
<div
|
||||
style={{
|
||||
css={{
|
||||
width: 16,
|
||||
height: 16,
|
||||
marginRight: 8,
|
||||
@ -363,7 +359,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
) {
|
||||
icon = (
|
||||
<div
|
||||
style={{
|
||||
css={{
|
||||
minWidth: 16,
|
||||
width: 16,
|
||||
height: 16,
|
||||
@ -374,7 +370,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
css={{
|
||||
height: nextChangesSource ? "50%" : "100%",
|
||||
width: 4,
|
||||
background: "hsl(222, 31%, 25%)",
|
||||
@ -383,7 +379,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
/>
|
||||
{nextChangesSource && (
|
||||
<div
|
||||
style={{
|
||||
css={{
|
||||
height: 4,
|
||||
width: "50%",
|
||||
top: "calc(50% - 2px)",
|
||||
@ -429,13 +425,10 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
</AutoSizer>
|
||||
</Collapse>
|
||||
|
||||
<div className={styles.logsPanelButtons}>
|
||||
<div css={styles.logsPanelButtons}>
|
||||
{showLogs ? (
|
||||
<button
|
||||
className={combineClasses([
|
||||
styles.logsPanelButton,
|
||||
styles.toggleLogsButton,
|
||||
])}
|
||||
css={[styles.logsPanelButton, styles.toggleLogsButton]}
|
||||
onClick={() => {
|
||||
setShowLogs((v) => !v);
|
||||
}}
|
||||
@ -445,10 +438,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className={combineClasses([
|
||||
styles.logsPanelButton,
|
||||
styles.toggleLogsButton,
|
||||
])}
|
||||
css={[styles.logsPanelButton, styles.toggleLogsButton]}
|
||||
onClick={() => {
|
||||
setShowLogs((v) => !v);
|
||||
}}
|
||||
@ -511,8 +501,8 @@ const useAgentLogs = (
|
||||
return logs;
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
agentRow: {
|
||||
const styles = {
|
||||
agentRow: (theme) => ({
|
||||
backgroundColor: theme.palette.background.paperLight,
|
||||
fontSize: 16,
|
||||
borderLeft: `2px solid ${theme.palette.text.secondary}`,
|
||||
@ -520,59 +510,59 @@ const useStyles = makeStyles((theme) => ({
|
||||
"&:not(:first-of-type)": {
|
||||
borderTop: `2px solid ${theme.palette.divider}`,
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
"agentRow-connected": {
|
||||
"agentRow-connected": (theme) => ({
|
||||
borderLeftColor: theme.palette.success.light,
|
||||
},
|
||||
}),
|
||||
|
||||
"agentRow-disconnected": {
|
||||
"agentRow-disconnected": (theme) => ({
|
||||
borderLeftColor: theme.palette.text.secondary,
|
||||
},
|
||||
}),
|
||||
|
||||
"agentRow-connecting": {
|
||||
"agentRow-connecting": (theme) => ({
|
||||
borderLeftColor: theme.palette.info.light,
|
||||
},
|
||||
}),
|
||||
|
||||
"agentRow-timeout": {
|
||||
"agentRow-timeout": (theme) => ({
|
||||
borderLeftColor: theme.palette.warning.light,
|
||||
},
|
||||
}),
|
||||
|
||||
"agentRow-lifecycle-created": {},
|
||||
|
||||
"agentRow-lifecycle-starting": {
|
||||
"agentRow-lifecycle-starting": (theme) => ({
|
||||
borderLeftColor: theme.palette.info.light,
|
||||
},
|
||||
}),
|
||||
|
||||
"agentRow-lifecycle-ready": {
|
||||
"agentRow-lifecycle-ready": (theme) => ({
|
||||
borderLeftColor: theme.palette.success.light,
|
||||
},
|
||||
}),
|
||||
|
||||
"agentRow-lifecycle-start_timeout": {
|
||||
"agentRow-lifecycle-start_timeout": (theme) => ({
|
||||
borderLeftColor: theme.palette.warning.light,
|
||||
},
|
||||
}),
|
||||
|
||||
"agentRow-lifecycle-start_error": {
|
||||
"agentRow-lifecycle-start_error": (theme) => ({
|
||||
borderLeftColor: theme.palette.error.light,
|
||||
},
|
||||
}),
|
||||
|
||||
"agentRow-lifecycle-shutting_down": {
|
||||
"agentRow-lifecycle-shutting_down": (theme) => ({
|
||||
borderLeftColor: theme.palette.info.light,
|
||||
},
|
||||
}),
|
||||
|
||||
"agentRow-lifecycle-shutdown_timeout": {
|
||||
"agentRow-lifecycle-shutdown_timeout": (theme) => ({
|
||||
borderLeftColor: theme.palette.warning.light,
|
||||
},
|
||||
}),
|
||||
|
||||
"agentRow-lifecycle-shutdown_error": {
|
||||
"agentRow-lifecycle-shutdown_error": (theme) => ({
|
||||
borderLeftColor: theme.palette.error.light,
|
||||
},
|
||||
}),
|
||||
|
||||
"agentRow-lifecycle-off": {
|
||||
"agentRow-lifecycle-off": (theme) => ({
|
||||
borderLeftColor: theme.palette.text.secondary,
|
||||
},
|
||||
}),
|
||||
|
||||
agentInfo: {
|
||||
agentInfo: (theme) => ({
|
||||
padding: theme.spacing(2, 4),
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
@ -582,9 +572,9 @@ const useStyles = makeStyles((theme) => ({
|
||||
[theme.breakpoints.down("md")]: {
|
||||
gap: theme.spacing(2),
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
agentNameAndInfo: {
|
||||
agentNameAndInfo: (theme) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing(3),
|
||||
@ -593,9 +583,9 @@ const useStyles = makeStyles((theme) => ({
|
||||
[theme.breakpoints.down("md")]: {
|
||||
gap: theme.spacing(1.5),
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
agentButtons: {
|
||||
agentButtons: (theme) => ({
|
||||
display: "flex",
|
||||
gap: theme.spacing(1),
|
||||
justifyContent: "flex-end",
|
||||
@ -606,14 +596,14 @@ const useStyles = makeStyles((theme) => ({
|
||||
marginLeft: 0,
|
||||
justifyContent: "flex-start",
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
agentDescription: {
|
||||
agentDescription: (theme) => ({
|
||||
fontSize: 14,
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
}),
|
||||
|
||||
startupLogs: {
|
||||
startupLogs: (theme) => ({
|
||||
maxHeight: 256,
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
@ -623,9 +613,9 @@ const useStyles = makeStyles((theme) => ({
|
||||
"& > div": {
|
||||
position: "relative",
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
agentNameAndStatus: {
|
||||
agentNameAndStatus: (theme) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing(4),
|
||||
@ -633,9 +623,9 @@ const useStyles = makeStyles((theme) => ({
|
||||
[theme.breakpoints.down("md")]: {
|
||||
width: "100%",
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
agentName: {
|
||||
agentName: (theme) => ({
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
@ -648,15 +638,15 @@ const useStyles = makeStyles((theme) => ({
|
||||
[theme.breakpoints.down("md")]: {
|
||||
overflow: "unset",
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
agentDataGroup: {
|
||||
agentDataGroup: (theme) => ({
|
||||
display: "flex",
|
||||
alignItems: "baseline",
|
||||
gap: theme.spacing(6),
|
||||
},
|
||||
}),
|
||||
|
||||
agentData: {
|
||||
agentData: (theme) => ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
fontSize: 12,
|
||||
@ -665,17 +655,17 @@ const useStyles = makeStyles((theme) => ({
|
||||
fontWeight: 500,
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
logsPanel: {
|
||||
logsPanel: (theme) => ({
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
},
|
||||
}),
|
||||
|
||||
logsPanelButtons: {
|
||||
display: "flex",
|
||||
},
|
||||
|
||||
logsPanelButton: {
|
||||
logsPanelButton: (theme) => ({
|
||||
textAlign: "left",
|
||||
background: "transparent",
|
||||
border: 0,
|
||||
@ -696,7 +686,7 @@ const useStyles = makeStyles((theme) => ({
|
||||
"& svg": {
|
||||
color: "inherit",
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
toggleLogsButton: {
|
||||
width: "100%",
|
||||
@ -706,17 +696,17 @@ const useStyles = makeStyles((theme) => ({
|
||||
borderRadius: 4,
|
||||
},
|
||||
|
||||
agentErrorMessage: {
|
||||
agentErrorMessage: (theme) => ({
|
||||
fontSize: 12,
|
||||
fontWeight: 400,
|
||||
marginTop: theme.spacing(0.5),
|
||||
color: theme.palette.warning.light,
|
||||
},
|
||||
}),
|
||||
|
||||
agentOS: {
|
||||
textTransform: "capitalize",
|
||||
},
|
||||
}));
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
||||
// These colors were picked at random. Feel free
|
||||
// to add more, adjust, or change! Users will not
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { AppPreviewLink } from "components/Resources/AppLink/AppPreviewLink";
|
||||
import { FC } from "react";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
import { WorkspaceAgent } from "api/typesGenerated";
|
||||
import { type Interpolation, type Theme } from "@emotion/react";
|
||||
import { type FC } from "react";
|
||||
import type { WorkspaceAgent } from "api/typesGenerated";
|
||||
import { Stack } from "../Stack/Stack";
|
||||
import { AppPreviewLink } from "./AppLink/AppPreviewLink";
|
||||
|
||||
interface AgentRowPreviewStyles {
|
||||
// Helpful when there are more than one row so the values are aligned
|
||||
@ -18,57 +17,58 @@ export const AgentRowPreview: FC<AgentRowPreviewProps> = ({
|
||||
agent,
|
||||
alignValues,
|
||||
}) => {
|
||||
const styles = useStyles({ alignValues });
|
||||
|
||||
return (
|
||||
<Stack
|
||||
key={agent.id}
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
className={styles.agentRow}
|
||||
css={styles.agentRow}
|
||||
>
|
||||
<Stack direction="row" alignItems="baseline">
|
||||
<div className={styles.agentStatusWrapper}>
|
||||
<div className={styles.agentStatusPreview}></div>
|
||||
<div css={styles.agentStatusWrapper}>
|
||||
<div css={styles.agentStatusPreview}></div>
|
||||
</div>
|
||||
<Stack
|
||||
alignItems="baseline"
|
||||
direction="row"
|
||||
spacing={4}
|
||||
className={styles.agentData}
|
||||
css={styles.agentData}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="baseline"
|
||||
spacing={1}
|
||||
className={combineClasses([
|
||||
css={[
|
||||
styles.noShrink,
|
||||
styles.agentDataItem,
|
||||
styles.agentDataName,
|
||||
])}
|
||||
(theme) => ({
|
||||
[theme.breakpoints.up("sm")]: {
|
||||
minWidth: alignValues ? 240 : undefined,
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<span>Agent:</span>
|
||||
<span className={styles.agentDataValue}>{agent.name}</span>
|
||||
<span css={styles.agentDataValue}>{agent.name}</span>
|
||||
</Stack>
|
||||
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="baseline"
|
||||
spacing={1}
|
||||
className={combineClasses([
|
||||
css={[
|
||||
styles.noShrink,
|
||||
styles.agentDataItem,
|
||||
styles.agentDataOS,
|
||||
])}
|
||||
(theme) => ({
|
||||
[theme.breakpoints.up("sm")]: {
|
||||
minWidth: alignValues ? 100 : undefined,
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<span>OS:</span>
|
||||
<span
|
||||
className={combineClasses([
|
||||
styles.agentDataValue,
|
||||
styles.agentOS,
|
||||
])}
|
||||
>
|
||||
<span css={[styles.agentDataValue, styles.agentOS]}>
|
||||
{agent.operating_system}
|
||||
</span>
|
||||
</Stack>
|
||||
@ -77,7 +77,7 @@ export const AgentRowPreview: FC<AgentRowPreviewProps> = ({
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
spacing={1}
|
||||
className={styles.agentDataItem}
|
||||
css={styles.agentDataItem}
|
||||
>
|
||||
<span>Apps:</span>
|
||||
<Stack
|
||||
@ -90,7 +90,7 @@ export const AgentRowPreview: FC<AgentRowPreviewProps> = ({
|
||||
<AppPreviewLink key={app.slug} app={app} />
|
||||
))}
|
||||
{agent.apps.length === 0 && (
|
||||
<span className={styles.agentDataValue}>None</span>
|
||||
<span css={styles.agentDataValue}>None</span>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
@ -100,8 +100,8 @@ export const AgentRowPreview: FC<AgentRowPreviewProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
agentRow: {
|
||||
const styles = {
|
||||
agentRow: (theme) => ({
|
||||
padding: theme.spacing(2, 4),
|
||||
backgroundColor: theme.palette.background.paperLight,
|
||||
fontSize: 16,
|
||||
@ -120,16 +120,16 @@ const useStyles = makeStyles((theme) => ({
|
||||
top: 0,
|
||||
left: 49,
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
agentStatusWrapper: {
|
||||
agentStatusWrapper: (theme) => ({
|
||||
width: theme.spacing(4.5),
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
flexShrink: 0,
|
||||
},
|
||||
}),
|
||||
|
||||
agentStatusPreview: {
|
||||
agentStatusPreview: (theme) => ({
|
||||
width: 10,
|
||||
height: 10,
|
||||
border: `2px solid ${theme.palette.text.secondary}`,
|
||||
@ -137,7 +137,7 @@ const useStyles = makeStyles((theme) => ({
|
||||
position: "relative",
|
||||
zIndex: 1,
|
||||
background: theme.palette.background.paper,
|
||||
},
|
||||
}),
|
||||
|
||||
agentName: {
|
||||
fontWeight: 600,
|
||||
@ -146,10 +146,9 @@ const useStyles = makeStyles((theme) => ({
|
||||
agentOS: {
|
||||
textTransform: "capitalize",
|
||||
fontSize: 14,
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
|
||||
agentData: {
|
||||
agentData: (theme) => ({
|
||||
fontSize: 14,
|
||||
color: theme.palette.text.secondary,
|
||||
|
||||
@ -157,36 +156,22 @@ const useStyles = makeStyles((theme) => ({
|
||||
gap: theme.spacing(2),
|
||||
flexWrap: "wrap",
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
agentDataName: {
|
||||
[theme.breakpoints.up("sm")]: {
|
||||
minWidth: ({ alignValues }: AgentRowPreviewStyles) =>
|
||||
alignValues ? 240 : undefined,
|
||||
},
|
||||
},
|
||||
|
||||
agentDataOS: {
|
||||
[theme.breakpoints.up("sm")]: {
|
||||
minWidth: ({ alignValues }: AgentRowPreviewStyles) =>
|
||||
alignValues ? 100 : undefined,
|
||||
},
|
||||
},
|
||||
|
||||
agentDataValue: {
|
||||
agentDataValue: (theme) => ({
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
}),
|
||||
|
||||
noShrink: {
|
||||
flexShrink: 0,
|
||||
},
|
||||
|
||||
agentDataItem: {
|
||||
agentDataItem: (theme) => ({
|
||||
[theme.breakpoints.down("md")]: {
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
gap: theme.spacing(1),
|
||||
width: "fit-content",
|
||||
},
|
||||
},
|
||||
}));
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { type Interpolation, type Theme } from "@emotion/react";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
import { WorkspaceAgent } from "api/typesGenerated";
|
||||
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne";
|
||||
import WarningRounded from "@mui/icons-material/WarningRounded";
|
||||
@ -19,27 +18,23 @@ import Link from "@mui/material/Link";
|
||||
// connected:shutdown_error, connected:off.
|
||||
|
||||
const ReadyLifecycle = () => {
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<div
|
||||
role="status"
|
||||
data-testid="agent-status-ready"
|
||||
aria-label="Ready"
|
||||
className={combineClasses([styles.status, styles.connected])}
|
||||
css={[styles.status, styles.connected]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const StartingLifecycle: React.FC = () => {
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<Tooltip title="Starting...">
|
||||
<div
|
||||
role="status"
|
||||
aria-label="Starting..."
|
||||
className={combineClasses([styles.status, styles.connecting])}
|
||||
css={[styles.status, styles.connecting]}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
@ -48,7 +43,6 @@ const StartingLifecycle: React.FC = () => {
|
||||
const StartTimeoutLifecycle: React.FC<{
|
||||
agent: WorkspaceAgent;
|
||||
}> = ({ agent }) => {
|
||||
const styles = useStyles();
|
||||
const anchorRef = useRef<SVGSVGElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const id = isOpen ? "timeout-popover" : undefined;
|
||||
@ -61,7 +55,7 @@ const StartTimeoutLifecycle: React.FC<{
|
||||
onMouseLeave={() => setIsOpen(false)}
|
||||
role="status"
|
||||
aria-label="Start timeout"
|
||||
className={styles.timeoutWarning}
|
||||
css={styles.timeoutWarning}
|
||||
/>
|
||||
<HelpPopover
|
||||
id={id}
|
||||
@ -90,7 +84,6 @@ const StartTimeoutLifecycle: React.FC<{
|
||||
const StartErrorLifecycle: React.FC<{
|
||||
agent: WorkspaceAgent;
|
||||
}> = ({ agent }) => {
|
||||
const styles = useStyles();
|
||||
const anchorRef = useRef<SVGSVGElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const id = isOpen ? "timeout-popover" : undefined;
|
||||
@ -103,7 +96,7 @@ const StartErrorLifecycle: React.FC<{
|
||||
onMouseLeave={() => setIsOpen(false)}
|
||||
role="status"
|
||||
aria-label="Start error"
|
||||
className={styles.errorWarning}
|
||||
css={styles.errorWarning}
|
||||
/>
|
||||
<HelpPopover
|
||||
id={id}
|
||||
@ -130,14 +123,12 @@ const StartErrorLifecycle: React.FC<{
|
||||
};
|
||||
|
||||
const ShuttingDownLifecycle: React.FC = () => {
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<Tooltip title="Stopping...">
|
||||
<div
|
||||
role="status"
|
||||
aria-label="Stopping..."
|
||||
className={combineClasses([styles.status, styles.connecting])}
|
||||
css={[styles.status, styles.connecting]}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
@ -146,7 +137,6 @@ const ShuttingDownLifecycle: React.FC = () => {
|
||||
const ShutdownTimeoutLifecycle: React.FC<{
|
||||
agent: WorkspaceAgent;
|
||||
}> = ({ agent }) => {
|
||||
const styles = useStyles();
|
||||
const anchorRef = useRef<SVGSVGElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const id = isOpen ? "timeout-popover" : undefined;
|
||||
@ -159,7 +149,7 @@ const ShutdownTimeoutLifecycle: React.FC<{
|
||||
onMouseLeave={() => setIsOpen(false)}
|
||||
role="status"
|
||||
aria-label="Stop timeout"
|
||||
className={styles.timeoutWarning}
|
||||
css={styles.timeoutWarning}
|
||||
/>
|
||||
<HelpPopover
|
||||
id={id}
|
||||
@ -188,7 +178,6 @@ const ShutdownTimeoutLifecycle: React.FC<{
|
||||
const ShutdownErrorLifecycle: React.FC<{
|
||||
agent: WorkspaceAgent;
|
||||
}> = ({ agent }) => {
|
||||
const styles = useStyles();
|
||||
const anchorRef = useRef<SVGSVGElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const id = isOpen ? "timeout-popover" : undefined;
|
||||
@ -201,7 +190,7 @@ const ShutdownErrorLifecycle: React.FC<{
|
||||
onMouseLeave={() => setIsOpen(false)}
|
||||
role="status"
|
||||
aria-label="Stop error"
|
||||
className={styles.errorWarning}
|
||||
css={styles.errorWarning}
|
||||
/>
|
||||
<HelpPopover
|
||||
id={id}
|
||||
@ -228,14 +217,12 @@ const ShutdownErrorLifecycle: React.FC<{
|
||||
};
|
||||
|
||||
const OffLifecycle: React.FC = () => {
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<Tooltip title="Stopped">
|
||||
<div
|
||||
role="status"
|
||||
aria-label="Stopped"
|
||||
className={combineClasses([styles.status, styles.disconnected])}
|
||||
css={[styles.status, styles.disconnected]}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
@ -280,28 +267,24 @@ const ConnectedStatus: React.FC<{
|
||||
};
|
||||
|
||||
const DisconnectedStatus: React.FC = () => {
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<Tooltip title="Disconnected">
|
||||
<div
|
||||
role="status"
|
||||
aria-label="Disconnected"
|
||||
className={combineClasses([styles.status, styles.disconnected])}
|
||||
css={[styles.status, styles.disconnected]}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const ConnectingStatus: React.FC = () => {
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<Tooltip title="Connecting...">
|
||||
<div
|
||||
role="status"
|
||||
aria-label="Connecting..."
|
||||
className={combineClasses([styles.status, styles.connecting])}
|
||||
css={[styles.status, styles.connecting]}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
@ -310,7 +293,6 @@ const ConnectingStatus: React.FC = () => {
|
||||
const TimeoutStatus: React.FC<{
|
||||
agent: WorkspaceAgent;
|
||||
}> = ({ agent }) => {
|
||||
const styles = useStyles();
|
||||
const anchorRef = useRef<SVGSVGElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const id = isOpen ? "timeout-popover" : undefined;
|
||||
@ -323,7 +305,7 @@ const TimeoutStatus: React.FC<{
|
||||
onMouseLeave={() => setIsOpen(false)}
|
||||
role="status"
|
||||
aria-label="Timeout"
|
||||
className={styles.timeoutWarning}
|
||||
css={styles.timeoutWarning}
|
||||
/>
|
||||
<HelpPopover
|
||||
id={id}
|
||||
@ -370,22 +352,22 @@ export const AgentStatus: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
status: {
|
||||
const styles = {
|
||||
status: (theme) => ({
|
||||
width: theme.spacing(1),
|
||||
height: theme.spacing(1),
|
||||
borderRadius: "100%",
|
||||
flexShrink: 0,
|
||||
},
|
||||
}),
|
||||
|
||||
connected: {
|
||||
connected: (theme) => ({
|
||||
backgroundColor: theme.palette.success.light,
|
||||
boxShadow: `0 0 12px 0 ${theme.palette.success.light}`,
|
||||
},
|
||||
}),
|
||||
|
||||
disconnected: {
|
||||
disconnected: (theme) => ({
|
||||
backgroundColor: theme.palette.text.secondary,
|
||||
},
|
||||
}),
|
||||
|
||||
"@keyframes pulse": {
|
||||
"0%": {
|
||||
@ -399,22 +381,22 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
},
|
||||
|
||||
connecting: {
|
||||
connecting: (theme) => ({
|
||||
backgroundColor: theme.palette.info.light,
|
||||
animation: "$pulse 1.5s 0.5s ease-in-out forwards infinite",
|
||||
},
|
||||
}),
|
||||
|
||||
timeoutWarning: {
|
||||
timeoutWarning: (theme) => ({
|
||||
color: theme.palette.warning.light,
|
||||
width: theme.spacing(2),
|
||||
height: theme.spacing(2),
|
||||
position: "relative",
|
||||
},
|
||||
}),
|
||||
|
||||
errorWarning: {
|
||||
errorWarning: (theme) => ({
|
||||
color: theme.palette.error.main,
|
||||
width: theme.spacing(2),
|
||||
height: theme.spacing(2),
|
||||
position: "relative",
|
||||
},
|
||||
}));
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { FC } from "react";
|
||||
import * as TypesGen from "api/typesGenerated";
|
||||
import { type FC } from "react";
|
||||
import type * as TypesGen from "api/typesGenerated";
|
||||
import { BaseIcon } from "./BaseIcon";
|
||||
import { ShareIcon } from "./ShareIcon";
|
||||
|
||||
@ -10,11 +9,22 @@ interface AppPreviewProps {
|
||||
}
|
||||
|
||||
export const AppPreviewLink: FC<AppPreviewProps> = ({ app }) => {
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
className={styles.appPreviewLink}
|
||||
css={(theme) => ({
|
||||
padding: theme.spacing(0.25, 1.5),
|
||||
borderRadius: 9999,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
color: theme.palette.text.primary,
|
||||
background: theme.palette.background.paper,
|
||||
flexShrink: 0,
|
||||
width: "fit-content",
|
||||
fontSize: 12,
|
||||
|
||||
"& img, & svg": {
|
||||
width: 13,
|
||||
},
|
||||
})}
|
||||
alignItems="center"
|
||||
direction="row"
|
||||
spacing={1}
|
||||
@ -25,20 +35,3 @@ export const AppPreviewLink: FC<AppPreviewProps> = ({ app }) => {
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
appPreviewLink: {
|
||||
padding: theme.spacing(0.25, 1.5),
|
||||
borderRadius: 9999,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
color: theme.palette.text.primary,
|
||||
background: theme.palette.background.paper,
|
||||
flexShrink: 0,
|
||||
width: "fit-content",
|
||||
fontSize: 12,
|
||||
|
||||
"& img, & svg": {
|
||||
width: 13,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { type Interpolation, type Theme } from "@emotion/react";
|
||||
import Button from "@mui/material/Button";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { type FC, useState } from "react";
|
||||
import type { WorkspaceAgent, WorkspaceResource } from "api/typesGenerated";
|
||||
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
|
||||
import { FC, useState } from "react";
|
||||
import { WorkspaceAgent, WorkspaceResource } from "api/typesGenerated";
|
||||
import { Stack } from "../Stack/Stack";
|
||||
import { ResourceCard } from "./ResourceCard";
|
||||
|
||||
@ -19,7 +19,6 @@ export const Resources: FC<React.PropsWithChildren<ResourcesProps>> = ({
|
||||
resources,
|
||||
agentRow,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
const [shouldDisplayHideResources, setShouldDisplayHideResources] =
|
||||
useState(false);
|
||||
const displayResources = shouldDisplayHideResources
|
||||
@ -40,9 +39,9 @@ export const Resources: FC<React.PropsWithChildren<ResourcesProps>> = ({
|
||||
/>
|
||||
))}
|
||||
{hasHideResources && (
|
||||
<div className={styles.buttonWrapper}>
|
||||
<div css={styles.buttonWrapper}>
|
||||
<Button
|
||||
className={styles.showMoreButton}
|
||||
css={styles.showMoreButton}
|
||||
size="small"
|
||||
onClick={() => setShouldDisplayHideResources((v) => !v)}
|
||||
>
|
||||
@ -55,17 +54,17 @@ export const Resources: FC<React.PropsWithChildren<ResourcesProps>> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
buttonWrapper: {
|
||||
const styles = {
|
||||
buttonWrapper: (theme) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
}),
|
||||
|
||||
showMoreButton: {
|
||||
borderRadius: 9999,
|
||||
width: "100%",
|
||||
maxWidth: 260,
|
||||
},
|
||||
}));
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { type Interpolation, type Theme } from "@emotion/react";
|
||||
import Chip from "@mui/material/Chip";
|
||||
import FormHelperText from "@mui/material/FormHelperText";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { FC } from "react";
|
||||
import { type FC } from "react";
|
||||
|
||||
export type MultiTextFieldProps = {
|
||||
label: string;
|
||||
@ -16,11 +16,9 @@ export const MultiTextField: FC<MultiTextFieldProps> = ({
|
||||
values,
|
||||
onChange,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label className={styles.root}>
|
||||
<label css={styles.root}>
|
||||
{values.map((value, index) => (
|
||||
<Chip
|
||||
key={index}
|
||||
@ -34,7 +32,7 @@ export const MultiTextField: FC<MultiTextFieldProps> = ({
|
||||
<input
|
||||
id={id}
|
||||
aria-label={label}
|
||||
className={styles.input}
|
||||
css={styles.input}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === ",") {
|
||||
event.preventDefault();
|
||||
@ -71,8 +69,8 @@ export const MultiTextField: FC<MultiTextFieldProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
const styles = {
|
||||
root: (theme) => ({
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
minHeight: theme.spacing(6), // Chip height + paddings
|
||||
@ -91,7 +89,7 @@ const useStyles = makeStyles((theme) => ({
|
||||
top: -1,
|
||||
left: -1,
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
input: {
|
||||
flexGrow: 1,
|
||||
@ -104,4 +102,4 @@ const useStyles = makeStyles((theme) => ({
|
||||
outline: "none",
|
||||
},
|
||||
},
|
||||
}));
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
@ -1,11 +1,4 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { FC } from "react";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
marginTop: theme.spacing(3),
|
||||
},
|
||||
}));
|
||||
import { type FC } from "react";
|
||||
|
||||
/**
|
||||
* SectionAction is a content box that call to actions should be placed
|
||||
@ -14,6 +7,13 @@ const useStyles = makeStyles((theme) => ({
|
||||
export const SectionAction: FC<React.PropsWithChildren<unknown>> = ({
|
||||
children,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
return <div className={styles.root}>{children}</div>;
|
||||
return (
|
||||
<div
|
||||
css={(theme) => ({
|
||||
marginTop: theme.spacing(3),
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,16 +1,14 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { Sidebar } from "./Sidebar";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { FC, Suspense } from "react";
|
||||
import { type FC, Suspense } from "react";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { pageTitle } from "utils/page";
|
||||
import { Margins } from "../Margins/Margins";
|
||||
import { useMe } from "hooks/useMe";
|
||||
import { Loader } from "components/Loader/Loader";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { Margins } from "../Margins/Margins";
|
||||
import { Sidebar } from "./Sidebar";
|
||||
|
||||
export const SettingsLayout: FC = () => {
|
||||
const styles = useStyles();
|
||||
const me = useMe();
|
||||
|
||||
return (
|
||||
@ -20,10 +18,21 @@ export const SettingsLayout: FC = () => {
|
||||
</Helmet>
|
||||
|
||||
<Margins>
|
||||
<Stack className={styles.wrapper} direction="row" spacing={6}>
|
||||
<Stack
|
||||
css={(theme) => ({
|
||||
padding: theme.spacing(6, 0),
|
||||
})}
|
||||
direction="row"
|
||||
spacing={6}
|
||||
>
|
||||
<Sidebar user={me} />
|
||||
<Suspense fallback={<Loader />}>
|
||||
<main className={styles.content}>
|
||||
<main
|
||||
css={{
|
||||
maxWidth: 800,
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Outlet />
|
||||
</main>
|
||||
</Suspense>
|
||||
@ -32,14 +41,3 @@ export const SettingsLayout: FC = () => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
wrapper: {
|
||||
padding: theme.spacing(6, 0),
|
||||
},
|
||||
|
||||
content: {
|
||||
maxWidth: 800,
|
||||
width: "100%",
|
||||
},
|
||||
}));
|
||||
|
@ -1,28 +1,73 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { css } from "@emotion/css";
|
||||
import {
|
||||
type CSSObject,
|
||||
type Interpolation,
|
||||
type Theme,
|
||||
useTheme,
|
||||
} from "@emotion/react";
|
||||
import VpnKeyOutlined from "@mui/icons-material/VpnKeyOutlined";
|
||||
import FingerprintOutlinedIcon from "@mui/icons-material/FingerprintOutlined";
|
||||
import { User } from "api/typesGenerated";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { UserAvatar } from "components/UserAvatar/UserAvatar";
|
||||
import { FC, ElementType, PropsWithChildren, ReactNode } from "react";
|
||||
import {
|
||||
type FC,
|
||||
type ComponentType,
|
||||
type PropsWithChildren,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
import AccountIcon from "@mui/icons-material/Person";
|
||||
import ScheduleIcon from "@mui/icons-material/EditCalendarOutlined";
|
||||
import SecurityIcon from "@mui/icons-material/LockOutlined";
|
||||
import type { User } from "api/typesGenerated";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { UserAvatar } from "components/UserAvatar/UserAvatar";
|
||||
import { useDashboard } from "components/Dashboard/DashboardProvider";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
|
||||
const SidebarNavItem: FC<
|
||||
PropsWithChildren<{ href: string; icon: ReactNode }>
|
||||
> = ({ children, href, icon }) => {
|
||||
const styles = useStyles();
|
||||
const theme = useTheme();
|
||||
|
||||
const sidebarNavItemStyles = css`
|
||||
color: inherit;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
padding: ${theme.spacing(1.5, 1.5, 1.5, 2)};
|
||||
border-radius: ${theme.shape.borderRadius / 2}px;
|
||||
transition: background-color 0.15s ease-in-out;
|
||||
margin-bottom: 1px;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background-color: theme.palette.action.hover;
|
||||
}
|
||||
`;
|
||||
|
||||
const sidebarNavItemActiveStyles = css`
|
||||
background-color: ${theme.palette.action.hover};
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 3px;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-color: ${theme.palette.secondary.dark};
|
||||
border-top-left-radius: ${theme.shape.borderRadius};
|
||||
border-bottom-left-radius: ${theme.shape.borderRadius};
|
||||
}
|
||||
`;
|
||||
|
||||
return (
|
||||
<NavLink
|
||||
to={href}
|
||||
className={({ isActive }) =>
|
||||
combineClasses([
|
||||
styles.sidebarNavItem,
|
||||
isActive ? styles.sidebarNavItemActive : undefined,
|
||||
sidebarNavItemStyles,
|
||||
isActive ? sidebarNavItemActiveStyles : undefined,
|
||||
])
|
||||
}
|
||||
>
|
||||
@ -34,26 +79,31 @@ const SidebarNavItem: FC<
|
||||
);
|
||||
};
|
||||
|
||||
const SidebarNavItemIcon: React.FC<{ icon: ElementType }> = ({
|
||||
icon: Icon,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
return <Icon className={styles.sidebarNavItemIcon} />;
|
||||
const SidebarNavItemIcon: React.FC<{
|
||||
icon: ComponentType<{ className?: string }>;
|
||||
}> = ({ icon: Icon }) => {
|
||||
return (
|
||||
<Icon
|
||||
css={(theme) => ({
|
||||
width: theme.spacing(2),
|
||||
height: theme.spacing(2),
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Sidebar: React.FC<{ user: User }> = ({ user }) => {
|
||||
const styles = useStyles();
|
||||
const { entitlements } = useDashboard();
|
||||
const allowAutostopRequirement =
|
||||
entitlements.features.template_autostop_requirement.enabled;
|
||||
|
||||
return (
|
||||
<nav className={styles.sidebar}>
|
||||
<Stack direction="row" alignItems="center" className={styles.userInfo}>
|
||||
<nav css={styles.sidebar}>
|
||||
<Stack direction="row" alignItems="center" css={styles.userInfo}>
|
||||
<UserAvatar username={user.username} avatarURL={user.avatar_url} />
|
||||
<Stack spacing={0} className={styles.userData}>
|
||||
<span className={styles.username}>{user.username}</span>
|
||||
<span className={styles.email}>{user.email}</span>
|
||||
<Stack spacing={0} css={styles.userData}>
|
||||
<span css={styles.username}>{user.username}</span>
|
||||
<span css={styles.email}>{user.email}</span>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
@ -93,50 +143,15 @@ export const Sidebar: React.FC<{ user: User }> = ({ user }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
const styles = {
|
||||
sidebar: {
|
||||
width: 245,
|
||||
flexShrink: 0,
|
||||
},
|
||||
sidebarNavItem: {
|
||||
color: "inherit",
|
||||
display: "block",
|
||||
fontSize: 14,
|
||||
textDecoration: "none",
|
||||
padding: theme.spacing(1.5, 1.5, 1.5, 2),
|
||||
borderRadius: theme.shape.borderRadius / 2,
|
||||
transition: "background-color 0.15s ease-in-out",
|
||||
marginBottom: 1,
|
||||
position: "relative",
|
||||
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
},
|
||||
},
|
||||
sidebarNavItemActive: {
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
|
||||
"&:before": {
|
||||
content: '""',
|
||||
display: "block",
|
||||
width: 3,
|
||||
height: "100%",
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0,
|
||||
backgroundColor: theme.palette.secondary.dark,
|
||||
borderTopLeftRadius: theme.shape.borderRadius,
|
||||
borderBottomLeftRadius: theme.shape.borderRadius,
|
||||
},
|
||||
},
|
||||
sidebarNavItemIcon: {
|
||||
width: theme.spacing(2),
|
||||
height: theme.spacing(2),
|
||||
},
|
||||
userInfo: {
|
||||
...theme.typography.body2,
|
||||
userInfo: (theme) => ({
|
||||
...(theme.typography.body2 as CSSObject),
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
}),
|
||||
userData: {
|
||||
overflow: "hidden",
|
||||
},
|
||||
@ -146,10 +161,10 @@ const useStyles = makeStyles((theme) => ({
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
},
|
||||
email: {
|
||||
email: (theme) => ({
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: 12,
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
},
|
||||
}));
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { ComponentProps, FC } from "react";
|
||||
import { type ComponentProps, type FC } from "react";
|
||||
import Editor, { DiffEditor, loader } from "@monaco-editor/react";
|
||||
import * as monaco from "monaco-editor";
|
||||
import { useCoderTheme } from "./coderTheme";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
|
||||
loader.config({ monaco });
|
||||
|
||||
@ -13,7 +12,6 @@ export const SyntaxHighlighter: FC<{
|
||||
ComponentProps<typeof DiffEditor>;
|
||||
compareWith?: string;
|
||||
}> = ({ value, compareWith, language, editorProps }) => {
|
||||
const styles = useStyles();
|
||||
const hasDiff = compareWith && value !== compareWith;
|
||||
const coderTheme = useCoderTheme();
|
||||
const commonProps = {
|
||||
@ -35,7 +33,13 @@ export const SyntaxHighlighter: FC<{
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div
|
||||
css={(theme) => ({
|
||||
padding: theme.spacing(1, 0),
|
||||
background: theme.palette.background.paper,
|
||||
height: "100%",
|
||||
})}
|
||||
>
|
||||
{hasDiff ? (
|
||||
<DiffEditor original={compareWith} modified={value} {...commonProps} />
|
||||
) : (
|
||||
@ -44,11 +48,3 @@ export const SyntaxHighlighter: FC<{
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
wrapper: {
|
||||
padding: theme.spacing(1, 0),
|
||||
background: theme.palette.background.paper,
|
||||
height: "100%",
|
||||
},
|
||||
}));
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { TemplateExample } from "api/typesGenerated";
|
||||
import { FC } from "react";
|
||||
import { type Interpolation, type Theme } from "@emotion/react";
|
||||
import type { TemplateExample } from "api/typesGenerated";
|
||||
import { type FC } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
|
||||
export interface TemplateExampleCardProps {
|
||||
example: TemplateExample;
|
||||
@ -13,29 +12,26 @@ export const TemplateExampleCard: FC<TemplateExampleCardProps> = ({
|
||||
example,
|
||||
className,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={`/starter-templates/${example.id}`}
|
||||
className={combineClasses([styles.template, className])}
|
||||
css={styles.template}
|
||||
className={className}
|
||||
key={example.id}
|
||||
>
|
||||
<div className={styles.templateIcon}>
|
||||
<div css={styles.templateIcon}>
|
||||
<img src={example.icon} alt="" />
|
||||
</div>
|
||||
<div className={styles.templateInfo}>
|
||||
<span className={styles.templateName}>{example.name}</span>
|
||||
<span className={styles.templateDescription}>
|
||||
{example.description}
|
||||
</span>
|
||||
<div css={styles.templateInfo}>
|
||||
<span css={styles.templateName}>{example.name}</span>
|
||||
<span css={styles.templateDescription}>{example.description}</span>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
template: {
|
||||
const styles = {
|
||||
template: (theme) => ({
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
background: theme.palette.background.paper,
|
||||
@ -49,9 +45,9 @@ const useStyles = makeStyles((theme) => ({
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.background.paperLight,
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
templateIcon: {
|
||||
templateIcon: (theme) => ({
|
||||
width: theme.spacing(12),
|
||||
height: theme.spacing(12),
|
||||
display: "flex",
|
||||
@ -62,29 +58,29 @@ const useStyles = makeStyles((theme) => ({
|
||||
"& img": {
|
||||
height: theme.spacing(4),
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
templateInfo: {
|
||||
templateInfo: (theme) => ({
|
||||
padding: theme.spacing(2, 2, 2, 0),
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflow: "hidden",
|
||||
},
|
||||
}),
|
||||
|
||||
templateName: {
|
||||
templateName: (theme) => ({
|
||||
fontSize: theme.spacing(2),
|
||||
textOverflow: "ellipsis",
|
||||
width: "100%",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
},
|
||||
}),
|
||||
|
||||
templateDescription: {
|
||||
templateDescription: (theme) => ({
|
||||
fontSize: theme.spacing(1.75),
|
||||
color: theme.palette.text.secondary,
|
||||
textOverflow: "ellipsis",
|
||||
width: "100%",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
},
|
||||
}));
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { type Interpolation, type Theme } from "@emotion/react";
|
||||
import { type FC } from "react";
|
||||
import { DockerIcon } from "components/Icons/DockerIcon";
|
||||
import { MarkdownIcon } from "components/Icons/MarkdownIcon";
|
||||
import { TerraformIcon } from "components/Icons/TerraformIcon";
|
||||
import { SyntaxHighlighter } from "components/SyntaxHighlighter/SyntaxHighlighter";
|
||||
import { UseTabResult } from "hooks/useTab";
|
||||
import { FC } from "react";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
import { AllowedExtension, TemplateVersionFiles } from "utils/templateVersion";
|
||||
import InsertDriveFileOutlined from "@mui/icons-material/InsertDriveFileOutlined";
|
||||
|
||||
@ -43,15 +42,14 @@ export const TemplateFiles: FC<{
|
||||
previousFiles?: TemplateVersionFiles;
|
||||
tab: UseTabResult;
|
||||
}> = ({ currentFiles, previousFiles, tab }) => {
|
||||
const styles = useStyles();
|
||||
const filenames = Object.keys(currentFiles);
|
||||
const selectedFilename = filenames[Number(tab.value)];
|
||||
const currentFile = currentFiles[selectedFilename];
|
||||
const previousFile = previousFiles && previousFiles[selectedFilename];
|
||||
|
||||
return (
|
||||
<div className={styles.files}>
|
||||
<div className={styles.tabs}>
|
||||
<div css={styles.files}>
|
||||
<div css={styles.tabs}>
|
||||
{filenames.map((filename, index) => {
|
||||
const tabValue = index.toString();
|
||||
const extension = getExtension(filename) as AllowedExtension;
|
||||
@ -63,10 +61,7 @@ export const TemplateFiles: FC<{
|
||||
|
||||
return (
|
||||
<button
|
||||
className={combineClasses({
|
||||
[styles.tab]: true,
|
||||
[styles.tabActive]: tabValue === tab.value,
|
||||
})}
|
||||
css={[styles.tab, tabValue === tab.value && styles.tabActive]}
|
||||
onClick={() => {
|
||||
tab.set(tabValue);
|
||||
}}
|
||||
@ -74,7 +69,7 @@ export const TemplateFiles: FC<{
|
||||
>
|
||||
{icon}
|
||||
{filename}
|
||||
{hasDiff && <div className={styles.tabDiff} />}
|
||||
{hasDiff && <div css={styles.tabDiff} />}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
@ -92,16 +87,16 @@ export const TemplateFiles: FC<{
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
tabs: {
|
||||
const styles = {
|
||||
tabs: (theme) => ({
|
||||
display: "flex",
|
||||
alignItems: "baseline",
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
gap: 1,
|
||||
overflowX: "auto",
|
||||
},
|
||||
}),
|
||||
|
||||
tab: {
|
||||
tab: (theme) => ({
|
||||
background: "transparent",
|
||||
border: 0,
|
||||
padding: theme.spacing(0, 3),
|
||||
@ -123,9 +118,9 @@ const useStyles = makeStyles((theme) => ({
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
tabActive: {
|
||||
tabActive: (theme) => ({
|
||||
opacity: 1,
|
||||
background: theme.palette.action.hover,
|
||||
color: theme.palette.text.primary,
|
||||
@ -140,26 +135,26 @@ const useStyles = makeStyles((theme) => ({
|
||||
backgroundColor: theme.palette.secondary.dark,
|
||||
position: "absolute",
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
tabDiff: {
|
||||
tabDiff: (theme) => ({
|
||||
height: 6,
|
||||
width: 6,
|
||||
backgroundColor: theme.palette.warning.light,
|
||||
borderRadius: "100%",
|
||||
marginLeft: theme.spacing(0.5),
|
||||
},
|
||||
}),
|
||||
|
||||
codeWrapper: {
|
||||
codeWrapper: (theme) => ({
|
||||
background: theme.palette.background.paperLight,
|
||||
},
|
||||
}),
|
||||
|
||||
files: {
|
||||
files: (theme) => ({
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
},
|
||||
}),
|
||||
|
||||
prism: {
|
||||
borderRadius: 0,
|
||||
},
|
||||
}));
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import TableRow, { TableRowProps } from "@mui/material/TableRow";
|
||||
import TableRow, { type TableRowProps } from "@mui/material/TableRow";
|
||||
import { type PropsWithChildren, forwardRef } from "react";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
|
||||
type TimelineEntryProps = PropsWithChildren<
|
||||
TableRowProps & {
|
||||
@ -13,47 +11,39 @@ export const TimelineEntry = forwardRef(function TimelineEntry(
|
||||
{ children, clickable = true, ...props }: TimelineEntryProps,
|
||||
ref?: React.ForwardedRef<HTMLTableRowElement>,
|
||||
) {
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
ref={ref}
|
||||
className={combineClasses({
|
||||
[styles.timelineEntry]: true,
|
||||
[styles.clickable]: clickable,
|
||||
})}
|
||||
css={(theme) => [
|
||||
{
|
||||
position: "relative",
|
||||
"&:focus": {
|
||||
outlineStyle: "solid",
|
||||
outlineOffset: -1,
|
||||
outlineWidth: 2,
|
||||
outlineColor: theme.palette.secondary.dark,
|
||||
},
|
||||
"& td:before": {
|
||||
position: "absolute",
|
||||
left: 50,
|
||||
display: "block",
|
||||
content: "''",
|
||||
height: "100%",
|
||||
width: 2,
|
||||
background: theme.palette.divider,
|
||||
},
|
||||
},
|
||||
clickable && {
|
||||
cursor: "pointer",
|
||||
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
},
|
||||
},
|
||||
]}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</TableRow>
|
||||
);
|
||||
});
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
clickable: {
|
||||
cursor: "pointer",
|
||||
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
},
|
||||
},
|
||||
|
||||
timelineEntry: {
|
||||
position: "relative",
|
||||
"&:focus": {
|
||||
outlineStyle: "solid",
|
||||
outlineOffset: -1,
|
||||
outlineWidth: 2,
|
||||
outlineColor: theme.palette.secondary.dark,
|
||||
},
|
||||
"& td:before": {
|
||||
position: "absolute",
|
||||
left: 50,
|
||||
display: "block",
|
||||
content: "''",
|
||||
height: "100%",
|
||||
width: 2,
|
||||
background: theme.palette.divider,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
@ -1,21 +1,19 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { Template, TemplateExample } from "api/typesGenerated";
|
||||
import { type FC } from "react";
|
||||
import type { Template, TemplateExample } from "api/typesGenerated";
|
||||
import { Avatar } from "components/Avatar/Avatar";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { FC } from "react";
|
||||
import { type Interpolation, type Theme } from "@emotion/react";
|
||||
|
||||
export interface SelectedTemplateProps {
|
||||
template: Template | TemplateExample;
|
||||
}
|
||||
|
||||
export const SelectedTemplate: FC<SelectedTemplateProps> = ({ template }) => {
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={3}
|
||||
className={styles.template}
|
||||
css={styles.template}
|
||||
alignItems="center"
|
||||
>
|
||||
<Avatar
|
||||
@ -27,35 +25,33 @@ export const SelectedTemplate: FC<SelectedTemplateProps> = ({ template }) => {
|
||||
</Avatar>
|
||||
|
||||
<Stack direction="column" spacing={0}>
|
||||
<span className={styles.templateName}>
|
||||
<span css={styles.templateName}>
|
||||
{"display_name" in template && template.display_name.length > 0
|
||||
? template.display_name
|
||||
: template.name}
|
||||
</span>
|
||||
{template.description && (
|
||||
<span className={styles.templateDescription}>
|
||||
{template.description}
|
||||
</span>
|
||||
<span css={styles.templateDescription}>{template.description}</span>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
template: {
|
||||
const styles = {
|
||||
template: (theme) => ({
|
||||
padding: theme.spacing(2.5, 3),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
},
|
||||
}),
|
||||
|
||||
templateName: {
|
||||
fontSize: 16,
|
||||
},
|
||||
|
||||
templateDescription: {
|
||||
templateDescription: (theme) => ({
|
||||
fontSize: 14,
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
}));
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
@ -1,16 +1,15 @@
|
||||
import { type CSSObject, type Interpolation, type Theme } from "@emotion/react";
|
||||
import Button from "@mui/material/Button";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import TableCell from "@mui/material/TableCell";
|
||||
import { TemplateVersion } from "api/typesGenerated";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import type { TemplateVersion } from "api/typesGenerated";
|
||||
import { Pill } from "components/Pill/Pill";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { TimelineEntry } from "components/Timeline/TimelineEntry";
|
||||
import { UserAvatar } from "components/UserAvatar/UserAvatar";
|
||||
import { InfoTooltip } from "components/InfoTooltip/InfoTooltip";
|
||||
import { useClickableTableRow } from "hooks/useClickableTableRow";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { colors } from "theme/colors";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
|
||||
export interface VersionRowProps {
|
||||
version: TemplateVersion;
|
||||
@ -27,7 +26,6 @@ export const VersionRow: React.FC<VersionRowProps> = ({
|
||||
onPromoteClick,
|
||||
onArchiveClick,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const clickableProps = useClickableTableRow({
|
||||
@ -40,17 +38,14 @@ export const VersionRow: React.FC<VersionRowProps> = ({
|
||||
<TimelineEntry
|
||||
data-testid={`version-${version.id}`}
|
||||
{...clickableProps}
|
||||
className={combineClasses({
|
||||
[clickableProps.className]: true,
|
||||
[styles.row]: true,
|
||||
[styles.active]: isActive,
|
||||
})}
|
||||
css={[styles.row, isActive && styles.active]}
|
||||
className={clickableProps.className}
|
||||
>
|
||||
<TableCell className={styles.versionCell}>
|
||||
<TableCell css={styles.versionCell}>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
className={styles.versionWrapper}
|
||||
css={styles.versionWrapper}
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Stack direction="row" alignItems="center">
|
||||
@ -59,7 +54,7 @@ export const VersionRow: React.FC<VersionRowProps> = ({
|
||||
avatarURL={version.created_by.avatar_url}
|
||||
/>
|
||||
<Stack
|
||||
className={styles.versionSummary}
|
||||
css={styles.versionSummary}
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
spacing={1}
|
||||
@ -73,7 +68,7 @@ export const VersionRow: React.FC<VersionRowProps> = ({
|
||||
<InfoTooltip title="Message" message={version.message} />
|
||||
)}
|
||||
|
||||
<span className={styles.versionTime}>
|
||||
<span css={styles.versionTime}>
|
||||
{new Date(version.created_at).toLocaleTimeString()}
|
||||
</span>
|
||||
</Stack>
|
||||
@ -94,7 +89,7 @@ export const VersionRow: React.FC<VersionRowProps> = ({
|
||||
{jobStatus === "failed" && <Pill text="Failed" type="error" />}
|
||||
{jobStatus === "failed" ? (
|
||||
<Button
|
||||
className={styles.promoteButton}
|
||||
css={styles.promoteButton}
|
||||
disabled={isActive || version.archived}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
@ -108,7 +103,7 @@ export const VersionRow: React.FC<VersionRowProps> = ({
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={styles.promoteButton}
|
||||
css={styles.promoteButton}
|
||||
disabled={isActive || jobStatus !== "succeeded"}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
@ -128,8 +123,8 @@ export const VersionRow: React.FC<VersionRowProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
row: {
|
||||
const styles = {
|
||||
row: (theme) => ({
|
||||
"&:hover $promoteButton": {
|
||||
color: theme.palette.text.primary,
|
||||
borderColor: colors.gray[11],
|
||||
@ -137,20 +132,20 @@ const useStyles = makeStyles((theme) => ({
|
||||
borderColor: theme.palette.text.primary,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
promoteButton: {
|
||||
promoteButton: (theme) => ({
|
||||
color: theme.palette.text.secondary,
|
||||
transition: "none",
|
||||
},
|
||||
}),
|
||||
|
||||
versionWrapper: {
|
||||
versionWrapper: (theme) => ({
|
||||
padding: theme.spacing(2, 4),
|
||||
},
|
||||
}),
|
||||
|
||||
active: {
|
||||
active: (theme) => ({
|
||||
backgroundColor: theme.palette.background.paperLight,
|
||||
},
|
||||
}),
|
||||
|
||||
versionCell: {
|
||||
padding: "0 !important",
|
||||
@ -158,13 +153,13 @@ const useStyles = makeStyles((theme) => ({
|
||||
borderBottom: 0,
|
||||
},
|
||||
|
||||
versionSummary: {
|
||||
...theme.typography.body1,
|
||||
versionSummary: (theme) => ({
|
||||
...(theme.typography.body1 as CSSObject),
|
||||
fontFamily: "inherit",
|
||||
},
|
||||
}),
|
||||
|
||||
versionTime: {
|
||||
versionTime: (theme) => ({
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: 12,
|
||||
},
|
||||
}));
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { type Interpolation, type Theme } from "@emotion/react";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import { Template, UpdateTemplateMeta } from "api/typesGenerated";
|
||||
import { FormikContextType, FormikTouched, useFormik } from "formik";
|
||||
import { FC } from "react";
|
||||
import type { Template, UpdateTemplateMeta } from "api/typesGenerated";
|
||||
import { type FormikContextType, type FormikTouched, useFormik } from "formik";
|
||||
import { type FC } from "react";
|
||||
import {
|
||||
getFormHelpers,
|
||||
nameValidator,
|
||||
@ -23,7 +24,6 @@ import {
|
||||
HelpTooltip,
|
||||
HelpTooltipText,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
|
||||
const MAX_DESCRIPTION_CHAR_LIMIT = 128;
|
||||
|
||||
@ -79,7 +79,6 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
||||
initialTouched,
|
||||
});
|
||||
const getFieldHelpers = getFormHelpers(form, error);
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<HorizontalForm
|
||||
@ -154,7 +153,7 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
spacing={0.5}
|
||||
className={styles.optionText}
|
||||
css={styles.optionText}
|
||||
>
|
||||
Allow users to cancel in-progress workspace jobs.
|
||||
<HelpTooltip>
|
||||
@ -163,7 +162,7 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
||||
</HelpTooltipText>
|
||||
</HelpTooltip>
|
||||
</Stack>
|
||||
<span className={styles.optionHelperText}>
|
||||
<span css={styles.optionHelperText}>
|
||||
Depending on your template, canceling builds may leave
|
||||
workspaces in an unhealthy state. This option isn't
|
||||
recommended for most use cases.
|
||||
@ -186,7 +185,7 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
spacing={0.5}
|
||||
className={styles.optionText}
|
||||
css={styles.optionText}
|
||||
>
|
||||
Require the active template version for workspace builds.
|
||||
<HelpTooltip>
|
||||
@ -195,7 +194,7 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
||||
</HelpTooltipText>
|
||||
</HelpTooltip>
|
||||
</Stack>
|
||||
<span className={styles.optionHelperText}>
|
||||
<span css={styles.optionHelperText}>
|
||||
Workspaces that are manually started or auto-started will
|
||||
use the promoted template version.
|
||||
</span>
|
||||
@ -211,14 +210,14 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
optionText: {
|
||||
const styles = {
|
||||
optionText: (theme) => ({
|
||||
fontSize: theme.spacing(2),
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
}),
|
||||
|
||||
optionHelperText: {
|
||||
optionHelperText: (theme) => ({
|
||||
fontSize: theme.spacing(1.5),
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
}));
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
@ -1,18 +1,17 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { Sidebar } from "./Sidebar";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { createContext, FC, Suspense, useContext } from "react";
|
||||
import { createContext, type FC, Suspense, useContext } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useQuery } from "react-query";
|
||||
import { pageTitle } from "utils/page";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { Loader } from "components/Loader/Loader";
|
||||
import { Outlet, useParams } from "react-router-dom";
|
||||
import { Margins } from "components/Margins/Margins";
|
||||
import { useQuery } from "react-query";
|
||||
import { useOrganizationId } from "hooks/useOrganizationId";
|
||||
import { templateByName } from "api/queries/templates";
|
||||
import { type AuthorizationResponse, type Template } from "api/typesGenerated";
|
||||
import type { AuthorizationResponse, Template } from "api/typesGenerated";
|
||||
import { ErrorAlert } from "components/Alert/ErrorAlert";
|
||||
import { checkAuthorization } from "api/queries/authCheck";
|
||||
import { Sidebar } from "./Sidebar";
|
||||
|
||||
const TemplateSettings = createContext<
|
||||
{ template: Template; permissions: AuthorizationResponse } | undefined
|
||||
@ -28,7 +27,6 @@ export function useTemplateSettings() {
|
||||
}
|
||||
|
||||
export const TemplateSettingsLayout: FC = () => {
|
||||
const styles = useStyles();
|
||||
const orgId = useOrganizationId();
|
||||
const { template: templateName } = useParams() as { template: string };
|
||||
const templateQuery = useQuery(templateByName(orgId, templateName));
|
||||
@ -58,7 +56,13 @@ export const TemplateSettingsLayout: FC = () => {
|
||||
</Helmet>
|
||||
|
||||
<Margins>
|
||||
<Stack className={styles.wrapper} direction="row" spacing={10}>
|
||||
<Stack
|
||||
css={(theme) => ({
|
||||
padding: theme.spacing(6, 0),
|
||||
})}
|
||||
direction="row"
|
||||
spacing={10}
|
||||
>
|
||||
{templateQuery.isError || permissionsQuery.isError ? (
|
||||
<ErrorAlert error={templateQuery.error} />
|
||||
) : (
|
||||
@ -70,7 +74,11 @@ export const TemplateSettingsLayout: FC = () => {
|
||||
>
|
||||
<Sidebar template={templateQuery.data} />
|
||||
<Suspense fallback={<Loader />}>
|
||||
<main className={styles.content}>
|
||||
<main
|
||||
css={{
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Outlet />
|
||||
</main>
|
||||
</Suspense>
|
||||
@ -81,13 +89,3 @@ export const TemplateSettingsLayout: FC = () => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
wrapper: {
|
||||
padding: theme.spacing(6, 0),
|
||||
},
|
||||
|
||||
content: {
|
||||
width: "100%",
|
||||
},
|
||||
}));
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import TreeView from "@mui/lab/TreeView";
|
||||
import TreeItem from "@mui/lab/TreeItem";
|
||||
import Menu from "@mui/material/Menu";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import { CSSProperties, FC, useState } from "react";
|
||||
import { type CSSProperties, type FC, useState } from "react";
|
||||
import { css } from "@emotion/react";
|
||||
import { FileTree } from "utils/filetree";
|
||||
import { DockerIcon } from "components/Icons/DockerIcon";
|
||||
import { colors } from "theme/colors";
|
||||
@ -35,7 +35,6 @@ export const FileTreeView: FC<{
|
||||
fileTree: FileTree;
|
||||
activePath?: string;
|
||||
}> = ({ fileTree, activePath, onDelete, onRename, onSelect }) => {
|
||||
const styles = useStyles();
|
||||
const [contextMenu, setContextMenu] = useState<ContextMenu | undefined>();
|
||||
|
||||
const buildTreeItems = (
|
||||
@ -60,9 +59,54 @@ export const FileTreeView: FC<{
|
||||
nodeId={currentPath}
|
||||
key={currentPath}
|
||||
label={filename}
|
||||
className={`${styles.fileTreeItem} ${
|
||||
currentPath === activePath ? "active" : ""
|
||||
}`}
|
||||
css={(theme) => css`
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
|
||||
&:focus:not(.active) > .MuiTreeItem-content {
|
||||
background: ${theme.palette.action.hover};
|
||||
color: ${theme.palette.text.primary};
|
||||
}
|
||||
|
||||
&:not(:focus):not(.active) > .MuiTreeItem-content:hover {
|
||||
background: ${theme.palette.action.hover};
|
||||
color: ${theme.palette.text.primary};
|
||||
}
|
||||
|
||||
& > .MuiTreeItem-content {
|
||||
padding: ${theme.spacing(0.25, 2)};
|
||||
color: ${theme.palette.text.secondary};
|
||||
|
||||
& svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
& > .MuiTreeItem-label {
|
||||
margin-left: 4px;
|
||||
font-size: 13px;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
& > .MuiTreeItem-content {
|
||||
color: ${theme.palette.text.primary};
|
||||
background: ${colors.gray[13]};
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
& .MuiTreeItem-group {
|
||||
margin-left: 0;
|
||||
|
||||
// We need to find a better way to recursive padding here
|
||||
& .MuiTreeItem-content {
|
||||
padding-left: calc(var(--level) * ${theme.spacing(5)});
|
||||
}
|
||||
}
|
||||
`}
|
||||
className={currentPath === activePath ? "active" : ""}
|
||||
onClick={() => {
|
||||
onSelect(currentPath);
|
||||
}}
|
||||
@ -161,60 +205,6 @@ export const FileTreeView: FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
fileTreeItem: {
|
||||
overflow: "hidden",
|
||||
userSelect: "none",
|
||||
|
||||
"&:focus:not(.active) > .MuiTreeItem-content": {
|
||||
background: theme.palette.action.hover,
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
|
||||
"&:not(:focus):not(.active) > .MuiTreeItem-content:hover": {
|
||||
background: theme.palette.action.hover,
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
|
||||
"& > .MuiTreeItem-content": {
|
||||
padding: theme.spacing(0.25, 2),
|
||||
color: theme.palette.text.secondary,
|
||||
|
||||
"& svg": {
|
||||
width: 16,
|
||||
height: 16,
|
||||
},
|
||||
|
||||
"& > .MuiTreeItem-label": {
|
||||
marginLeft: 4,
|
||||
fontSize: 13,
|
||||
color: "inherit",
|
||||
},
|
||||
},
|
||||
|
||||
"&.active": {
|
||||
"& > .MuiTreeItem-content": {
|
||||
color: theme.palette.text.primary,
|
||||
background: colors.gray[13],
|
||||
pointerEvents: "none",
|
||||
},
|
||||
},
|
||||
|
||||
"& .MuiTreeItem-group": {
|
||||
marginLeft: 0,
|
||||
|
||||
// We need to find a better way to recursive padding here
|
||||
"& .MuiTreeItem-content": {
|
||||
paddingLeft: `calc(var(--level) * ${theme.spacing(5)})`,
|
||||
},
|
||||
},
|
||||
},
|
||||
editor: {
|
||||
flex: 1,
|
||||
},
|
||||
preview: {},
|
||||
}));
|
||||
|
||||
const FileTypeTerraform = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="#813cf3">
|
||||
<title>file_type_terraform</title>
|
||||
|
@ -1,12 +1,16 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { css } from "@emotion/css";
|
||||
import { useTheme, type Interpolation, type Theme } from "@emotion/react";
|
||||
import Dialog from "@mui/material/Dialog";
|
||||
import DialogContent from "@mui/material/DialogContent";
|
||||
import DialogContentText from "@mui/material/DialogContentText";
|
||||
import DialogTitle from "@mui/material/DialogTitle";
|
||||
import { DialogProps } from "components/Dialogs/Dialog";
|
||||
import { FC, useEffect, useState } from "react";
|
||||
import { type DialogProps } from "components/Dialogs/Dialog";
|
||||
import { type FC, useEffect, useState } from "react";
|
||||
import { FormFields, VerticalForm } from "components/Form/Form";
|
||||
import { TemplateVersionVariable, VariableValue } from "api/typesGenerated";
|
||||
import type {
|
||||
TemplateVersionVariable,
|
||||
VariableValue,
|
||||
} from "api/typesGenerated";
|
||||
import DialogActions from "@mui/material/DialogActions";
|
||||
import Button from "@mui/material/Button";
|
||||
import { VariableInput } from "pages/CreateTemplatePage/VariableInput";
|
||||
@ -24,7 +28,7 @@ export type MissingTemplateVariablesDialogProps = Omit<
|
||||
export const MissingTemplateVariablesDialog: FC<
|
||||
MissingTemplateVariablesDialogProps
|
||||
> = ({ missingVariables, onSubmit, ...dialogProps }) => {
|
||||
const styles = useStyles();
|
||||
const theme = useTheme();
|
||||
const [variableValues, setVariableValues] = useState<VariableValue[]>([]);
|
||||
|
||||
// Pre-fill the form with the default values when missing variables are loaded
|
||||
@ -47,16 +51,25 @@ export const MissingTemplateVariablesDialog: FC<
|
||||
>
|
||||
<DialogTitle
|
||||
id="update-build-parameters-title"
|
||||
classes={{ root: styles.title }}
|
||||
classes={{
|
||||
root: css`
|
||||
padding: ${theme.spacing(3, 5)};
|
||||
|
||||
& h2 {
|
||||
font-size: ${theme.spacing(2.5)};
|
||||
font-weight: 400;
|
||||
}
|
||||
`,
|
||||
}}
|
||||
>
|
||||
Template variables
|
||||
</DialogTitle>
|
||||
<DialogContent className={styles.content}>
|
||||
<DialogContentText className={styles.info}>
|
||||
<DialogContent css={styles.content}>
|
||||
<DialogContentText css={styles.info}>
|
||||
There are a few missing template variable values. Please fill them in.
|
||||
</DialogContentText>
|
||||
<VerticalForm
|
||||
className={styles.form}
|
||||
css={styles.form}
|
||||
id="updateVariables"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
@ -89,7 +102,7 @@ export const MissingTemplateVariablesDialog: FC<
|
||||
)}
|
||||
</VerticalForm>
|
||||
</DialogContent>
|
||||
<DialogActions disableSpacing className={styles.dialogActions}>
|
||||
<DialogActions disableSpacing css={styles.dialogActions}>
|
||||
<Button color="primary" fullWidth type="submit" form="updateVariables">
|
||||
Submit
|
||||
</Button>
|
||||
@ -101,43 +114,22 @@ export const MissingTemplateVariablesDialog: FC<
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
title: {
|
||||
padding: theme.spacing(3, 5),
|
||||
|
||||
"& h2": {
|
||||
fontSize: theme.spacing(2.5),
|
||||
fontWeight: 400,
|
||||
},
|
||||
},
|
||||
|
||||
content: {
|
||||
padding: theme.spacing(0, 5, 0, 5),
|
||||
},
|
||||
const styles = {
|
||||
content: (theme) => ({
|
||||
padding: theme.spacing(0, 5),
|
||||
}),
|
||||
|
||||
info: {
|
||||
margin: 0,
|
||||
},
|
||||
|
||||
form: {
|
||||
form: (theme) => ({
|
||||
paddingTop: theme.spacing(4),
|
||||
},
|
||||
}),
|
||||
|
||||
infoTitle: {
|
||||
fontSize: theme.spacing(2),
|
||||
fontWeight: 600,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing(1),
|
||||
},
|
||||
|
||||
formFooter: {
|
||||
flexDirection: "column",
|
||||
},
|
||||
|
||||
dialogActions: {
|
||||
dialogActions: (theme) => ({
|
||||
padding: theme.spacing(5),
|
||||
flexDirection: "column",
|
||||
gap: theme.spacing(1),
|
||||
},
|
||||
}));
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
@ -5,7 +5,6 @@ import FormGroup from "@mui/material/FormGroup";
|
||||
import FormHelperText from "@mui/material/FormHelperText";
|
||||
import FormLabel from "@mui/material/FormLabel";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import Switch from "@mui/material/Switch";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import {
|
||||
@ -200,8 +199,6 @@ export const WorkspaceScheduleForm: FC<
|
||||
enableAutoStop,
|
||||
enableAutoStart,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
|
||||
const form = useFormik<WorkspaceScheduleFormValues>({
|
||||
initialValues,
|
||||
onSubmit,
|
||||
@ -340,11 +337,23 @@ export const WorkspaceScheduleForm: FC<
|
||||
</Stack>
|
||||
|
||||
<FormControl component="fieldset" error={Boolean(form.errors.monday)}>
|
||||
<FormLabel className={styles.daysOfWeekLabel} component="legend">
|
||||
<FormLabel
|
||||
css={{
|
||||
fontSize: 12,
|
||||
}}
|
||||
component="legend"
|
||||
>
|
||||
{Language.daysOfWeekLabel}
|
||||
</FormLabel>
|
||||
|
||||
<FormGroup className={styles.daysOfWeekOptions}>
|
||||
<FormGroup
|
||||
css={(theme) => ({
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
paddingTop: theme.spacing(0.5),
|
||||
})}
|
||||
>
|
||||
{checkboxes.map((checkbox) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
@ -422,15 +431,3 @@ export const ttlShutdownAt = (formTTL: number): string => {
|
||||
.humanize()} ${Language.ttlCausesShutdownAfterStart}.`;
|
||||
}
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
daysOfWeekLabel: {
|
||||
fontSize: 12,
|
||||
},
|
||||
daysOfWeekOptions: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
paddingTop: theme.spacing(0.5),
|
||||
},
|
||||
}));
|
||||
|
Reference in New Issue
Block a user