Skip to content

Commit

Permalink
Merge pull request #845 from jlandowner/ui-support-yaml2
Browse files Browse the repository at this point in the history
Show User and Workspace live manifests on UI
  • Loading branch information
oruharo authored Jun 27, 2024
2 parents 0023dd5 + f6e4c10 commit 9616c63
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 52 deletions.
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

0 comments on commit 9616c63

Please sign in to comment.