rendering users page with the responsive container
This commit is contained in:
parent
39f5f6d452
commit
f2d3b32c97
4 changed files with 139 additions and 184 deletions
|
|
@ -67,8 +67,8 @@ export default function UserWrapper(props: Props) {
|
||||||
if (hasFetched.current) return;
|
if (hasFetched.current) return;
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
userAPI.getUserWithTeam(user_id).then(resp => {
|
userAPI.getUserWithTeam(user_id).then(resp => {
|
||||||
console.log(resp.data.team)
|
// console.log(resp.data.team)
|
||||||
console.log(resp.data.user)
|
// console.log(resp.data.user)
|
||||||
setGlobal({
|
setGlobal({
|
||||||
user: resp.data.user ? User.create(resp.data.user) : User.create(),
|
user: resp.data.user ? User.create(resp.data.user) : User.create(),
|
||||||
team: resp.data.team ? Team.create(resp.data.team) : Team.create(),
|
team: resp.data.team ? Team.create(resp.data.team) : Team.create(),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Box, CircularProgress, Collapse, darken, IconButton, InputAdornment, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, TextField, Theme, Typography } from "@mui/material";
|
import { Box, CircularProgress, Collapse, darken, IconButton, InputAdornment, Paper, SxProps, Table, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, TextField, Theme, Typography } from "@mui/material";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import { ChevronRight, Search } from "@mui/icons-material"
|
import { ChevronRight, Search } from "@mui/icons-material"
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
@ -28,15 +28,18 @@ const useStyles = makeStyles((theme: Theme) => {
|
||||||
border: "none",
|
border: "none",
|
||||||
textAlign: "right",
|
textAlign: "right",
|
||||||
},
|
},
|
||||||
addIcon: {
|
|
||||||
color: "var(--status-ok)"
|
|
||||||
}
|
|
||||||
})},
|
})},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export interface Column<T> {
|
||||||
|
title: string | JSX.Element;
|
||||||
|
cellStyle?: SxProps<Theme> | undefined
|
||||||
|
render?: (row: T) => JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
interface Props<T> {
|
interface Props<T> {
|
||||||
rows: T[],
|
rows: T[],
|
||||||
columns?: any,
|
columns?: Column<T>[],
|
||||||
title?: string;
|
title?: string;
|
||||||
total: number,
|
total: number,
|
||||||
pageSize: number,
|
pageSize: number,
|
||||||
|
|
@ -100,12 +103,12 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
||||||
setPage(page)
|
setPage(page)
|
||||||
}
|
}
|
||||||
|
|
||||||
return(
|
return (
|
||||||
<TableContainer className={classes.tableContainer} component={Paper}>
|
<TableContainer className={classes.tableContainer} component={Paper}>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
{title && <TableCell colSpan={6} className={classes.titleContainer}>
|
{title && <TableCell colSpan={3} className={classes.titleContainer}>
|
||||||
{typeof(title) === "string" ?
|
{typeof(title) === "string" ?
|
||||||
<Typography variant="h5" className={classes.title}>
|
<Typography variant="h5" className={classes.title}>
|
||||||
{title}
|
{title}
|
||||||
|
|
@ -117,7 +120,7 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
||||||
}
|
}
|
||||||
</TableCell>}
|
</TableCell>}
|
||||||
{ setSearchText &&
|
{ setSearchText &&
|
||||||
<TableCell colSpan={6} className={classes.searchContainer}>
|
<TableCell colSpan={title ? 3 : 6} className={classes.searchContainer}>
|
||||||
<TextField
|
<TextField
|
||||||
variant="standard"
|
variant="standard"
|
||||||
className={classes.title}
|
className={classes.title}
|
||||||
|
|
@ -137,7 +140,13 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
||||||
<TableRow>
|
<TableRow>
|
||||||
{ renderGutter && <TableCell></TableCell>}
|
{ renderGutter && <TableCell></TableCell>}
|
||||||
{ columns ?
|
{ columns ?
|
||||||
null
|
columns.map((column, index) => {
|
||||||
|
return (
|
||||||
|
<TableCell key={"header-cell-"+index}>
|
||||||
|
{column.title}
|
||||||
|
</TableCell>
|
||||||
|
)
|
||||||
|
})
|
||||||
: rows.length > 0 &&
|
: rows.length > 0 &&
|
||||||
Object.keys(rows[0]).map((value, index) => {
|
Object.keys(rows[0]).map((value, index) => {
|
||||||
return(
|
return(
|
||||||
|
|
@ -176,7 +185,18 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
||||||
</TableCell>
|
</TableCell>
|
||||||
}
|
}
|
||||||
{columns ?
|
{columns ?
|
||||||
null
|
columns.map((column, j) => {
|
||||||
|
if (column.render) return (
|
||||||
|
<TableCell key={"row-"+index+"cell-"+j}>
|
||||||
|
{column.render(row)}
|
||||||
|
</TableCell>
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<TableCell key={"row-"+index+"cell-"+j}>
|
||||||
|
{Object.values(row)[j].toString()}
|
||||||
|
</TableCell>
|
||||||
|
)
|
||||||
|
})
|
||||||
:
|
:
|
||||||
Object.values(row).map((value, j) => {
|
Object.values(row).map((value, j) => {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ export default function Router(props: Props) {
|
||||||
{/* Page routes */}
|
{/* Page routes */}
|
||||||
<Route index element={hello()} />
|
<Route index element={hello()} />
|
||||||
<Route path="/teams" element={<Teams/>} />
|
<Route path="/teams" element={<Teams/>} />
|
||||||
<Route path="/users" element={<Users title="Users"/>} />
|
<Route path="/users" element={<Users/>} />
|
||||||
{/* <Route
|
{/* <Route
|
||||||
path="/teams/:teamID"
|
path="/teams/:teamID"
|
||||||
element={<Team />}
|
element={<Team />}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Box,
|
|
||||||
Button,
|
Button,
|
||||||
Collapse,
|
Chip,
|
||||||
darken,
|
darken,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
Divider,
|
Divider,
|
||||||
|
Grid2,
|
||||||
IconButton,
|
IconButton,
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
|
|
@ -17,32 +17,19 @@ import {
|
||||||
ListItemText,
|
ListItemText,
|
||||||
ListSubheader,
|
ListSubheader,
|
||||||
PaletteColor,
|
PaletteColor,
|
||||||
Paper,
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableContainer,
|
|
||||||
TableHead,
|
|
||||||
TablePagination,
|
|
||||||
TableRow,
|
|
||||||
Theme,
|
Theme,
|
||||||
Typography,
|
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import ResponsiveDialog from "common/ResponsiveDialog";
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||||
// import { getTableIcons } from "common/ResponsiveTable";
|
|
||||||
import SearchSelect, { Option } from "common/SearchSelect";
|
import SearchSelect, { Option } from "common/SearchSelect";
|
||||||
// import SmartBreadcrumb from "common/SmartBreadcrumb";
|
import { useSnackbar, useUserAPI } from "hooks";
|
||||||
import { useMobile, useSnackbar, useUserAPI } from "hooks";
|
|
||||||
// import MaterialTable, { Column } from "material-table";
|
|
||||||
import { User } from "models";
|
import { User } from "models";
|
||||||
import PageContainer from "pages/PageContainer";
|
import PageContainer from "pages/PageContainer";
|
||||||
// import { ListUsersResponse } from "providers/pond/userAPI";
|
// import { ListUsersResponse } from "providers/pond/userAPI";
|
||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import { pond } from "protobuf-ts/pond";
|
|
||||||
import { getSecondaryColour, getSignatureAccentColour } from "services/whiteLabel";
|
import { getSecondaryColour, getSignatureAccentColour } from "services/whiteLabel";
|
||||||
import { Add, ChevronRight, Face, Remove } from "@mui/icons-material";
|
import { Add, Face, Remove } from "@mui/icons-material";
|
||||||
import ResponsiveTable from "common/ResponsiveTable";
|
import ResponsiveTable, { Column } from "common/ResponsiveTable";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => {
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
// const isMobile = useMobile()
|
// const isMobile = useMobile()
|
||||||
|
|
@ -98,12 +85,7 @@ const useStyles = makeStyles((theme: Theme) => {
|
||||||
}})},
|
}})},
|
||||||
);
|
);
|
||||||
|
|
||||||
interface Props {
|
export default function Users() {
|
||||||
title: string | JSX.Element;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Users(props: Props) {
|
|
||||||
const { title } = props;
|
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const userAPI = useUserAPI();
|
const userAPI = useUserAPI();
|
||||||
// let tableRef: any = React.createRef();
|
// let tableRef: any = React.createRef();
|
||||||
|
|
@ -118,13 +100,6 @@ export default function Users(props: Props) {
|
||||||
const [selectedFeature, setSelectedFeature] = useState<Option | undefined>();
|
const [selectedFeature, setSelectedFeature] = useState<Option | undefined>();
|
||||||
const [selectedUser, setSelectedUser] = useState(User.create());
|
const [selectedUser, setSelectedUser] = useState(User.create());
|
||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
const TitleMap = new Map([
|
|
||||||
["Uploaded", "uploaded"],
|
|
||||||
["Version", "version"],
|
|
||||||
["Platform", "platform"],
|
|
||||||
["Channel", "channel"],
|
|
||||||
["Size", "size"]
|
|
||||||
]);
|
|
||||||
const actions = [
|
const actions = [
|
||||||
"provision",
|
"provision",
|
||||||
"upload-firmware",
|
"upload-firmware",
|
||||||
|
|
@ -153,6 +128,32 @@ export default function Users(props: Props) {
|
||||||
"installer"
|
"installer"
|
||||||
].sort();
|
].sort();
|
||||||
|
|
||||||
|
const [rows, setRows] = useState<User[]>([])
|
||||||
|
|
||||||
|
const loadUsers = () => {
|
||||||
|
setIsLoading(true)
|
||||||
|
userAPI.listUsers(pageSize, page*pageSize, "desc", "name", searchText).then(resp => {
|
||||||
|
let r: User[] = []
|
||||||
|
resp.users.forEach((user, _index) => {
|
||||||
|
r.push(user)
|
||||||
|
})
|
||||||
|
setRows(r)
|
||||||
|
setTotal(resp.total)
|
||||||
|
}).then(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadUsers();
|
||||||
|
}, [page, pageSize, searchText]); // 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 allow = () => {
|
const allow = () => {
|
||||||
const action = selectedAction ? selectedAction.label : "";
|
const action = selectedAction ? selectedAction.label : "";
|
||||||
if (selectedUser.id() === "" || action === "") return;
|
if (selectedUser.id() === "" || action === "") return;
|
||||||
|
|
@ -215,6 +216,64 @@ export default function Users(props: Props) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const flagChip = (flag: any) => {
|
||||||
|
return (
|
||||||
|
<Grid2 key={flag} size={{xs: 12}} >
|
||||||
|
<Chip size="small" label={flag} color={"default"} />
|
||||||
|
</Grid2>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = (): Column<User>[] => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: "Profile",
|
||||||
|
render: 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Actions",
|
||||||
|
cellStyle: { verticalAlign: "top" },
|
||||||
|
render: user => {
|
||||||
|
const actions = user.settings.actions;
|
||||||
|
return (
|
||||||
|
<Grid2 container spacing={1} className={classes.chipContainer}>
|
||||||
|
{actions.slice(0, 2).map(action => flagChip(action))}
|
||||||
|
{actions.length > 3 && flagChip("+" + (actions.length - 3) + " more")}
|
||||||
|
</Grid2>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Features",
|
||||||
|
cellStyle: { verticalAlign: "top" },
|
||||||
|
render: user => {
|
||||||
|
const features = user.settings.features;
|
||||||
|
return (
|
||||||
|
<Grid2 container spacing={1} className={classes.chipContainer}>
|
||||||
|
{features.slice(0, 2).map(feature => flagChip(feature))}
|
||||||
|
{features.length > 3 && flagChip("+" + (features.length - 3) + " more")}
|
||||||
|
</Grid2>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
const details = (user: User) => {
|
const details = (user: User) => {
|
||||||
const item = (text: string, type: "action" | "feature") => (
|
const item = (text: string, type: "action" | "feature") => (
|
||||||
<ListItem key={text}>
|
<ListItem key={text}>
|
||||||
|
|
@ -232,8 +291,6 @@ export default function Users(props: Props) {
|
||||||
);
|
);
|
||||||
const actions = user.settings.actions;
|
const actions = user.settings.actions;
|
||||||
const features = user.settings.features;
|
const features = user.settings.features;
|
||||||
// const showActions = view !== "features";
|
|
||||||
// const showFeatures = view !== "actions";
|
|
||||||
return (
|
return (
|
||||||
<List disablePadding dense>
|
<List disablePadding dense>
|
||||||
<ListSubheader className={classes.gutter}>Actions</ListSubheader>
|
<ListSubheader className={classes.gutter}>Actions</ListSubheader>
|
||||||
|
|
@ -269,132 +326,24 @@ export default function Users(props: Props) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
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 = () => {
|
|
||||||
setIsLoading(true)
|
|
||||||
userAPI.listUsers(pageSize, page*pageSize, "desc", "name", searchText).then(resp => {
|
|
||||||
let r: User[] = []
|
|
||||||
resp.users.forEach((user, _index) => {
|
|
||||||
r.push(user)
|
|
||||||
})
|
|
||||||
setRows(r)
|
|
||||||
setTotal(resp.total)
|
|
||||||
}).then(() => {
|
|
||||||
setIsLoading(false);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
loadUsers();
|
|
||||||
}, [page, pageSize, searchText]); // 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(isLoading)
|
|
||||||
}, [isLoading])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<TableContainer className={classes.tableContainer} component={Paper}>
|
<ResponsiveTable<User>
|
||||||
<Table>
|
title="Users"
|
||||||
<TableHead>
|
rows={rows}
|
||||||
<TableRow>
|
columns={columns()}
|
||||||
<TableCell colSpan={6} className={classes.titleContainer}>
|
total={total}
|
||||||
{typeof(title) === "string" ?
|
page={page}
|
||||||
<Typography variant="h5" className={classes.title}>
|
pageSize={pageSize}
|
||||||
{title}
|
setPage={setPage}
|
||||||
</Typography>
|
handleRowsPerPageChange={handleRowsPerPageChange}
|
||||||
:
|
setSearchText={setSearchText}
|
||||||
<Box className={classes.title}>
|
isLoading={isLoading}
|
||||||
title
|
renderGutter={details}
|
||||||
</Box>
|
// multiGutter
|
||||||
}
|
/>
|
||||||
</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
|
<ResponsiveDialog
|
||||||
maxWidth="sm"
|
maxWidth="sm"
|
||||||
fullWidth
|
fullWidth
|
||||||
|
|
@ -442,7 +391,6 @@ export default function Users(props: Props) {
|
||||||
<SearchSelect
|
<SearchSelect
|
||||||
label="Select a feature to enable"
|
label="Select a feature to enable"
|
||||||
selected={selectedFeature}
|
selected={selectedFeature}
|
||||||
// options={features}
|
|
||||||
options={features
|
options={features
|
||||||
.filter(a => !selectedUser.settings.features.includes(a))
|
.filter(a => !selectedUser.settings.features.includes(a))
|
||||||
.map(a => {
|
.map(a => {
|
||||||
|
|
@ -467,19 +415,6 @@ export default function Users(props: Props) {
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</ResponsiveDialog>
|
</ResponsiveDialog>
|
||||||
<ResponsiveTable<User>
|
|
||||||
// title="Users"
|
|
||||||
rows={rows}
|
|
||||||
total={total}
|
|
||||||
page={page}
|
|
||||||
pageSize={pageSize}
|
|
||||||
setPage={setPage}
|
|
||||||
handleRowsPerPageChange={handleRowsPerPageChange}
|
|
||||||
setSearchText={setSearchText}
|
|
||||||
isLoading={isLoading}
|
|
||||||
renderGutter={details}
|
|
||||||
// multiGutter
|
|
||||||
/>
|
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue