import * as React from "react";
import { useContext, useEffect, useState } from "react";
import {
Alert,
CardActions,
CardContent,
Chip,
FormControl,
Select,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Tooltip,
Typography,
Container,
Card,
Button,
Dialog,
DialogTitle,
DialogContent,
TextField,
IconButton,
MenuItem,
DialogContentText,
useMediaQuery,
useTheme,
Stack,
CircularProgress,
Box,
} from "@mui/material";
import EditIcon from "@mui/icons-material/Edit";
import { useTranslation } from "react-i18next";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
import AddIcon from "@mui/icons-material/Add";
import CloseIcon from "@mui/icons-material/Close";
import LockIcon from "@mui/icons-material/Lock";
import routes from "./routes";
import { AccountContext } from "./App";
import DialogFooter from "./DialogFooter";
import { Paragraph } from "./styles";
import { UnauthorizedError } from "../app/errors";
import session from "../app/Session";
import adminApi from "../app/AdminApi";
import { Role } from "../app/AccountApi";
const Admin = () => {
const { account } = useContext(AccountContext);
// Redirect non-admins away
if (!session.exists() || (account && account.role !== Role.ADMIN)) {
window.location.href = routes.app;
return null;
}
// Wait for account to load
if (!account) {
return (
);
}
return (
);
};
const Users = () => {
const { t } = useTranslation();
const [users, setUsers] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const [addDialogKey, setAddDialogKey] = useState(0);
const [addDialogOpen, setAddDialogOpen] = useState(false);
const loadUsers = async () => {
try {
setLoading(true);
const data = await adminApi.getUsers();
setUsers(data);
setError("");
} catch (e) {
console.log(`[Admin] Error loading users`, e);
if (e instanceof UnauthorizedError) {
await session.resetAndRedirect(routes.login);
} else {
setError(e.message);
}
} finally {
setLoading(false);
}
};
useEffect(() => {
loadUsers();
}, []);
const handleAddClick = () => {
setAddDialogKey((prev) => prev + 1);
setAddDialogOpen(true);
};
const handleDialogClose = () => {
setAddDialogOpen(false);
loadUsers();
};
return (
{t("admin_users_title")}
{t("admin_users_description")}
{error && (
{error}
)}
{loading && (
)}
{!loading && users && (
)}
}>
{t("admin_users_add_button")}
);
};
const UsersTable = (props) => {
const { t } = useTranslation();
const [editDialogKey, setEditDialogKey] = useState(0);
const [editDialogOpen, setEditDialogOpen] = useState(false);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [accessDialogKey, setAccessDialogKey] = useState(0);
const [accessDialogOpen, setAccessDialogOpen] = useState(false);
const [deleteAccessDialogOpen, setDeleteAccessDialogOpen] = useState(false);
const [selectedUser, setSelectedUser] = useState(null);
const [selectedGrant, setSelectedGrant] = useState(null);
const { users } = props;
const handleEditClick = (user) => {
setEditDialogKey((prev) => prev + 1);
setSelectedUser(user);
setEditDialogOpen(true);
};
const handleDeleteClick = (user) => {
setSelectedUser(user);
setDeleteDialogOpen(true);
};
const handleAddAccessClick = (user) => {
setAccessDialogKey((prev) => prev + 1);
setSelectedUser(user);
setAccessDialogOpen(true);
};
const handleDeleteAccessClick = (user, grant) => {
setSelectedUser(user);
setSelectedGrant(grant);
setDeleteAccessDialogOpen(true);
};
const handleDialogClose = () => {
setEditDialogOpen(false);
setDeleteDialogOpen(false);
setAccessDialogOpen(false);
setDeleteAccessDialogOpen(false);
setSelectedUser(null);
setSelectedGrant(null);
props.onUserChanged();
};
return (
<>
{t("admin_users_table_username_header")}
{t("admin_users_table_role_header")}
{t("admin_users_table_tier_header")}
{t("admin_users_table_grants_header")}
{t("admin_users_table_actions_header")}
{users.map((user) => (
{user.username}
{user.provisioned && (
)}
{user.tier || "-"}
{user.grants && user.grants.length > 0 ? (
{user.grants.map((grant, idx) => {
const canDelete = user.role !== "admin" && !grant.provisioned;
const tooltipText = grant.provisioned
? t("admin_users_table_grant_provisioned_tooltip", { permission: grant.permission })
: t("admin_users_table_grant_tooltip", { permission: grant.permission });
return (
: undefined}
onDelete={canDelete ? () => handleDeleteAccessClick(user, grant) : undefined}
/>
);
})}
) : (
"-"
)}
{user.role !== "admin" && !user.provisioned ? (
<>
handleAddAccessClick(user)} size="small">
handleEditClick(user)} size="small">
handleDeleteClick(user)} size="small">
>
) : user.role !== "admin" && user.provisioned ? (
<>
handleAddAccessClick(user)} size="small">
>
) : (
)}
))}
>
);
};
const RoleChip = ({ role }) => {
const { t } = useTranslation();
if (role === "admin") {
return ;
}
return ;
};
const AddUserDialog = (props) => {
const theme = useTheme();
const { t } = useTranslation();
const [error, setError] = useState("");
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [tier, setTier] = useState("");
const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));
const handleSubmit = async () => {
try {
await adminApi.addUser(username, password, tier || undefined);
props.onClose();
} catch (e) {
console.log(`[Admin] Error adding user`, e);
if (e instanceof UnauthorizedError) {
await session.resetAndRedirect(routes.login);
} else {
setError(e.message);
}
}
};
return (
);
};
const EditUserDialog = (props) => {
const theme = useTheme();
const { t } = useTranslation();
const [error, setError] = useState("");
const [password, setPassword] = useState("");
const [tier, setTier] = useState(props.user?.tier || "");
const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));
const handleSubmit = async () => {
try {
await adminApi.updateUser(props.user.username, password || undefined, tier || undefined);
props.onClose();
} catch (e) {
console.log(`[Admin] Error updating user`, e);
if (e instanceof UnauthorizedError) {
await session.resetAndRedirect(routes.login);
} else {
setError(e.message);
}
}
};
if (!props.user) {
return null;
}
return (
);
};
const DeleteUserDialog = (props) => {
const { t } = useTranslation();
const [error, setError] = useState("");
const handleSubmit = async () => {
try {
await adminApi.deleteUser(props.user.username);
props.onClose();
} catch (e) {
console.log(`[Admin] Error deleting user`, e);
if (e instanceof UnauthorizedError) {
await session.resetAndRedirect(routes.login);
} else {
setError(e.message);
}
}
};
if (!props.user) {
return null;
}
return (
);
};
const AddAccessDialog = (props) => {
const theme = useTheme();
const { t } = useTranslation();
const [error, setError] = useState("");
const [topic, setTopic] = useState("");
const [permission, setPermission] = useState("read-write");
const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));
const handleSubmit = async () => {
try {
await adminApi.allowAccess(props.user.username, topic, permission);
props.onClose();
} catch (e) {
console.log(`[Admin] Error adding access`, e);
if (e instanceof UnauthorizedError) {
await session.resetAndRedirect(routes.login);
} else {
setError(e.message);
}
}
};
if (!props.user) {
return null;
}
return (
);
};
const DeleteAccessDialog = (props) => {
const { t } = useTranslation();
const [error, setError] = useState("");
const handleSubmit = async () => {
try {
await adminApi.resetAccess(props.user.username, props.grant.topic);
props.onClose();
} catch (e) {
console.log(`[Admin] Error removing access`, e);
if (e instanceof UnauthorizedError) {
await session.resetAndRedirect(routes.login);
} else {
setError(e.message);
}
}
};
if (!props.user || !props.grant) {
return null;
}
return (
);
};
export default Admin;