Upgrade to React-Admin 4
This commit is contained in:
parent
08472937e8
commit
24a6b66203
@ -18,22 +18,20 @@
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"prettier": "^2.2.0",
|
||||
"ra-test": "^3.15.0"
|
||||
"prettier": "^2.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.7.1",
|
||||
"@emotion/styled": "^11.6.0",
|
||||
"@mui/icons-material": "^5.3.1",
|
||||
"@mui/material": "^5.11.7",
|
||||
"@mui/styles": "^5.11.7",
|
||||
"papaparse": "^5.2.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"ra-language-chinese": "^2.0.10",
|
||||
"ra-language-french": "^4.2.0",
|
||||
"ra-language-german": "^3.13.4",
|
||||
"react": "^17.0.0",
|
||||
"react-admin": "^3.19.7",
|
||||
"react-admin": "^4.7.3",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "^5.0.1"
|
||||
},
|
||||
|
13
src/App.js
13
src/App.js
@ -1,5 +1,10 @@
|
||||
import React from "react";
|
||||
import { Admin, Resource, resolveBrowserLocale } from "react-admin";
|
||||
import {
|
||||
Admin,
|
||||
CustomRoutes,
|
||||
Resource,
|
||||
resolveBrowserLocale,
|
||||
} from "react-admin";
|
||||
import polyglotI18nProvider from "ra-i18n-polyglot";
|
||||
import authProvider from "./synapse/authProvider";
|
||||
import dataProvider from "./synapse/dataProvider";
|
||||
@ -48,10 +53,10 @@ const App = () => (
|
||||
authProvider={authProvider}
|
||||
dataProvider={dataProvider}
|
||||
i18nProvider={i18nProvider}
|
||||
customRoutes={[
|
||||
<Route key="userImport" path="/import_users" component={ImportFeature} />,
|
||||
]}
|
||||
>
|
||||
<CustomRoutes>
|
||||
<Route path="/import_users" element={<ImportFeature />} />
|
||||
</CustomRoutes>
|
||||
<Resource
|
||||
name="users"
|
||||
list={UserList}
|
||||
|
@ -1,19 +1,20 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
fetchUtils,
|
||||
Form,
|
||||
FormDataConsumer,
|
||||
Notification,
|
||||
useLogin,
|
||||
useNotify,
|
||||
useLocale,
|
||||
useSetLocale,
|
||||
useLocaleState,
|
||||
useTranslate,
|
||||
PasswordInput,
|
||||
TextInput,
|
||||
} from "react-admin";
|
||||
import { Form, useForm } from "react-final-form";
|
||||
import { useForm } from "react-hook-form";
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
CardActions,
|
||||
@ -22,11 +23,10 @@ import {
|
||||
Select,
|
||||
TextField,
|
||||
} from "@mui/material";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { styled } from "@mui/material/styles";
|
||||
import LockIcon from "@mui/icons-material/Lock";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
main: {
|
||||
const FormBox = styled("div")(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
minHeight: "calc(100vh - 1em)",
|
||||
@ -36,36 +36,36 @@ const useStyles = makeStyles(theme => ({
|
||||
backgroundColor: "#f9f9f9",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundSize: "cover",
|
||||
},
|
||||
card: {
|
||||
|
||||
[`& .card`]: {
|
||||
minWidth: "30em",
|
||||
marginTop: "6em",
|
||||
marginBottom: "6em",
|
||||
},
|
||||
avatar: {
|
||||
[`& .avatar`]: {
|
||||
margin: "1em",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
},
|
||||
icon: {
|
||||
[`& .icon`]: {
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
},
|
||||
hint: {
|
||||
[`& .hint`]: {
|
||||
marginTop: "1em",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
color: theme.palette.grey[500],
|
||||
},
|
||||
form: {
|
||||
[`& .form`]: {
|
||||
padding: "0 1em 1em 1em",
|
||||
},
|
||||
input: {
|
||||
[`& .input`]: {
|
||||
marginTop: "1em",
|
||||
},
|
||||
actions: {
|
||||
[`& .actions`]: {
|
||||
padding: "0 1em 1em 1em",
|
||||
},
|
||||
serverVersion: {
|
||||
[`& .serverVersion`]: {
|
||||
color: "#9e9e9e",
|
||||
fontFamily: "Roboto, Helvetica, Arial, sans-serif",
|
||||
marginBottom: "1em",
|
||||
@ -73,14 +73,12 @@ const useStyles = makeStyles(theme => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const LoginPage = ({ theme }) => {
|
||||
const classes = useStyles({ theme });
|
||||
const LoginPage = () => {
|
||||
const login = useLogin();
|
||||
const notify = useNotify();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [supportPassAuth, setSupportPassAuth] = useState(true);
|
||||
var locale = useLocale();
|
||||
const setLocale = useSetLocale();
|
||||
const [locale, setLocale] = useLocaleState();
|
||||
const translate = useTranslate();
|
||||
const base_url = localStorage.getItem("base_url");
|
||||
const cfg_base_url = process.env.REACT_APP_SERVER;
|
||||
@ -263,8 +261,7 @@ const LoginPage = ({ theme }) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={classes.input}>
|
||||
<Box>
|
||||
<TextInput
|
||||
autoFocus
|
||||
name="username"
|
||||
@ -274,9 +271,8 @@ const LoginPage = ({ theme }) => {
|
||||
onBlur={handleUsernameChange}
|
||||
resettable
|
||||
fullWidth
|
||||
className="input"
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.input}>
|
||||
<PasswordInput
|
||||
name="password"
|
||||
component={renderInput}
|
||||
@ -285,9 +281,8 @@ const LoginPage = ({ theme }) => {
|
||||
disabled={loading || !supportPassAuth}
|
||||
resettable
|
||||
fullWidth
|
||||
className="input"
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.input}>
|
||||
<TextInput
|
||||
name="base_url"
|
||||
component={renderInput}
|
||||
@ -295,10 +290,10 @@ const LoginPage = ({ theme }) => {
|
||||
disabled={cfg_base_url || loading}
|
||||
resettable
|
||||
fullWidth
|
||||
className="input"
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.serverVersion}>{serverVersion}</div>
|
||||
</div>
|
||||
<Box className="serverVersion">{serverVersion}</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@ -307,20 +302,16 @@ const LoginPage = ({ theme }) => {
|
||||
initialValues={{ base_url: cfg_base_url || base_url }}
|
||||
onSubmit={handleSubmit}
|
||||
validate={validate}
|
||||
render={({ handleSubmit }) => (
|
||||
<form onSubmit={handleSubmit} noValidate>
|
||||
<div className={classes.main}>
|
||||
<Card className={classes.card}>
|
||||
<div className={classes.avatar}>
|
||||
<Avatar className={classes.icon}>
|
||||
>
|
||||
<FormBox>
|
||||
<Card className="card">
|
||||
<Box className="avatar">
|
||||
<Avatar className="icon">
|
||||
<LockIcon />
|
||||
</Avatar>
|
||||
</div>
|
||||
<div className={classes.hint}>
|
||||
{translate("synapseadmin.auth.welcome")}
|
||||
</div>
|
||||
<div className={classes.form}>
|
||||
<div className={classes.input}>
|
||||
</Box>
|
||||
<Box className="hint">{translate("synapseadmin.auth.welcome")}</Box>
|
||||
<Box className="form">
|
||||
<Select
|
||||
value={locale}
|
||||
onChange={e => {
|
||||
@ -328,18 +319,17 @@ const LoginPage = ({ theme }) => {
|
||||
}}
|
||||
fullWidth
|
||||
disabled={loading}
|
||||
className="input"
|
||||
>
|
||||
<MenuItem value="de">Deutsch</MenuItem>
|
||||
<MenuItem value="en">English</MenuItem>
|
||||
<MenuItem value="fr">Français</MenuItem>
|
||||
<MenuItem value="zh">简体中文</MenuItem>
|
||||
</Select>
|
||||
</div>
|
||||
<FormDataConsumer>
|
||||
{formDataProps => <UserData {...formDataProps} />}
|
||||
</FormDataConsumer>
|
||||
</div>
|
||||
<CardActions className={classes.actions}>
|
||||
<CardActions className="actions">
|
||||
<Button
|
||||
variant="contained"
|
||||
type="submit"
|
||||
@ -361,12 +351,11 @@ const LoginPage = ({ theme }) => {
|
||||
{translate("synapseadmin.auth.sso_sign_in")}
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Box>
|
||||
</Card>
|
||||
</FormBox>
|
||||
<Notification />
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
/>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
import React from "react";
|
||||
import { render } from "@testing-library/react";
|
||||
import { TestContext } from "ra-test";
|
||||
import { AdminContext } from "react-admin";
|
||||
import LoginPage from "./LoginPage";
|
||||
|
||||
describe("LoginForm", () => {
|
||||
it("renders", () => {
|
||||
render(
|
||||
<TestContext>
|
||||
<AdminContext>
|
||||
<LoginPage />
|
||||
</TestContext>
|
||||
</AdminContext>
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -1,26 +1,28 @@
|
||||
import React, { Fragment } from "react";
|
||||
import { Avatar, Chip } from "@mui/material";
|
||||
import { connect } from "react-redux";
|
||||
import { Avatar } from "@mui/material";
|
||||
import FolderSharedIcon from "@mui/icons-material/FolderShared";
|
||||
import {
|
||||
BooleanField,
|
||||
BulkDeleteButton,
|
||||
Button,
|
||||
Datagrid,
|
||||
DatagridConfigurable,
|
||||
ExportButton,
|
||||
DeleteButton,
|
||||
Filter,
|
||||
List,
|
||||
NumberField,
|
||||
Pagination,
|
||||
SelectColumnsButton,
|
||||
TextField,
|
||||
TopToolbar,
|
||||
useCreate,
|
||||
useMutation,
|
||||
useListContext,
|
||||
useNotify,
|
||||
useTranslate,
|
||||
useRecordContext,
|
||||
useRefresh,
|
||||
useUnselectAll,
|
||||
} from "react-admin";
|
||||
import { useMutation } from "react-query";
|
||||
|
||||
const RoomDirectoryPagination = props => (
|
||||
<Pagination {...props} rowsPerPageOptions={[100, 500, 1000, 2000]} />
|
||||
@ -59,26 +61,23 @@ export const RoomDirectoryBulkDeleteButton = props => (
|
||||
/>
|
||||
);
|
||||
|
||||
export const RoomDirectoryBulkSaveButton = ({ selectedIds }) => {
|
||||
export const RoomDirectoryBulkSaveButton = () => {
|
||||
const { selectedIds } = useListContext();
|
||||
const notify = useNotify();
|
||||
const refresh = useRefresh();
|
||||
const unselectAll = useUnselectAll();
|
||||
const [createMany, { loading }] = useMutation();
|
||||
const { createMany, isloading } = useMutation();
|
||||
|
||||
const handleSend = values => {
|
||||
createMany(
|
||||
["room_directory", "createMany", { ids: selectedIds, data: {} }],
|
||||
{
|
||||
type: "createMany",
|
||||
resource: "room_directory",
|
||||
payload: { ids: selectedIds, data: {} },
|
||||
},
|
||||
{
|
||||
onSuccess: ({ data }) => {
|
||||
onSuccess: data => {
|
||||
notify("resources.room_directory.action.send_success");
|
||||
unselectAll("rooms");
|
||||
refresh();
|
||||
},
|
||||
onFailure: error =>
|
||||
onError: error =>
|
||||
notify("resources.room_directory.action.send_failure", {
|
||||
type: "error",
|
||||
}),
|
||||
@ -90,18 +89,18 @@ export const RoomDirectoryBulkSaveButton = ({ selectedIds }) => {
|
||||
<Button
|
||||
label="resources.room_directory.action.create"
|
||||
onClick={handleSend}
|
||||
disabled={loading}
|
||||
disabled={isloading}
|
||||
>
|
||||
<FolderSharedIcon />
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export const RoomDirectorySaveButton = props => {
|
||||
export const RoomDirectorySaveButton = () => {
|
||||
const record = useRecordContext();
|
||||
const notify = useNotify();
|
||||
const refresh = useRefresh();
|
||||
const [create, { loading }] = useCreate("room_directory");
|
||||
const [create, { isloading }] = useCreate("room_directory");
|
||||
|
||||
const handleSend = values => {
|
||||
create(
|
||||
@ -109,11 +108,11 @@ export const RoomDirectorySaveButton = props => {
|
||||
payload: { data: { id: record.id } },
|
||||
},
|
||||
{
|
||||
onSuccess: ({ data }) => {
|
||||
onSuccess: data => {
|
||||
notify("resources.room_directory.action.send_success");
|
||||
refresh();
|
||||
},
|
||||
onFailure: error =>
|
||||
onError: error =>
|
||||
notify("resources.room_directory.action.send_failure", {
|
||||
type: "error",
|
||||
}),
|
||||
@ -125,16 +124,16 @@ export const RoomDirectorySaveButton = props => {
|
||||
<Button
|
||||
label="resources.room_directory.action.create"
|
||||
onClick={handleSend}
|
||||
disabled={loading}
|
||||
disabled={isloading}
|
||||
>
|
||||
<FolderSharedIcon />
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const RoomDirectoryBulkActionButtons = props => (
|
||||
const RoomDirectoryBulkActionButtons = () => (
|
||||
<Fragment>
|
||||
<RoomDirectoryBulkDeleteButton {...props} />
|
||||
<RoomDirectoryBulkDeleteButton />
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@ -142,51 +141,31 @@ const AvatarField = ({ source, className, record = {} }) => (
|
||||
<Avatar src={record[source]} className={className} />
|
||||
);
|
||||
|
||||
const RoomDirectoryFilter = ({ ...props }) => {
|
||||
const translate = useTranslate();
|
||||
return (
|
||||
<Filter {...props}>
|
||||
<Chip
|
||||
label={translate("resources.rooms.fields.room_id")}
|
||||
source="room_id"
|
||||
defaultValue={false}
|
||||
sx={{ marginBottom: "8px" }}
|
||||
/>
|
||||
<Chip
|
||||
label={translate("resources.rooms.fields.topic")}
|
||||
source="topic"
|
||||
defaultValue={false}
|
||||
sx={{ marginBottom: "8px" }}
|
||||
/>
|
||||
<Chip
|
||||
label={translate("resources.rooms.fields.canonical_alias")}
|
||||
source="canonical_alias"
|
||||
defaultValue={false}
|
||||
sx={{ marginBottom: "8px" }}
|
||||
/>
|
||||
</Filter>
|
||||
);
|
||||
};
|
||||
const RoomDirectoryListActions = () => (
|
||||
<TopToolbar>
|
||||
<SelectColumnsButton />
|
||||
<ExportButton />
|
||||
</TopToolbar>
|
||||
);
|
||||
|
||||
export const FilterableRoomDirectoryList = ({
|
||||
roomDirectoryFilters,
|
||||
dispatch,
|
||||
...props
|
||||
}) => {
|
||||
export const RoomDirectoryList = () => (
|
||||
/*
|
||||
const filter = roomDirectoryFilters;
|
||||
const roomIdFilter = filter && filter.room_id ? true : false;
|
||||
const topicFilter = filter && filter.topic ? true : false;
|
||||
const canonicalAliasFilter = filter && filter.canonical_alias ? true : false;
|
||||
*/
|
||||
|
||||
return (
|
||||
<List
|
||||
{...props}
|
||||
pagination={<RoomDirectoryPagination />}
|
||||
bulkActionButtons={<RoomDirectoryBulkActionButtons />}
|
||||
filters={<RoomDirectoryFilter />}
|
||||
perPage={100}
|
||||
actions={<RoomDirectoryListActions />}
|
||||
>
|
||||
<DatagridConfigurable
|
||||
rowClick={(id, resource, record) => "/rooms/" + id + "/show"}
|
||||
bulkActionButtons={<RoomDirectoryBulkActionButtons />}
|
||||
omit={["room_id", "canonical_alias", "topic"]}
|
||||
>
|
||||
<Datagrid rowClick={(id, basePath, record) => "/rooms/" + id + "/show"}>
|
||||
<AvatarField
|
||||
source="avatar_src"
|
||||
sortable={false}
|
||||
@ -198,27 +177,25 @@ export const FilterableRoomDirectoryList = ({
|
||||
sortable={false}
|
||||
label="resources.rooms.fields.name"
|
||||
/>
|
||||
{roomIdFilter && (
|
||||
|
||||
<TextField
|
||||
source="room_id"
|
||||
sortable={false}
|
||||
label="resources.rooms.fields.room_id"
|
||||
/>
|
||||
)}
|
||||
{canonicalAliasFilter && (
|
||||
|
||||
<TextField
|
||||
source="canonical_alias"
|
||||
sortable={false}
|
||||
label="resources.rooms.fields.canonical_alias"
|
||||
/>
|
||||
)}
|
||||
{topicFilter && (
|
||||
|
||||
<TextField
|
||||
source="topic"
|
||||
sortable={false}
|
||||
label="resources.rooms.fields.topic"
|
||||
/>
|
||||
)}
|
||||
|
||||
<NumberField
|
||||
source="num_joined_members"
|
||||
sortable={false}
|
||||
@ -234,18 +211,6 @@ export const FilterableRoomDirectoryList = ({
|
||||
sortable={false}
|
||||
label="resources.room_directory.fields.guest_can_join"
|
||||
/>
|
||||
</Datagrid>
|
||||
</DatagridConfigurable>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
roomDirectoryFilters:
|
||||
state.admin.resources.room_directory.list.params.displayedFilters,
|
||||
};
|
||||
}
|
||||
|
||||
export const RoomDirectoryList = connect(mapStateToProps)(
|
||||
FilterableRoomDirectoryList
|
||||
);
|
||||
|
@ -7,12 +7,13 @@ import {
|
||||
Toolbar,
|
||||
required,
|
||||
useCreate,
|
||||
useMutation,
|
||||
useListContext,
|
||||
useNotify,
|
||||
useRecordContext,
|
||||
useTranslate,
|
||||
useUnselectAll,
|
||||
} from "react-admin";
|
||||
import { useMutation } from "react-query";
|
||||
import MessageIcon from "@mui/icons-material/Message";
|
||||
import IconCancel from "@mui/icons-material/Cancel";
|
||||
import {
|
||||
@ -48,7 +49,6 @@ const ServerNoticeDialog = ({ open, loading, onClose, onSend }) => {
|
||||
</DialogContentText>
|
||||
<SimpleForm
|
||||
toolbar={<ServerNoticeToolbar />}
|
||||
submitOnEnter={false}
|
||||
redirect={false}
|
||||
save={onSend}
|
||||
>
|
||||
@ -67,11 +67,11 @@ const ServerNoticeDialog = ({ open, loading, onClose, onSend }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const ServerNoticeButton = props => {
|
||||
export const ServerNoticeButton = () => {
|
||||
const record = useRecordContext();
|
||||
const [open, setOpen] = useState(false);
|
||||
const notify = useNotify();
|
||||
const [create, { loading }] = useCreate("servernotices");
|
||||
const [create, { isloading }] = useCreate("servernotices");
|
||||
|
||||
const handleDialogOpen = () => setOpen(true);
|
||||
const handleDialogClose = () => setOpen(false);
|
||||
@ -84,7 +84,7 @@ export const ServerNoticeButton = props => {
|
||||
notify("resources.servernotices.action.send_success");
|
||||
handleDialogClose();
|
||||
},
|
||||
onFailure: () =>
|
||||
onError: () =>
|
||||
notify("resources.servernotices.action.send_failure", {
|
||||
type: "error",
|
||||
}),
|
||||
@ -97,7 +97,7 @@ export const ServerNoticeButton = props => {
|
||||
<Button
|
||||
label="resources.servernotices.send"
|
||||
onClick={handleDialogOpen}
|
||||
disabled={loading}
|
||||
disabled={isloading}
|
||||
>
|
||||
<MessageIcon />
|
||||
</Button>
|
||||
@ -110,29 +110,26 @@ export const ServerNoticeButton = props => {
|
||||
);
|
||||
};
|
||||
|
||||
export const ServerNoticeBulkButton = ({ selectedIds }) => {
|
||||
export const ServerNoticeBulkButton = () => {
|
||||
const { selectedIds } = useListContext();
|
||||
const [open, setOpen] = useState(false);
|
||||
const notify = useNotify();
|
||||
const unselectAll = useUnselectAll();
|
||||
const [createMany, { loading }] = useMutation();
|
||||
const { createMany, isloading } = useMutation();
|
||||
|
||||
const handleDialogOpen = () => setOpen(true);
|
||||
const handleDialogClose = () => setOpen(false);
|
||||
|
||||
const handleSend = values => {
|
||||
createMany(
|
||||
["servernotices", "createMany", { ids: selectedIds, data: values }],
|
||||
{
|
||||
type: "createMany",
|
||||
resource: "servernotices",
|
||||
payload: { ids: selectedIds, data: values },
|
||||
},
|
||||
{
|
||||
onSuccess: ({ data }) => {
|
||||
onSuccess: data => {
|
||||
notify("resources.servernotices.action.send_success");
|
||||
unselectAll("users");
|
||||
handleDialogClose();
|
||||
},
|
||||
onFailure: error =>
|
||||
onError: error =>
|
||||
notify("resources.servernotices.action.send_failure", {
|
||||
type: "error",
|
||||
}),
|
||||
@ -141,11 +138,11 @@ export const ServerNoticeBulkButton = ({ selectedIds }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
<Button
|
||||
label="resources.servernotices.send"
|
||||
onClick={handleDialogOpen}
|
||||
disabled={loading}
|
||||
disabled={isloading}
|
||||
>
|
||||
<MessageIcon />
|
||||
</Button>
|
||||
@ -154,6 +151,6 @@ export const ServerNoticeBulkButton = ({ selectedIds }) => {
|
||||
onClose={handleDialogClose}
|
||||
onSend={handleSend}
|
||||
/>
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -71,7 +71,7 @@ export const DestinationReconnectButton = props => {
|
||||
});
|
||||
refresh();
|
||||
},
|
||||
onFailure: () => {
|
||||
onError: () => {
|
||||
notify("ra.message.error", { type: "error" });
|
||||
},
|
||||
}
|
||||
@ -112,11 +112,11 @@ export const DestinationList = props => {
|
||||
filters={<DestinationFilter />}
|
||||
pagination={<DestinationPagination />}
|
||||
sort={{ field: "destination", order: "ASC" }}
|
||||
bulkActionButtons={false}
|
||||
>
|
||||
<Datagrid
|
||||
rowStyle={destinationRowStyle}
|
||||
rowClick={(id, basePath, record) => `${basePath}/${id}/show/rooms`}
|
||||
rowClick={(id, resource, record) => `${id}/show/rooms`}
|
||||
bulkActionButtons={false}
|
||||
>
|
||||
<TextField source="destination" />
|
||||
<DateField source="failure_ts" showTime options={date_format} />
|
||||
@ -160,7 +160,7 @@ export const DestinationShow = props => {
|
||||
>
|
||||
<Datagrid
|
||||
style={{ width: "100%" }}
|
||||
rowClick={(id, basePath, record) => `/rooms/${id}/show`}
|
||||
rowClick={(id, resource, record) => `/rooms/${id}/show`}
|
||||
>
|
||||
<TextField
|
||||
source="room_id"
|
||||
|
@ -7,30 +7,12 @@ import {
|
||||
useRecordContext,
|
||||
useRefresh,
|
||||
} from "react-admin";
|
||||
import { alpha, useTheme } from "@mui/material/styles";
|
||||
import ActionDelete from "@mui/icons-material/Delete";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { alpha } from "@mui/material/styles";
|
||||
import classnames from "classnames";
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
deleteButton: {
|
||||
color: theme.palette.error.main,
|
||||
"&:hover": {
|
||||
backgroundColor: alpha(theme.palette.error.main, 0.12),
|
||||
// Reset on mouse devices
|
||||
"@media (hover: none)": {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
{ name: "RaDeleteDeviceButton" }
|
||||
);
|
||||
|
||||
export const DeviceRemoveButton = props => {
|
||||
const theme = useTheme();
|
||||
const record = useRecordContext();
|
||||
const classes = useStyles(props);
|
||||
const [open, setOpen] = useState(false);
|
||||
const refresh = useRefresh();
|
||||
const notify = useNotify();
|
||||
@ -63,7 +45,16 @@ export const DeviceRemoveButton = props => {
|
||||
<Button
|
||||
label="ra.action.remove"
|
||||
onClick={handleClick}
|
||||
className={classnames("ra-delete-button", classes.deleteButton)}
|
||||
sx={{
|
||||
color: theme.palette.error.main,
|
||||
"&:hover": {
|
||||
backgroundColor: alpha(theme.palette.error.main, 0.12),
|
||||
// Reset on mouse devices
|
||||
"@media (hover: none)": {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ActionDelete />
|
||||
</Button>
|
||||
|
@ -1,7 +1,4 @@
|
||||
import React, { Fragment, useState } from "react";
|
||||
import classnames from "classnames";
|
||||
import { alpha } from "@mui/material/styles";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import {
|
||||
BooleanInput,
|
||||
Button,
|
||||
@ -17,6 +14,7 @@ import {
|
||||
useRefresh,
|
||||
useTranslate,
|
||||
} from "react-admin";
|
||||
import { alpha, useTheme } from "@mui/material/styles";
|
||||
import BlockIcon from "@mui/icons-material/Block";
|
||||
import ClearIcon from "@mui/icons-material/Clear";
|
||||
import DeleteSweepIcon from "@mui/icons-material/DeleteSweep";
|
||||
@ -31,22 +29,6 @@ import IconCancel from "@mui/icons-material/Cancel";
|
||||
import LockIcon from "@mui/icons-material/Lock";
|
||||
import LockOpenIcon from "@mui/icons-material/LockOpen";
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
deleteButton: {
|
||||
color: theme.palette.error.main,
|
||||
"&:hover": {
|
||||
backgroundColor: alpha(theme.palette.error.main, 0.12),
|
||||
// Reset on mouse devices
|
||||
"@media (hover: none)": {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
{ name: "RaDeleteDeviceButton" }
|
||||
);
|
||||
|
||||
const DeleteMediaDialog = ({ open, loading, onClose, onSend }) => {
|
||||
const translate = useTranslate();
|
||||
|
||||
@ -81,7 +63,6 @@ const DeleteMediaDialog = ({ open, loading, onClose, onSend }) => {
|
||||
</DialogContentText>
|
||||
<SimpleForm
|
||||
toolbar={<DeleteMediaToolbar />}
|
||||
submitOnEnter={false}
|
||||
redirect={false}
|
||||
save={onSend}
|
||||
>
|
||||
@ -113,10 +94,10 @@ const DeleteMediaDialog = ({ open, loading, onClose, onSend }) => {
|
||||
};
|
||||
|
||||
export const DeleteMediaButton = props => {
|
||||
const classes = useStyles(props);
|
||||
const theme = useTheme();
|
||||
const [open, setOpen] = useState(false);
|
||||
const notify = useNotify();
|
||||
const [deleteOne, { loading }] = useDelete("delete_media");
|
||||
const [deleteOne, { isLoading }] = useDelete("delete_media");
|
||||
|
||||
const handleDialogOpen = () => setOpen(true);
|
||||
const handleDialogClose = () => setOpen(false);
|
||||
@ -129,7 +110,7 @@ export const DeleteMediaButton = props => {
|
||||
notify("resources.delete_media.action.send_success");
|
||||
handleDialogClose();
|
||||
},
|
||||
onFailure: () =>
|
||||
onError: () =>
|
||||
notify("resources.delete_media.action.send_failure", {
|
||||
type: "error",
|
||||
}),
|
||||
@ -142,8 +123,17 @@ export const DeleteMediaButton = props => {
|
||||
<Button
|
||||
label="resources.delete_media.action.send"
|
||||
onClick={handleDialogOpen}
|
||||
disabled={loading}
|
||||
className={classnames("ra-delete-button", classes.deleteButton)}
|
||||
disabled={isLoading}
|
||||
sx={{
|
||||
color: theme.palette.error.main,
|
||||
"&:hover": {
|
||||
backgroundColor: alpha(theme.palette.error.main, 0.12),
|
||||
// Reset on mouse devices
|
||||
"@media (hover: none)": {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DeleteSweepIcon />
|
||||
</Button>
|
||||
@ -174,7 +164,7 @@ export const ProtectMediaButton = props => {
|
||||
notify("resources.protect_media.action.send_success");
|
||||
refresh();
|
||||
},
|
||||
onFailure: () =>
|
||||
onError: () =>
|
||||
notify("resources.protect_media.action.send_failure", {
|
||||
type: "error",
|
||||
}),
|
||||
@ -190,7 +180,7 @@ export const ProtectMediaButton = props => {
|
||||
notify("resources.protect_media.action.send_success");
|
||||
refresh();
|
||||
},
|
||||
onFailure: () =>
|
||||
onError: () =>
|
||||
notify("resources.protect_media.action.send_failure", {
|
||||
type: "error",
|
||||
}),
|
||||
@ -270,7 +260,7 @@ export const QuarantineMediaButton = props => {
|
||||
notify("resources.quarantine_media.action.send_success");
|
||||
refresh();
|
||||
},
|
||||
onFailure: () =>
|
||||
onError: () =>
|
||||
notify("resources.quarantine_media.action.send_failure", {
|
||||
type: "error",
|
||||
}),
|
||||
@ -286,7 +276,7 @@ export const QuarantineMediaButton = props => {
|
||||
notify("resources.quarantine_media.action.send_success");
|
||||
refresh();
|
||||
},
|
||||
onFailure: () =>
|
||||
onError: () =>
|
||||
notify("resources.quarantine_media.action.send_failure", {
|
||||
type: "error",
|
||||
}),
|
||||
|
@ -1,11 +1,12 @@
|
||||
import React, { Fragment } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
BooleanField,
|
||||
BulkDeleteButton,
|
||||
DateField,
|
||||
Datagrid,
|
||||
DatagridConfigurable,
|
||||
DeleteButton,
|
||||
ExportButton,
|
||||
Filter,
|
||||
List,
|
||||
NumberField,
|
||||
@ -13,6 +14,7 @@ import {
|
||||
ReferenceField,
|
||||
ReferenceManyField,
|
||||
SearchInput,
|
||||
SelectColumnsButton,
|
||||
SelectField,
|
||||
Show,
|
||||
Tab,
|
||||
@ -54,8 +56,9 @@ const RoomPagination = props => (
|
||||
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
||||
);
|
||||
|
||||
const EncryptionField = ({ source, record = {}, emptyText }) => {
|
||||
const EncryptionField = ({ source, emptyText }) => {
|
||||
const translate = useTranslate();
|
||||
const record = useRecordContext();
|
||||
const value = get(record, source);
|
||||
let ariaLabel = value === false ? "ra.boolean.false" : "ra.boolean.true";
|
||||
|
||||
@ -95,7 +98,7 @@ const RoomTitle = props => {
|
||||
);
|
||||
};
|
||||
|
||||
const RoomShowActions = ({ basePath, data, resource }) => {
|
||||
const RoomShowActions = ({ data, resource }) => {
|
||||
var roomDirectoryStatus = "";
|
||||
if (data) {
|
||||
roomDirectoryStatus = data.public;
|
||||
@ -110,7 +113,6 @@ const RoomShowActions = ({ basePath, data, resource }) => {
|
||||
<RoomDirectoryDeleteButton record={data} />
|
||||
)}
|
||||
<DeleteButton
|
||||
basePath={basePath}
|
||||
record={data}
|
||||
resource={resource}
|
||||
mutationMode="pessimistic"
|
||||
@ -163,7 +165,7 @@ export const RoomShow = props => {
|
||||
>
|
||||
<Datagrid
|
||||
style={{ width: "100%" }}
|
||||
rowClick={(id, basePath, record) => "/users/" + id}
|
||||
rowClick={(id, resource, record) => "/users/" + id}
|
||||
>
|
||||
<TextField
|
||||
source="id"
|
||||
@ -304,12 +306,11 @@ export const RoomShow = props => {
|
||||
);
|
||||
};
|
||||
|
||||
const RoomBulkActionButtons = props => (
|
||||
const RoomBulkActionButtons = () => (
|
||||
<Fragment>
|
||||
<RoomDirectoryBulkSaveButton {...props} />
|
||||
<RoomDirectoryBulkDeleteButton {...props} />
|
||||
<RoomDirectoryBulkSaveButton />
|
||||
<RoomDirectoryBulkDeleteButton />
|
||||
<BulkDeleteButton
|
||||
{...props}
|
||||
confirmTitle="resources.rooms.action.erase.title"
|
||||
confirmContent="resources.rooms.action.erase.content"
|
||||
mutationMode="pessimistic"
|
||||
@ -364,44 +365,38 @@ RoomNameField.propTypes = {
|
||||
source: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
const FilterableRoomList = ({ roomFilters, dispatch, ...props }) => {
|
||||
const filter = roomFilters;
|
||||
const localMembersFilter =
|
||||
filter && filter.joined_local_members ? true : false;
|
||||
const stateEventsFilter = filter && filter.state_events ? true : false;
|
||||
const versionFilter = filter && filter.version ? true : false;
|
||||
const federateableFilter = filter && filter.federatable ? true : false;
|
||||
const RoomListActions = () => (
|
||||
<TopToolbar>
|
||||
<SelectColumnsButton />
|
||||
<ExportButton />
|
||||
</TopToolbar>
|
||||
);
|
||||
|
||||
return (
|
||||
export const RoomList = () => (
|
||||
<List
|
||||
{...props}
|
||||
pagination={<RoomPagination />}
|
||||
sort={{ field: "name", order: "ASC" }}
|
||||
filters={<RoomFilter />}
|
||||
bulkActionButtons={<RoomBulkActionButtons />}
|
||||
actions={<RoomListActions />}
|
||||
>
|
||||
<DatagridConfigurable
|
||||
rowClick="show"
|
||||
bulkActionButtons={<RoomBulkActionButtons />}
|
||||
omit={["joined_local_members", "state_events", "version", "federatable"]}
|
||||
>
|
||||
<Datagrid rowClick="show">
|
||||
<EncryptionField
|
||||
source="is_encrypted"
|
||||
sortBy="encryption"
|
||||
label={<HttpsIcon />}
|
||||
//label={<HttpsIcon />}
|
||||
/>
|
||||
|
||||
<RoomNameField source="name" />
|
||||
<TextField source="joined_members" />
|
||||
{localMembersFilter && <TextField source="joined_local_members" />}
|
||||
{stateEventsFilter && <TextField source="state_events" />}
|
||||
{versionFilter && <TextField source="version" />}
|
||||
{federateableFilter && <BooleanField source="federatable" />}
|
||||
<TextField source="joined_local_members" />
|
||||
<TextField source="state_events" />
|
||||
<TextField source="version" />
|
||||
<BooleanField source="federatable" />
|
||||
<BooleanField source="public" />
|
||||
</Datagrid>
|
||||
</DatagridConfigurable>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
roomFilters: state.admin.resources.rooms.list.params.displayedFilters,
|
||||
};
|
||||
}
|
||||
|
||||
export const RoomList = connect(mapStateToProps)(FilterableRoomList);
|
||||
);
|
||||
|
@ -65,9 +65,11 @@ export const UserMediaStatsList = props => {
|
||||
filters={<UserMediaStatsFilter />}
|
||||
pagination={<UserMediaStatsPagination />}
|
||||
sort={{ field: "media_length", order: "DESC" }}
|
||||
>
|
||||
<Datagrid
|
||||
rowClick={(id, resource, record) => "/users/" + id + "/media"}
|
||||
bulkActionButtons={false}
|
||||
>
|
||||
<Datagrid rowClick={(id, basePath, record) => "/users/" + id + "/media"}>
|
||||
<TextField source="user_id" label="resources.users.fields.id" />
|
||||
<TextField
|
||||
source="displayname"
|
||||
|
@ -53,12 +53,6 @@ import { ServerNoticeButton, ServerNoticeBulkButton } from "./ServerNotices";
|
||||
import { DeviceRemoveButton } from "./devices";
|
||||
import { ProtectMediaButton, QuarantineMediaButton } from "./media";
|
||||
|
||||
const redirect = () => {
|
||||
return {
|
||||
pathname: "/import_users",
|
||||
};
|
||||
};
|
||||
|
||||
const choices_medium = [
|
||||
{ id: "email", name: "resources.users.email" },
|
||||
{ id: "msisdn", name: "resources.users.msisdn" },
|
||||
@ -88,7 +82,6 @@ const UserListActions = ({
|
||||
filterValues,
|
||||
permanentFilter,
|
||||
hasCreate, // you can hide CreateButton if hasCreate = false
|
||||
basePath,
|
||||
selectedIds,
|
||||
onUnselectItems,
|
||||
showFilter,
|
||||
@ -106,7 +99,7 @@ const UserListActions = ({
|
||||
filterValues,
|
||||
context: "button",
|
||||
})}
|
||||
<CreateButton basePath={basePath} />
|
||||
<CreateButton />
|
||||
<ExportButton
|
||||
disabled={total === 0}
|
||||
resource={resource}
|
||||
@ -116,7 +109,7 @@ const UserListActions = ({
|
||||
maxResults={maxResults}
|
||||
/>
|
||||
{/* Add your custom actions */}
|
||||
<Button component={Link} to={redirect} label="CSV Import">
|
||||
<Button component={Link} to="/import_users" label="CSV Import">
|
||||
<GetAppIcon sx={{ transform: "rotate(180deg)", fontSize: "20px" }} />
|
||||
</Button>
|
||||
</TopToolbar>
|
||||
@ -144,16 +137,15 @@ const UserFilter = props => (
|
||||
</Filter>
|
||||
);
|
||||
|
||||
const UserBulkActionButtons = props => (
|
||||
<Fragment>
|
||||
<ServerNoticeBulkButton {...props} />
|
||||
const UserBulkActionButtons = () => (
|
||||
<>
|
||||
<ServerNoticeBulkButton />
|
||||
<BulkDeleteButton
|
||||
{...props}
|
||||
label="resources.users.action.erase"
|
||||
confirmTitle="resources.users.helper.erase"
|
||||
mutationMode="pessimistic"
|
||||
/>
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
|
||||
const AvatarField = ({ source, record = {}, sx }) => (
|
||||
@ -168,10 +160,9 @@ export const UserList = props => {
|
||||
filterDefaultValues={{ guests: true, deactivated: false }}
|
||||
sort={{ field: "name", order: "ASC" }}
|
||||
actions={<UserListActions maxResults={10000} />}
|
||||
bulkActionButtons={<UserBulkActionButtons />}
|
||||
pagination={<UserPagination />}
|
||||
>
|
||||
<Datagrid rowClick="edit">
|
||||
<Datagrid rowClick="edit" bulkActionButtons={<UserBulkActionButtons />}>
|
||||
<AvatarField
|
||||
source="avatar_src"
|
||||
sx={{ height: "40px", width: "40px" }}
|
||||
@ -248,7 +239,7 @@ export function generateRandomUser() {
|
||||
|
||||
const UserEditToolbar = props => (
|
||||
<Toolbar {...props}>
|
||||
<SaveButton submitOnEnter={true} disabled={props.pristine} />
|
||||
<SaveButton disabled={props.pristine} />
|
||||
</Toolbar>
|
||||
);
|
||||
|
||||
@ -288,7 +279,6 @@ export const UserCreate = props => (
|
||||
source="user_type"
|
||||
choices={choices_type}
|
||||
translateChoice={false}
|
||||
allowEmpty={true}
|
||||
resettable
|
||||
/>
|
||||
<BooleanInput source="admin" />
|
||||
@ -354,7 +344,6 @@ export const UserEdit = props => {
|
||||
source="user_type"
|
||||
choices={choices_type}
|
||||
translateChoice={false}
|
||||
allowEmpty={true}
|
||||
resettable
|
||||
/>
|
||||
<BooleanInput source="admin" />
|
||||
@ -498,7 +487,7 @@ export const UserEdit = props => {
|
||||
>
|
||||
<Datagrid
|
||||
style={{ width: "100%" }}
|
||||
rowClick={(id, basePath, record) => "/rooms/" + id + "/show"}
|
||||
rowClick={(id, resource, record) => "/rooms/" + id + "/show"}
|
||||
>
|
||||
<TextField
|
||||
source="id"
|
||||
|
Loading…
Reference in New Issue
Block a user