Skip to content
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

Show User and Workspace live manifests on UI #845

Merged
merged 2 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions web/dashboard-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@types/node": "20.11.15",
"base64url": "^3.0.1",
"copy-to-clipboard": "3.3.3",
"highlight.js": "^11.9.0",
"notistack": "3.0.1",
"react": "18.2.0",
"react-dom": "18.2.0",
Expand Down
2 changes: 1 addition & 1 deletion web/dashboard-ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { ProgressProvider } from "./components/ProgressProvider";
import { AuthenticatorManageDialogContext } from "./views/organisms/AuthenticatorManageDialog";
import { EventDetailDialogContext } from "./views/organisms/EventDetailDialog";
import { PasswordChangeDialogContext } from "./views/organisms/PasswordChangeDialog";
import { UserInfoDialogContext } from "./views/organisms/UserActionDialog";
import { UserAddonChangeDialogContext } from "./views/organisms/UserAddonsChangeDialog";
import { UserInfoDialogContext } from "./views/organisms/UserInfoDialog";
import { UserContext } from "./views/organisms/UserModule";
import { UserNameChangeDialogContext } from "./views/organisms/UserNameChangeDialog";
import { EventPage } from "./views/pages/EventPage";
Expand Down
87 changes: 87 additions & 0 deletions web/dashboard-ui/src/views/atoms/YAMLTextArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { ContentCopy } from "@mui/icons-material";
import { Fab } from "@mui/material";
import { styled } from "@mui/material/styles";
import copy from "copy-to-clipboard";
import hljs from "highlight.js";
import "highlight.js/styles/default.css";
import { useSnackbar } from "notistack";
import React, { useState } from "react";

const StyledPre = styled("pre")({
fontFamily: "Menlo, Monaco, 'Courier New', monospace",
fontSize: 12,
lineHeight: 1.6,
margin: 0,
padding: 16,
whiteSpace: "pre",
wordWrap: "break-word",
overflow: "auto",
border: "1px solid #ccc",
borderRadius: "4px",
backgroundColor: "#1E1E1E",
"& .hljs-attr": {
color: "#9CDCFE",
},
"& .hljs-string": {
color: "#CE9178",
},
"& .hljs-number": {
color: "#B5CEA8",
},
"& .hljs-literal": {
color: "#569CD6",
},
});

const YAMLTextArea: React.FC<{
code: string;
}> = ({ code }) => {
const [hover, setHover] = useState(false);
const { enqueueSnackbar } = useSnackbar();

const onCopy = (text: string) => {
copy(text);
enqueueSnackbar("Copied!", { variant: "success" });
};

const highlightedCode = hljs.highlight(code, {
language: "yaml",
}).value;

const highlightedCodeWithSpaces = highlightedCode.replace(
/(^|\n)( +)/g,
function (_, newline, spaces) {
return newline + "&nbsp;".repeat(spaces.length);
}
);

return (
<div
onMouseOver={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
<StyledPre
dangerouslySetInnerHTML={{ __html: highlightedCodeWithSpaces }}
/>
{hover && (
<Fab
color="secondary"
aria-label="copy"
onClick={() => {
onCopy(code);
}}
size="medium"
sx={{
position: "absolute",
bottom: 80,
right: 64,
}}
>
<ContentCopy fontSize="small" />
</Fab>
)}
</div>
);
};

export default YAMLTextArea;
34 changes: 0 additions & 34 deletions web/dashboard-ui/src/views/organisms/UserActionDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -230,36 +230,6 @@ const UserActionDialog: React.FC<UserActionDialogProps> = ({
);
};

/**
* Info
*/
export const UserInfoDialog: React.VFC<{
onClose: () => void;
user: User;
defaultOpenUserAddon?: boolean;
}> = ({ onClose, user, defaultOpenUserAddon }) => {
console.log("UserInfoDialog");
return (
<UserActionDialog
title="User Info"
onClose={() => onClose()}
user={user}
defaultOpenUserAddon={defaultOpenUserAddon}
actions={
<Button
variant="contained"
color="primary"
onClick={() => {
onClose();
}}
>
Close
</Button>
}
/>
);
};

/**
* Delete
*/
Expand Down Expand Up @@ -704,10 +674,6 @@ export const UserCreateDialog: React.VFC<{ onClose: () => void }> = ({
/**
* Context
*/
export const UserInfoDialogContext = DialogContext<{
user: User;
defaultOpenUserAddon?: boolean;
}>((props) => <UserInfoDialog {...props} />);
export const UserDeleteDialogContext = DialogContext<{ user: User }>(
(props) => <UserDeleteDialog {...props} />
);
Expand Down
88 changes: 88 additions & 0 deletions web/dashboard-ui/src/views/organisms/UserInfoDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { Close } from "@mui/icons-material";
import {
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
IconButton,
Stack,
Tab,
Tabs,
} from "@mui/material";
import { styled } from "@mui/material/styles";
import "highlight.js/styles/default.css";
import React, { useEffect, useState } from "react";
import { DialogContext } from "../../components/ContextProvider";
import { User } from "../../proto/gen/dashboard/v1alpha1/user_pb";
import { useUserService } from "../../services/DashboardServices";
import YAMLTextArea from "../atoms/YAMLTextArea";

const StyledDialogContent = styled(DialogContent)({
overflow: "auto",
});

const UserInfoDialog: React.FC<{
onClose: () => void;
user: User;
}> = ({ onClose, user }) => {
const userService = useUserService();

const [code, setCode] = useState("");
const [showTab, setShowTab] = useState<"objects">("objects");

useEffect(() => {
userService
.getUser({
userName: user.name,
withRaw: true,
})
.then((res) => {
setCode(res.user?.raw || "no yaml");
});
}, [user]);

return (
<Dialog open={true} scroll="paper" fullWidth maxWidth="md">
<DialogTitle>
<Stack direction="row" alignItems="center" spacing={1}>
User Object
<Box sx={{ flexGrow: 1 }} />
<IconButton
sx={{
color: (theme) => theme.palette.grey[500],
}}
onClick={() => onClose()}
>
<Close />
</IconButton>
</Stack>
</DialogTitle>

<StyledDialogContent>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<Tabs
value={showTab}
onChange={(_: React.SyntheticEvent, newValue: "objects") => {
setShowTab(newValue);
}}
aria-label="basic tabs example"
>
<Tab label="Live Manifest" value="objects" />
</Tabs>
</Box>
{showTab === "objects" && <YAMLTextArea code={code}></YAMLTextArea>}
</StyledDialogContent>
<DialogActions>
<Button onClick={() => onClose()} variant="contained" color="primary">
Close
</Button>
</DialogActions>
</Dialog>
);
};

export const UserInfoDialogContext = DialogContext<{
user: User;
}>((props) => <UserInfoDialog {...props} />);
2 changes: 1 addition & 1 deletion web/dashboard-ui/src/views/organisms/UserModule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const useUser = () => {
const [existingRoles, setExistingRoles] = useState<string[]>([]);

/**
* WorkspaceList: workspace list
* UserList: user list
*/
const getUsers = async () => {
console.log("getUsers");
Expand Down
89 changes: 89 additions & 0 deletions web/dashboard-ui/src/views/organisms/WorkspaceInfoDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Close } from "@mui/icons-material";
import {
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
IconButton,
Stack,
Tab,
Tabs,
} from "@mui/material";
import { styled } from "@mui/material/styles";
import "highlight.js/styles/default.css";
import React, { useEffect, useState } from "react";
import { DialogContext } from "../../components/ContextProvider";
import { Workspace } from "../../proto/gen/dashboard/v1alpha1/workspace_pb";
import { useWorkspaceService } from "../../services/DashboardServices";
import YAMLTextArea from "../atoms/YAMLTextArea";

const StyledDialogContent = styled(DialogContent)({
overflow: "auto",
});

const WorkspaceInfoDialog: React.FC<{
onClose: () => void;
ws: Workspace;
}> = ({ onClose, ws }) => {
const wsService = useWorkspaceService();

const [code, setCode] = useState("");
const [showTab, setShowTab] = useState<"objects">("objects");

useEffect(() => {
wsService
.getWorkspace({
wsName: ws.name,
userName: ws.ownerName,
withRaw: true,
})
.then((res) => {
setCode(res.workspace?.raw || "no yaml");
});
}, [ws]);

return (
<Dialog open={true} scroll="paper" fullWidth maxWidth="md">
<DialogTitle>
<Stack direction="row" alignItems="center" spacing={1}>
Workspace Object
<Box sx={{ flexGrow: 1 }} />
<IconButton
sx={{
color: (theme) => theme.palette.grey[500],
}}
onClick={() => onClose()}
>
<Close />
</IconButton>
</Stack>
</DialogTitle>

<StyledDialogContent>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<Tabs
value={showTab}
onChange={(_: React.SyntheticEvent, newValue: "objects") => {
setShowTab(newValue);
}}
aria-label="basic tabs example"
>
<Tab label="Live Manifest" value="objects" />
</Tabs>
</Box>
{showTab === "objects" && <YAMLTextArea code={code}></YAMLTextArea>}
</StyledDialogContent>
<DialogActions>
<Button onClick={() => onClose()} variant="contained" color="primary">
Close
</Button>
</DialogActions>
</Dialog>
);
};

export const WorkspaceInfoDialogContext = DialogContext<{
ws: Workspace;
}>((props) => <WorkspaceInfoDialog {...props} />);
Loading
Loading