users page added and functional with basic mui table components
This commit is contained in:
parent
d050fdee55
commit
dd522732db
7 changed files with 547 additions and 183 deletions
128
package-lock.json
generated
128
package-lock.json
generated
|
|
@ -14,8 +14,6 @@
|
|||
"@mui/icons-material": "^6.1.6",
|
||||
"@mui/material": "^6.1.6",
|
||||
"@mui/styles": "^6.1.6",
|
||||
"@mui/x-data-grid": "^7.22.2",
|
||||
"@mui/x-date-pickers": "^7.22.2",
|
||||
"@sentry/react": "^8.38.0",
|
||||
"@types/classnames": "^2.3.0",
|
||||
"axios": "^1.7.7",
|
||||
|
|
@ -1512,126 +1510,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/x-data-grid": {
|
||||
"version": "7.22.2",
|
||||
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.22.2.tgz",
|
||||
"integrity": "sha512-yfy2s5A6tbajQZiEdsba49T4FYb9F0WPrzbbG30dl1+sIiX4ZRX7ma44UIDGPZrsZv8xkkE+p8qeJxZ7OaMteA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.7",
|
||||
"@mui/utils": "^5.16.6 || ^6.0.0",
|
||||
"@mui/x-internals": "7.21.0",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"reselect": "^5.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.9.0",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
"@mui/material": "^5.15.14 || ^6.0.0",
|
||||
"@mui/system": "^5.15.14 || ^6.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/x-date-pickers": {
|
||||
"version": "7.22.2",
|
||||
"integrity": "sha512-1KHSlIlnSoY3oHm820By8X344pIdGYqPvCCvfVHrEeeIQ/pHdxDD8tjZFWkFl4Jgm9oVFK90fMcqNZAzc+WaCw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.7",
|
||||
"@mui/utils": "^5.16.6 || ^6.0.0",
|
||||
"@mui/x-internals": "7.21.0",
|
||||
"@types/react-transition-group": "^4.4.11",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-transition-group": "^4.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.9.0",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
"@mui/material": "^5.15.14 || ^6.0.0",
|
||||
"@mui/system": "^5.15.14 || ^6.0.0",
|
||||
"date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0",
|
||||
"date-fns-jalali": "^2.13.0-0 || ^3.2.0-0",
|
||||
"dayjs": "^1.10.7",
|
||||
"luxon": "^3.0.2",
|
||||
"moment": "^2.29.4",
|
||||
"moment-hijri": "^2.1.2 || ^3.0.0",
|
||||
"moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
|
||||
"react": "^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
},
|
||||
"date-fns": {
|
||||
"optional": true
|
||||
},
|
||||
"date-fns-jalali": {
|
||||
"optional": true
|
||||
},
|
||||
"dayjs": {
|
||||
"optional": true
|
||||
},
|
||||
"luxon": {
|
||||
"optional": true
|
||||
},
|
||||
"moment": {
|
||||
"optional": true
|
||||
},
|
||||
"moment-hijri": {
|
||||
"optional": true
|
||||
},
|
||||
"moment-jalaali": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/x-internals": {
|
||||
"version": "7.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.21.0.tgz",
|
||||
"integrity": "sha512-94YNyZ0BhK5Z+Tkr90RKf47IVCW8R/1MvdUhh6MCQg6sZa74jsX+x+gEZ4kzuCqOsuyTyxikeQ8vVuCIQiP7UQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.7",
|
||||
"@mui/utils": "^5.16.6 || ^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
|
|
@ -4368,12 +4246,6 @@
|
|||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||
},
|
||||
"node_modules/reselect": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
|
||||
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.8",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@
|
|||
"@mui/icons-material": "^6.1.6",
|
||||
"@mui/material": "^6.1.6",
|
||||
"@mui/styles": "^6.1.6",
|
||||
"@mui/x-data-grid": "^7.22.2",
|
||||
"@mui/x-date-pickers": "^7.22.2",
|
||||
"@sentry/react": "^8.38.0",
|
||||
"@types/classnames": "^2.3.0",
|
||||
"axios": "^1.7.7",
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ export default function SearchSelect(props: Props) {
|
|||
multiple,
|
||||
variant,
|
||||
style,
|
||||
getOptionSelected,
|
||||
// getOptionSelected,
|
||||
onChange,
|
||||
onKeyDown
|
||||
} = props;
|
||||
|
|
@ -65,6 +65,17 @@ export default function SearchSelect(props: Props) {
|
|||
return { value: s, label: s } as Option;
|
||||
};
|
||||
|
||||
const anyToLabel = (o: any): string => {
|
||||
if (o.text && o.value) {
|
||||
return o.text + " - " + o.value
|
||||
} else if (o.value) {
|
||||
return o.value
|
||||
} else if (o.label) {
|
||||
return o.label
|
||||
}
|
||||
return o.toString();
|
||||
};
|
||||
|
||||
return (
|
||||
<Autocomplete
|
||||
id={label + "-autocomplete"}
|
||||
|
|
@ -123,10 +134,15 @@ export default function SearchSelect(props: Props) {
|
|||
return filtered;
|
||||
}}
|
||||
getOptionLabel={option => {
|
||||
// return anyToLabel(option)
|
||||
// console.log(option)
|
||||
// console.log("Is Option: ",option instanceof Option)
|
||||
if (option instanceof Option) {
|
||||
return option.text + " - " + option.value
|
||||
// } else if (option.text && option.value) {
|
||||
|
||||
} else {
|
||||
return option as string
|
||||
return anyToLabel(option)
|
||||
}
|
||||
}}
|
||||
// getOptionSelected={
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export default function Router(props: Props) {
|
|||
{/* Page routes */}
|
||||
<Route index element={hello()} />
|
||||
<Route path="/teams" element={<Teams/>} />
|
||||
<Route path="/users" element={<Users/>} />
|
||||
<Route path="/users" element={<Users title="Users"/>} />
|
||||
{/* <Route
|
||||
path="/teams/:teamID"
|
||||
element={<Team />}
|
||||
|
|
|
|||
468
src/pages/Users.tsx
Normal file
468
src/pages/Users.tsx
Normal file
|
|
@ -0,0 +1,468 @@
|
|||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
Collapse,
|
||||
darken,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Divider,
|
||||
IconButton,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
ListSubheader,
|
||||
PaletteColor,
|
||||
Paper,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TablePagination,
|
||||
TableRow,
|
||||
Theme,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||
// import { getTableIcons } from "common/ResponsiveTable";
|
||||
import SearchSelect, { Option } from "common/SearchSelect";
|
||||
// import SmartBreadcrumb from "common/SmartBreadcrumb";
|
||||
import { useMobile, useSnackbar, useUserAPI } from "hooks";
|
||||
// import MaterialTable, { Column } from "material-table";
|
||||
import { User } from "models";
|
||||
import PageContainer from "pages/PageContainer";
|
||||
// import { ListUsersResponse } from "providers/pond/userAPI";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { getSecondaryColour, getSignatureAccentColour } from "services/whiteLabel";
|
||||
import { Add, ChevronRight, Face, Remove } from "@mui/icons-material";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => {
|
||||
// const isMobile = useMobile()
|
||||
return ({
|
||||
gutter: {
|
||||
backgroundColor: darken(theme.palette.background.paper, 0.03),
|
||||
border: "none"
|
||||
},
|
||||
dataTable: {
|
||||
backgroundColor: theme.palette.background.paper
|
||||
},
|
||||
tableContainer: {
|
||||
margin: theme.spacing(1),
|
||||
[theme.breakpoints.up("sm")]: {
|
||||
margin: theme.spacing(2)
|
||||
},
|
||||
width: "auto"
|
||||
},
|
||||
title: {
|
||||
padding: theme.spacing(1)
|
||||
},
|
||||
titleContainer: {
|
||||
border: "none"
|
||||
},
|
||||
chipContainer: {
|
||||
maxWidth: "100%",
|
||||
maxHeight: "100%",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start"
|
||||
},
|
||||
deleteIcon: {
|
||||
color: "var(--status-alert)"
|
||||
},
|
||||
userAvatar: {
|
||||
color: "#fff",
|
||||
backgroundColor: getSecondaryColour()["700" as keyof PaletteColor],
|
||||
[theme.breakpoints.down("xs")]: {
|
||||
width: "32px",
|
||||
height: "32px"
|
||||
}
|
||||
},
|
||||
listItemButton: {
|
||||
'&:hover': {
|
||||
color: getSignatureAccentColour()
|
||||
}
|
||||
},
|
||||
avatarContainer: {
|
||||
textAlign: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
addIcon: {
|
||||
color: "var(--status-ok)"
|
||||
}})},
|
||||
);
|
||||
|
||||
interface Props {
|
||||
title: string | JSX.Element;
|
||||
}
|
||||
|
||||
export default function Users(props: Props) {
|
||||
const { title } = props;
|
||||
const classes = useStyles();
|
||||
const userAPI = useUserAPI();
|
||||
// let tableRef: any = React.createRef();
|
||||
const { error, success } = useSnackbar();
|
||||
const [pageSize, setPageSize] = useState(5);
|
||||
const [page, setPage] = useState(0);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [showAllowAction, setShowAllowAction] = useState(false);
|
||||
const [selectedAction, setSelectedAction] = useState<Option | undefined>();
|
||||
const [showEnableFeature, setShowEnableFeature] = useState(false);
|
||||
const [selectedFeature, setSelectedFeature] = useState<Option | undefined>();
|
||||
const [selectedUser, setSelectedUser] = useState(User.create());
|
||||
const TitleMap = new Map([
|
||||
["Uploaded", "uploaded"],
|
||||
["Version", "version"],
|
||||
["Platform", "platform"],
|
||||
["Channel", "channel"],
|
||||
["Size", "size"]
|
||||
]);
|
||||
const actions = [
|
||||
"provision",
|
||||
"upload-firmware",
|
||||
"remove-devices",
|
||||
"copy-token",
|
||||
"recluse",
|
||||
"pause-data"
|
||||
].sort();
|
||||
const features = [
|
||||
"admin",
|
||||
"beta",
|
||||
"billing",
|
||||
"security",
|
||||
"dev-channel",
|
||||
"docs",
|
||||
"sleep",
|
||||
"support",
|
||||
"tasks",
|
||||
"teams",
|
||||
"maps",
|
||||
"json",
|
||||
"john-deere",
|
||||
"grain-composition",
|
||||
"developer",
|
||||
"marketplace",
|
||||
"installer"
|
||||
].sort();
|
||||
|
||||
const allow = () => {
|
||||
const action = selectedAction ? selectedAction.label : "";
|
||||
if (selectedUser.id() === "" || action === "") return;
|
||||
if (!selectedUser.settings.actions.includes(action)) {
|
||||
selectedUser.settings.actions.push(action);
|
||||
}
|
||||
userAPI
|
||||
.updateUser(selectedUser.id(), selectedUser.protobuf())
|
||||
.then(() => {
|
||||
success("Allowed " + selectedUser.name() + " to " + action);
|
||||
setShowAllowAction(false);
|
||||
setSelectedAction({} as Option);
|
||||
})
|
||||
.catch(() => error("Failed to allow " + selectedUser.name() + " to " + action));
|
||||
};
|
||||
|
||||
const enable = () => {
|
||||
const feature = selectedFeature ? selectedFeature.label : "";
|
||||
if (selectedUser.id() === "" || feature === "") return;
|
||||
if (!selectedUser.settings.features.includes(feature)) {
|
||||
selectedUser.settings.features.push(feature);
|
||||
}
|
||||
userAPI
|
||||
.updateUser(selectedUser.id(), selectedUser.protobuf())
|
||||
.then(() => {
|
||||
success("Enabled " + feature + " for " + selectedUser.name());
|
||||
setShowEnableFeature(false);
|
||||
setSelectedFeature({} as Option);
|
||||
})
|
||||
.catch(() => error("Failed to enable " + feature + " for " + selectedUser.name()));
|
||||
};
|
||||
|
||||
const remove = (user: User, type: "action" | "feature", key: string) => {
|
||||
if (user.id() === "" || key === "") return;
|
||||
if (type === "action") {
|
||||
user.settings.actions = user.settings.actions.filter(v => v !== key);
|
||||
}
|
||||
if (type === "feature") {
|
||||
user.settings.features = user.settings.features.filter(v => v !== key);
|
||||
}
|
||||
userAPI
|
||||
.updateUser(user.id(), user.protobuf())
|
||||
.then(() => {
|
||||
if (type === "action") {
|
||||
success("Disallowed " + user.name() + " from " + key);
|
||||
setSelectedAction({} as Option);
|
||||
}
|
||||
if (type === "feature") {
|
||||
success("Disabled " + key + " for " + user.name());
|
||||
setSelectedFeature({} as Option);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (type === "action") {
|
||||
error("Failed to disallow " + user.name() + " from " + key);
|
||||
}
|
||||
if (type === "feature") {
|
||||
error("Failed to disable " + key + " for " + user.name());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const details = (user: User) => {
|
||||
const item = (text: string, type: "action" | "feature") => (
|
||||
<ListItem key={text}>
|
||||
<ListItemIcon>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setSelectedUser(user);
|
||||
remove(user, type, text);
|
||||
}}>
|
||||
<Remove className={classes.deleteIcon} />
|
||||
</IconButton>
|
||||
</ListItemIcon>
|
||||
<ListItemText>{text}</ListItemText>
|
||||
</ListItem>
|
||||
);
|
||||
const actions = user.settings.actions;
|
||||
const features = user.settings.features;
|
||||
// const showActions = view !== "features";
|
||||
// const showFeatures = view !== "actions";
|
||||
return (
|
||||
<List disablePadding dense>
|
||||
<ListSubheader className={classes.gutter}>Actions</ListSubheader>
|
||||
{actions.map(action => item(action, "action"))}
|
||||
<ListItemButton
|
||||
onClick={() => {
|
||||
setSelectedUser(user);
|
||||
setShowAllowAction(true);
|
||||
}}>
|
||||
<ListItemIcon>
|
||||
<IconButton>
|
||||
<Add className={classes.addIcon} />
|
||||
</IconButton>
|
||||
</ListItemIcon>
|
||||
<ListItemText>Allow an action</ListItemText>
|
||||
</ListItemButton>
|
||||
<Divider />
|
||||
<ListSubheader className={classes.gutter}>Features</ListSubheader>
|
||||
{features.map(feature => item(feature, "feature"))}
|
||||
<ListItemButton
|
||||
onClick={() => {
|
||||
setSelectedUser(user);
|
||||
setShowEnableFeature(true);
|
||||
}}>
|
||||
<ListItemIcon>
|
||||
<IconButton>
|
||||
<Add className={classes.addIcon} />
|
||||
</IconButton>
|
||||
</ListItemIcon>
|
||||
<ListItemText>Enable a feature</ListItemText>
|
||||
</ListItemButton>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
const renderUser = (user: User) => {
|
||||
const avatarURL = user.settings.avatar;
|
||||
const avatar = avatarURL ? (
|
||||
<Avatar alt={user.name()} src={avatarURL} />
|
||||
) : (
|
||||
<Avatar alt={user.name()}>
|
||||
<Face />
|
||||
</Avatar>
|
||||
);
|
||||
return (
|
||||
<ListItem style={{ padding: 0 }}>
|
||||
<ListItemAvatar>{avatar}</ListItemAvatar>
|
||||
<ListItemText primary={user.name()} secondary={user.settings.email} />
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
|
||||
// const renderGutter = (user: UserData) => {
|
||||
// return (
|
||||
|
||||
// )
|
||||
// }
|
||||
|
||||
// interface UserData {
|
||||
// settings: pond.UserSettings;
|
||||
// }
|
||||
const [rows, setRows] = useState<User[]>([])
|
||||
|
||||
const loadUsers = () => {
|
||||
userAPI.listUsers(pageSize, page*pageSize, "desc", "name").then(resp => {
|
||||
let r: User[] = []
|
||||
resp.users.forEach((user, _index) => {
|
||||
r.push(user)
|
||||
})
|
||||
setRows(r)
|
||||
setTotal(resp.total)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadUsers();
|
||||
}, [page, pageSize]); // Trigger fetching data when page or pageSize changes
|
||||
|
||||
const handleRowsPerPageChange = (event: any) => {
|
||||
const newRowsPerPage = parseInt(event.target.value, 10); // Get selected value
|
||||
setPageSize(newRowsPerPage);
|
||||
setPage(0); // Reset to the first page
|
||||
};
|
||||
|
||||
const handleCollapse = (user: User) => {
|
||||
if (selectedUser.id() === user.id()) {
|
||||
setSelectedUser(User.create())
|
||||
} else {
|
||||
setSelectedUser(user)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// console.log("Open Row: ", openRow);
|
||||
console.log("Selected User: ", selectedUser);
|
||||
console.log("Selected ID: ", selectedUser.id())
|
||||
}, [selectedUser])
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<TableContainer className={classes.tableContainer} component={Paper}>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} className={classes.titleContainer}>
|
||||
{typeof(title) === "string" ?
|
||||
<Typography variant="h5" className={classes.title}>
|
||||
{title}
|
||||
</Typography>
|
||||
:
|
||||
<Box className={classes.title}>
|
||||
title
|
||||
</Box>
|
||||
}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell></TableCell>
|
||||
<TableCell>Profile</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rows.map((row, index) => (
|
||||
<React.Fragment key={"user-row-"+index}>
|
||||
<TableRow>
|
||||
<TableCell width={1}>
|
||||
<IconButton sx={{
|
||||
transform: selectedUser.settings.id === row.settings.id ?
|
||||
'rotate(90deg)' : 'rotate(0deg)',
|
||||
transition: 'transform 0.3s ease',
|
||||
}}
|
||||
onClick={() => handleCollapse(row)}>
|
||||
<ChevronRight/>
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{renderUser(row)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow className={classes.gutter}>
|
||||
<TableCell colSpan={6} style={{padding:0, margin: 0, border: "none"}}>
|
||||
<Collapse in={selectedUser.settings.id === row.settings.id} timeout="auto" unmountOnExit>
|
||||
{details(row)}
|
||||
</Collapse>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 20]}
|
||||
component="div"
|
||||
count={total}
|
||||
rowsPerPage={pageSize}
|
||||
page={page}
|
||||
onPageChange={(_event, page) => setPage(page)}
|
||||
onRowsPerPageChange={handleRowsPerPageChange}
|
||||
/>
|
||||
</TableContainer>
|
||||
<ResponsiveDialog
|
||||
maxWidth="sm"
|
||||
fullWidth
|
||||
open={showAllowAction}
|
||||
onClose={() => setShowAllowAction(false)}
|
||||
aria-labelledby="allow-action">
|
||||
<DialogTitle id="allow-action-title">Allow an action</DialogTitle>
|
||||
<DialogContent>
|
||||
<SearchSelect
|
||||
label="Select an action to allow"
|
||||
selected={selectedAction}
|
||||
// options={actions}
|
||||
options={actions
|
||||
.filter(a => !selectedUser.settings.actions.includes(a))
|
||||
.map(a => {
|
||||
return { value: a, label: a };
|
||||
})}
|
||||
changeSelection={(option: Option | null) =>
|
||||
setSelectedAction(option ? option : undefined)
|
||||
}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button color="primary" onClick={() => setShowAllowAction(false)}>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
disabled={
|
||||
!selectedAction || !actions.includes(selectedAction ? selectedAction.label : "")
|
||||
}
|
||||
onClick={allow}>
|
||||
Allow
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</ResponsiveDialog>
|
||||
<ResponsiveDialog
|
||||
maxWidth="sm"
|
||||
fullWidth
|
||||
open={showEnableFeature}
|
||||
onClose={() => setShowEnableFeature(false)}
|
||||
aria-labelledby="enable-feature">
|
||||
<DialogTitle id="enable-feature-title">Enable a feature</DialogTitle>
|
||||
<DialogContent>
|
||||
<SearchSelect
|
||||
label="Select a feature to enable"
|
||||
selected={selectedFeature}
|
||||
// options={features}
|
||||
options={features
|
||||
.filter(a => !selectedUser.settings.features.includes(a))
|
||||
.map(a => {
|
||||
return { value: a, label: a };
|
||||
})}
|
||||
changeSelection={(option: Option | null) =>
|
||||
setSelectedFeature(option ? option : undefined)
|
||||
}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button color="primary" onClick={() => setShowEnableFeature(false)}>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
disabled={
|
||||
!selectedFeature || !features.includes(selectedFeature ? selectedFeature.label : "")
|
||||
}
|
||||
onClick={enable}>
|
||||
Enable
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</ResponsiveDialog>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
|
|
@ -11,20 +11,21 @@ import { AxiosResponse } from "axios";
|
|||
import { pond } from "protobuf-ts/pond";
|
||||
import { User } from "../../models/user";
|
||||
import { Scope } from "models";
|
||||
import { or } from "utils/types";
|
||||
|
||||
// export interface ListUsersResponse {
|
||||
// users: User[];
|
||||
// nextOffset: number;
|
||||
// total: number;
|
||||
// }
|
||||
export interface ListUsersResponse {
|
||||
users: User[];
|
||||
nextOffset: number;
|
||||
total: number;
|
||||
}
|
||||
export interface IUserAPIContext {
|
||||
// listUsers: (
|
||||
// limit: number,
|
||||
// offset: number,
|
||||
// order?: "asc" | "desc",
|
||||
// by?: string,
|
||||
// search?: string
|
||||
// ) => Promise<ListUsersResponse>;
|
||||
listUsers: (
|
||||
limit: number,
|
||||
offset: number,
|
||||
order?: "asc" | "desc",
|
||||
by?: string,
|
||||
search?: string
|
||||
) => Promise<ListUsersResponse>;
|
||||
// listProfiles: () => Promise<pond.UserProfile[]>;
|
||||
listObjectUsers: (scope: Scope) => Promise<any>;
|
||||
updateObjectUsers: (scope: Scope, users: pond.IUser[]) => Promise<any>;
|
||||
|
|
@ -56,39 +57,39 @@ export default function UserProvider(props: PropsWithChildren<any>) {
|
|||
const { get, put } = useHTTP();
|
||||
const [{ as }] = useGlobalState();
|
||||
|
||||
// const listUsers = (
|
||||
// limit: number,
|
||||
// offset: number,
|
||||
// order?: "asc" | "desc",
|
||||
// by?: string,
|
||||
// search?: string
|
||||
// ): Promise<ListUsersResponse> => {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// get(
|
||||
// pondURL(
|
||||
// "/users?limit=" +
|
||||
// limit +
|
||||
// "&offset=" +
|
||||
// offset +
|
||||
// ("&order=" + (order ? order : "asc")) +
|
||||
// ("&by=" + (by ? by : "name")) +
|
||||
// (search ? "&search=" + search : "")
|
||||
// )
|
||||
// )
|
||||
// .then((res: any) => {
|
||||
// let users: User[] = [];
|
||||
// if (res && res.data && res.data.users) {
|
||||
// res.data.users.forEach((raw: any) => users.push(User.any(raw)));
|
||||
// }
|
||||
// resolve({
|
||||
// users: users,
|
||||
// nextOffset: or(res.data.nextOffset, 0),
|
||||
// total: or(res.data.total, 0)
|
||||
// });
|
||||
// })
|
||||
// .catch((err: any) => reject(err));
|
||||
// });
|
||||
// };
|
||||
const listUsers = (
|
||||
limit: number,
|
||||
offset: number,
|
||||
order?: "asc" | "desc",
|
||||
by?: string,
|
||||
search?: string
|
||||
): Promise<ListUsersResponse> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
get(
|
||||
pondURL(
|
||||
"/users?limit=" +
|
||||
limit +
|
||||
"&offset=" +
|
||||
offset +
|
||||
("&order=" + (order ? order : "asc")) +
|
||||
("&by=" + (by ? by : "name")) +
|
||||
(search ? "&search=" + search : "")
|
||||
)
|
||||
)
|
||||
.then((res: any) => {
|
||||
let users: User[] = [];
|
||||
if (res && res.data && res.data.users) {
|
||||
res.data.users.forEach((raw: any) => users.push(User.any(raw)));
|
||||
}
|
||||
resolve({
|
||||
users: users,
|
||||
nextOffset: or(res.data.nextOffset, 0),
|
||||
total: or(res.data.total, 0)
|
||||
});
|
||||
})
|
||||
.catch((err: any) => reject(err));
|
||||
});
|
||||
};
|
||||
|
||||
// const listProfiles = (): Promise<pond.UserProfile[]> => {
|
||||
// return new Promise((resolve, reject) => {
|
||||
|
|
@ -230,7 +231,7 @@ export default function UserProvider(props: PropsWithChildren<any>) {
|
|||
return (
|
||||
<UserAPIContext.Provider
|
||||
value={{
|
||||
// listUsers,
|
||||
listUsers,
|
||||
// listProfiles,
|
||||
listObjectUsers,
|
||||
updateObjectUsers,
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ function generateBackgroundShades(themeType: "light" | "dark") {
|
|||
})
|
||||
|
||||
return {
|
||||
default: "#303030", // Lightens the base color for background.default
|
||||
paper: "#424242", // Slightly less light for background.paper
|
||||
default: "#303030",
|
||||
paper: "#373737",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -185,7 +185,8 @@ function options(themeType: "light" | "dark"): ThemeOptions {
|
|||
// color: "white"
|
||||
},
|
||||
'&:hover': {
|
||||
backgroundColor: "rgba(125, 125, 125, 0.2)"
|
||||
backgroundColor: "rgba(125, 125, 125, 0.2)",
|
||||
color: accent,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -198,8 +199,9 @@ function options(themeType: "light" | "dark"): ThemeOptions {
|
|||
// color: "white"
|
||||
},
|
||||
'&:hover': {
|
||||
color: themeType === "light" ? signature : "black",
|
||||
// color: themeType === "light" ? signature : "black",
|
||||
// color: "white"
|
||||
color: accent
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -220,6 +222,13 @@ function options(themeType: "light" | "dark"): ThemeOptions {
|
|||
},
|
||||
},
|
||||
},
|
||||
// MuiDataGrid: {
|
||||
// styleOverrides: {
|
||||
// root: {
|
||||
// backgroundColor: "black"
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
}
|
||||
} as ThemeOptions;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue