2020-07-13 11:14:49 +03:00
|
|
|
import React, { cloneElement, Fragment } from "react";
|
2020-06-10 15:59:55 +03:00
|
|
|
import Avatar from "@material-ui/core/Avatar";
|
2020-03-28 23:25:34 +03:00
|
|
|
import PersonPinIcon from "@material-ui/icons/PersonPin";
|
2020-07-06 13:35:26 +03:00
|
|
|
import ContactMailIcon from "@material-ui/icons/ContactMail";
|
2020-07-08 11:58:57 +03:00
|
|
|
import DevicesIcon from "@material-ui/icons/Devices";
|
2020-09-22 12:30:27 +03:00
|
|
|
import GetAppIcon from "@material-ui/icons/GetApp";
|
2020-03-28 23:25:34 +03:00
|
|
|
import SettingsInputComponentIcon from "@material-ui/icons/SettingsInputComponent";
|
2021-02-11 22:37:20 +03:00
|
|
|
import NotificationsIcon from "@material-ui/icons/Notifications";
|
2021-02-11 23:11:34 +03:00
|
|
|
import PermMediaIcon from "@material-ui/icons/PermMedia";
|
|
|
|
import ViewListIcon from "@material-ui/icons/ViewList";
|
2020-02-07 19:44:48 +03:00
|
|
|
import {
|
2020-03-30 11:22:27 +03:00
|
|
|
ArrayInput,
|
2020-03-28 23:25:34 +03:00
|
|
|
ArrayField,
|
2020-09-22 12:30:27 +03:00
|
|
|
Button,
|
2020-02-07 19:44:48 +03:00
|
|
|
Datagrid,
|
2020-03-28 23:25:34 +03:00
|
|
|
DateField,
|
2020-02-07 19:44:48 +03:00
|
|
|
Create,
|
|
|
|
Edit,
|
|
|
|
List,
|
|
|
|
Filter,
|
2020-04-09 11:32:06 +03:00
|
|
|
Toolbar,
|
2020-02-07 19:44:48 +03:00
|
|
|
SimpleForm,
|
2020-03-30 11:22:27 +03:00
|
|
|
SimpleFormIterator,
|
2020-03-28 23:25:34 +03:00
|
|
|
TabbedForm,
|
|
|
|
FormTab,
|
2020-02-07 19:44:48 +03:00
|
|
|
BooleanField,
|
|
|
|
BooleanInput,
|
|
|
|
PasswordInput,
|
|
|
|
TextField,
|
|
|
|
TextInput,
|
|
|
|
ReferenceField,
|
2020-07-08 11:58:57 +03:00
|
|
|
ReferenceManyField,
|
2020-10-08 09:07:55 +03:00
|
|
|
SearchInput,
|
2020-03-30 11:22:27 +03:00
|
|
|
SelectInput,
|
2020-04-09 11:32:06 +03:00
|
|
|
BulkDeleteButton,
|
|
|
|
DeleteButton,
|
|
|
|
SaveButton,
|
2020-02-07 19:44:48 +03:00
|
|
|
regex,
|
2020-09-22 12:30:27 +03:00
|
|
|
useRedirect,
|
2020-04-09 11:32:06 +03:00
|
|
|
useTranslate,
|
2020-03-27 23:02:37 +03:00
|
|
|
Pagination,
|
2020-07-13 11:14:49 +03:00
|
|
|
CreateButton,
|
|
|
|
ExportButton,
|
|
|
|
TopToolbar,
|
|
|
|
sanitizeListRestProps,
|
2021-02-11 23:11:34 +03:00
|
|
|
NumberField,
|
2020-02-07 19:44:48 +03:00
|
|
|
} from "react-admin";
|
2020-05-06 10:03:33 +03:00
|
|
|
import { ServerNoticeButton, ServerNoticeBulkButton } from "./ServerNotices";
|
2020-07-08 12:11:24 +03:00
|
|
|
import { DeviceRemoveButton } from "./devices";
|
2020-06-10 15:59:55 +03:00
|
|
|
import { makeStyles } from "@material-ui/core/styles";
|
|
|
|
|
2020-09-22 12:30:27 +03:00
|
|
|
const redirect = (basePath, id, data) => {
|
|
|
|
return {
|
2020-11-11 13:38:37 +03:00
|
|
|
pathname: "/import_users",
|
2020-09-22 12:30:27 +03:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2020-06-10 15:59:55 +03:00
|
|
|
const useStyles = makeStyles({
|
|
|
|
small: {
|
|
|
|
height: "40px",
|
|
|
|
width: "40px",
|
|
|
|
},
|
|
|
|
large: {
|
|
|
|
height: "120px",
|
|
|
|
width: "120px",
|
|
|
|
float: "right",
|
|
|
|
},
|
|
|
|
});
|
2020-02-07 19:44:48 +03:00
|
|
|
|
2020-07-13 11:14:49 +03:00
|
|
|
const UserListActions = ({
|
|
|
|
currentSort,
|
|
|
|
className,
|
|
|
|
resource,
|
|
|
|
filters,
|
|
|
|
displayedFilters,
|
|
|
|
exporter, // you can hide ExportButton if exporter = (null || false)
|
|
|
|
filterValues,
|
|
|
|
permanentFilter,
|
|
|
|
hasCreate, // you can hide CreateButton if hasCreate = false
|
|
|
|
basePath,
|
|
|
|
selectedIds,
|
|
|
|
onUnselectItems,
|
|
|
|
showFilter,
|
|
|
|
maxResults,
|
|
|
|
total,
|
|
|
|
...rest
|
2020-09-22 12:30:27 +03:00
|
|
|
}) => {
|
|
|
|
const redirectTo = useRedirect();
|
|
|
|
return (
|
|
|
|
<TopToolbar className={className} {...sanitizeListRestProps(rest)}>
|
|
|
|
{filters &&
|
|
|
|
cloneElement(filters, {
|
|
|
|
resource,
|
|
|
|
showFilter,
|
|
|
|
displayedFilters,
|
|
|
|
filterValues,
|
|
|
|
context: "button",
|
|
|
|
})}
|
|
|
|
<CreateButton basePath={basePath} />
|
|
|
|
<ExportButton
|
|
|
|
disabled={total === 0}
|
|
|
|
resource={resource}
|
|
|
|
sort={currentSort}
|
|
|
|
filter={{ ...filterValues, ...permanentFilter }}
|
|
|
|
exporter={exporter}
|
|
|
|
maxResults={maxResults}
|
|
|
|
/>
|
|
|
|
{/* Add your custom actions */}
|
|
|
|
<Button
|
|
|
|
onClick={() => {
|
|
|
|
redirectTo(redirect);
|
|
|
|
}}
|
|
|
|
label="CSV Import"
|
|
|
|
>
|
|
|
|
<GetAppIcon style={{ transform: "rotate(180deg)", fontSize: "20" }} />
|
|
|
|
</Button>
|
|
|
|
</TopToolbar>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
UserListActions.defaultProps = {
|
|
|
|
selectedIds: [],
|
|
|
|
onUnselectItems: () => null,
|
|
|
|
};
|
2020-07-13 11:14:49 +03:00
|
|
|
|
2020-03-27 23:02:37 +03:00
|
|
|
const UserPagination = props => (
|
|
|
|
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
|
|
|
);
|
|
|
|
|
2020-02-07 19:44:48 +03:00
|
|
|
const UserFilter = props => (
|
|
|
|
<Filter {...props}>
|
2020-10-08 09:07:55 +03:00
|
|
|
<SearchInput source="name" alwaysOn />
|
2020-02-07 19:44:48 +03:00
|
|
|
<BooleanInput source="guests" alwaysOn />
|
|
|
|
<BooleanInput
|
|
|
|
label="resources.users.fields.show_deactivated"
|
|
|
|
source="deactivated"
|
|
|
|
alwaysOn
|
|
|
|
/>
|
|
|
|
</Filter>
|
|
|
|
);
|
|
|
|
|
2021-05-04 14:52:43 +03:00
|
|
|
const UserBulkActionButtons = props => (
|
|
|
|
<Fragment>
|
|
|
|
<ServerNoticeBulkButton {...props} />
|
|
|
|
<BulkDeleteButton
|
|
|
|
{...props}
|
|
|
|
label="resources.users.action.erase"
|
|
|
|
confirmTitle="resources.users.helper.erase"
|
|
|
|
undoable={false}
|
|
|
|
/>
|
|
|
|
</Fragment>
|
|
|
|
);
|
2020-04-09 11:32:06 +03:00
|
|
|
|
2020-06-10 15:59:55 +03:00
|
|
|
const AvatarField = ({ source, className, record = {} }) => (
|
|
|
|
<Avatar src={record[source]} className={className} />
|
2020-02-07 19:44:48 +03:00
|
|
|
);
|
|
|
|
|
2020-06-10 15:59:55 +03:00
|
|
|
export const UserList = props => {
|
|
|
|
const classes = useStyles();
|
|
|
|
return (
|
|
|
|
<List
|
|
|
|
{...props}
|
|
|
|
filters={<UserFilter />}
|
|
|
|
filterDefaultValues={{ guests: true, deactivated: false }}
|
2020-07-13 11:14:49 +03:00
|
|
|
actions={<UserListActions maxResults={10000} />}
|
2020-06-10 15:59:55 +03:00
|
|
|
bulkActionButtons={<UserBulkActionButtons />}
|
|
|
|
pagination={<UserPagination />}
|
|
|
|
>
|
|
|
|
<Datagrid rowClick="edit">
|
|
|
|
<AvatarField
|
|
|
|
source="avatar_src"
|
|
|
|
sortable={false}
|
|
|
|
className={classes.small}
|
|
|
|
/>
|
|
|
|
<TextField source="id" sortable={false} />
|
|
|
|
<TextField source="displayname" sortable={false} />
|
|
|
|
<BooleanField source="is_guest" sortable={false} />
|
|
|
|
<BooleanField source="admin" sortable={false} />
|
|
|
|
<BooleanField source="deactivated" sortable={false} />
|
|
|
|
</Datagrid>
|
|
|
|
</List>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2020-02-07 19:44:48 +03:00
|
|
|
// https://matrix.org/docs/spec/appendices#user-identifiers
|
|
|
|
const validateUser = regex(
|
|
|
|
/^@[a-z0-9._=\-/]+:.*/,
|
|
|
|
"synapseadmin.users.invalid_user_id"
|
|
|
|
);
|
|
|
|
|
2020-09-22 12:30:27 +03:00
|
|
|
export function generateRandomUser() {
|
|
|
|
const homeserver = localStorage.getItem("home_server");
|
|
|
|
const user_id =
|
|
|
|
"@" +
|
|
|
|
Array(8)
|
|
|
|
.fill("0123456789abcdefghijklmnopqrstuvwxyz")
|
|
|
|
.map(
|
|
|
|
x =>
|
|
|
|
x[
|
|
|
|
Math.floor(
|
|
|
|
(crypto.getRandomValues(new Uint32Array(1))[0] /
|
|
|
|
(0xffffffff + 1)) *
|
|
|
|
x.length
|
|
|
|
)
|
|
|
|
]
|
|
|
|
)
|
|
|
|
.join("") +
|
|
|
|
":" +
|
|
|
|
homeserver;
|
|
|
|
|
|
|
|
const password = Array(20)
|
|
|
|
.fill(
|
|
|
|
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~!@-#$"
|
|
|
|
)
|
|
|
|
.map(
|
|
|
|
x =>
|
|
|
|
x[
|
|
|
|
Math.floor(
|
|
|
|
(crypto.getRandomValues(new Uint32Array(1))[0] / (0xffffffff + 1)) *
|
|
|
|
x.length
|
|
|
|
)
|
|
|
|
]
|
|
|
|
)
|
|
|
|
.join("");
|
|
|
|
|
|
|
|
return {
|
|
|
|
id: user_id,
|
|
|
|
password: password,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-04-09 11:32:06 +03:00
|
|
|
const UserEditToolbar = props => {
|
|
|
|
const translate = useTranslate();
|
|
|
|
return (
|
|
|
|
<Toolbar {...props}>
|
|
|
|
<SaveButton submitOnEnter={true} />
|
|
|
|
<DeleteButton
|
|
|
|
label="resources.users.action.erase"
|
2021-05-04 14:52:43 +03:00
|
|
|
confirmTitle={translate("resources.users.helper.erase", {
|
|
|
|
smart_count: 1,
|
|
|
|
})}
|
|
|
|
mutationMode="pessimistic"
|
2020-04-09 11:32:06 +03:00
|
|
|
/>
|
2020-04-23 11:00:46 +03:00
|
|
|
<ServerNoticeButton />
|
2020-04-09 11:32:06 +03:00
|
|
|
</Toolbar>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2020-02-07 19:44:48 +03:00
|
|
|
export const UserCreate = props => (
|
|
|
|
<Create {...props}>
|
|
|
|
<SimpleForm>
|
|
|
|
<TextInput source="id" autoComplete="off" validate={validateUser} />
|
|
|
|
<TextInput source="displayname" />
|
|
|
|
<PasswordInput source="password" autoComplete="new-password" />
|
|
|
|
<BooleanInput source="admin" />
|
2020-03-30 11:22:27 +03:00
|
|
|
<ArrayInput source="threepids">
|
|
|
|
<SimpleFormIterator>
|
|
|
|
<SelectInput
|
|
|
|
source="medium"
|
|
|
|
choices={[
|
|
|
|
{ id: "email", name: "resources.users.email" },
|
|
|
|
{ id: "msisdn", name: "resources.users.msisdn" },
|
|
|
|
]}
|
|
|
|
/>
|
|
|
|
<TextInput source="address" />
|
|
|
|
</SimpleFormIterator>
|
|
|
|
</ArrayInput>
|
2020-02-07 19:44:48 +03:00
|
|
|
</SimpleForm>
|
|
|
|
</Create>
|
|
|
|
);
|
|
|
|
|
2020-04-21 19:39:51 +03:00
|
|
|
const UserTitle = ({ record }) => {
|
|
|
|
const translate = useTranslate();
|
|
|
|
return (
|
|
|
|
<span>
|
2020-06-16 10:52:08 +03:00
|
|
|
{translate("resources.users.name", {
|
|
|
|
smart_count: 1,
|
|
|
|
})}{" "}
|
2020-04-21 19:39:51 +03:00
|
|
|
{record ? `"${record.displayname}"` : ""}
|
|
|
|
</span>
|
|
|
|
);
|
|
|
|
};
|
2020-06-10 15:59:55 +03:00
|
|
|
export const UserEdit = props => {
|
|
|
|
const classes = useStyles();
|
2020-07-08 11:58:57 +03:00
|
|
|
const translate = useTranslate();
|
2020-06-10 15:59:55 +03:00
|
|
|
return (
|
|
|
|
<Edit {...props} title={<UserTitle />}>
|
|
|
|
<TabbedForm toolbar={<UserEditToolbar />}>
|
2020-08-11 11:25:05 +03:00
|
|
|
<FormTab
|
|
|
|
label={translate("resources.users.name", { smart_count: 1 })}
|
|
|
|
icon={<PersonPinIcon />}
|
|
|
|
>
|
2020-06-10 15:59:55 +03:00
|
|
|
<AvatarField
|
|
|
|
source="avatar_src"
|
|
|
|
sortable={false}
|
|
|
|
className={classes.large}
|
|
|
|
/>
|
|
|
|
<TextInput source="id" disabled />
|
|
|
|
<TextInput source="displayname" />
|
|
|
|
<PasswordInput source="password" autoComplete="new-password" />
|
|
|
|
<BooleanInput source="admin" />
|
|
|
|
<BooleanInput
|
|
|
|
source="deactivated"
|
|
|
|
helperText="resources.users.helper.deactivate"
|
|
|
|
/>
|
|
|
|
<DateField
|
|
|
|
source="creation_ts_ms"
|
|
|
|
showTime
|
|
|
|
options={{
|
|
|
|
year: "numeric",
|
|
|
|
month: "2-digit",
|
|
|
|
day: "2-digit",
|
|
|
|
hour: "2-digit",
|
|
|
|
minute: "2-digit",
|
|
|
|
second: "2-digit",
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
<TextField source="consent_version" />
|
|
|
|
</FormTab>
|
2021-02-11 22:37:20 +03:00
|
|
|
|
2020-06-10 15:59:55 +03:00
|
|
|
<FormTab
|
|
|
|
label="resources.users.threepid"
|
|
|
|
icon={<ContactMailIcon />}
|
|
|
|
path="threepid"
|
2020-06-16 08:54:49 +03:00
|
|
|
>
|
2020-06-10 15:59:55 +03:00
|
|
|
<ArrayInput source="threepids">
|
|
|
|
<SimpleFormIterator>
|
|
|
|
<SelectInput
|
|
|
|
source="medium"
|
|
|
|
choices={[
|
|
|
|
{ id: "email", name: "resources.users.email" },
|
|
|
|
{ id: "msisdn", name: "resources.users.msisdn" },
|
|
|
|
]}
|
2020-03-28 23:25:34 +03:00
|
|
|
/>
|
2020-06-10 15:59:55 +03:00
|
|
|
<TextInput source="address" />
|
|
|
|
</SimpleFormIterator>
|
|
|
|
</ArrayInput>
|
|
|
|
</FormTab>
|
2021-02-11 22:37:20 +03:00
|
|
|
|
2020-07-08 11:58:57 +03:00
|
|
|
<FormTab
|
|
|
|
label={translate("resources.devices.name", { smart_count: 2 })}
|
|
|
|
icon={<DevicesIcon />}
|
|
|
|
path="devices"
|
|
|
|
>
|
|
|
|
<ReferenceManyField
|
|
|
|
reference="devices"
|
|
|
|
target="user_id"
|
|
|
|
addLabel={false}
|
|
|
|
>
|
|
|
|
<Datagrid style={{ width: "100%" }}>
|
|
|
|
<TextField source="device_id" sortable={false} />
|
|
|
|
<TextField source="display_name" sortable={false} />
|
|
|
|
<TextField source="last_seen_ip" sortable={false} />
|
|
|
|
<DateField
|
|
|
|
source="last_seen_ts"
|
|
|
|
showTime
|
|
|
|
options={{
|
|
|
|
year: "numeric",
|
|
|
|
month: "2-digit",
|
|
|
|
day: "2-digit",
|
|
|
|
hour: "2-digit",
|
|
|
|
minute: "2-digit",
|
|
|
|
second: "2-digit",
|
|
|
|
}}
|
|
|
|
sortable={false}
|
|
|
|
/>
|
2020-07-08 12:11:24 +03:00
|
|
|
<DeviceRemoveButton />
|
2020-07-08 11:58:57 +03:00
|
|
|
</Datagrid>
|
|
|
|
</ReferenceManyField>
|
|
|
|
</FormTab>
|
2021-02-11 22:37:20 +03:00
|
|
|
|
2020-06-10 15:59:55 +03:00
|
|
|
<FormTab
|
|
|
|
label="resources.connections.name"
|
|
|
|
icon={<SettingsInputComponentIcon />}
|
|
|
|
path="connections"
|
|
|
|
>
|
|
|
|
<ReferenceField
|
|
|
|
reference="connections"
|
|
|
|
source="id"
|
|
|
|
addLabel={false}
|
|
|
|
link={false}
|
|
|
|
>
|
|
|
|
<ArrayField
|
|
|
|
source="devices[].sessions[0].connections"
|
|
|
|
label="resources.connections.name"
|
|
|
|
>
|
|
|
|
<Datagrid style={{ width: "100%" }}>
|
|
|
|
<TextField source="ip" sortable={false} />
|
|
|
|
<DateField
|
|
|
|
source="last_seen"
|
|
|
|
showTime
|
|
|
|
options={{
|
|
|
|
year: "numeric",
|
|
|
|
month: "2-digit",
|
|
|
|
day: "2-digit",
|
|
|
|
hour: "2-digit",
|
|
|
|
minute: "2-digit",
|
|
|
|
second: "2-digit",
|
|
|
|
}}
|
|
|
|
sortable={false}
|
|
|
|
/>
|
|
|
|
<TextField
|
|
|
|
source="user_agent"
|
|
|
|
sortable={false}
|
|
|
|
style={{ width: "100%" }}
|
|
|
|
/>
|
|
|
|
</Datagrid>
|
|
|
|
</ArrayField>
|
|
|
|
</ReferenceField>
|
|
|
|
</FormTab>
|
2021-02-11 22:37:20 +03:00
|
|
|
|
2021-02-11 23:11:34 +03:00
|
|
|
<FormTab
|
|
|
|
label={translate("resources.users_media.name", { smart_count: 2 })}
|
|
|
|
icon={<PermMediaIcon />}
|
|
|
|
path="media"
|
|
|
|
>
|
|
|
|
<ReferenceManyField
|
|
|
|
reference="users_media"
|
|
|
|
target="user_id"
|
|
|
|
addLabel={false}
|
|
|
|
pagination={<UserPagination />}
|
|
|
|
perPage={50}
|
2021-05-04 17:18:12 +03:00
|
|
|
sort={{ field: "created_ts", order: "DESC" }}
|
2021-02-11 23:11:34 +03:00
|
|
|
>
|
|
|
|
<Datagrid style={{ width: "100%" }}>
|
|
|
|
<DateField
|
|
|
|
source="created_ts"
|
|
|
|
showTime
|
|
|
|
options={{
|
|
|
|
year: "numeric",
|
|
|
|
month: "2-digit",
|
|
|
|
day: "2-digit",
|
|
|
|
hour: "2-digit",
|
|
|
|
minute: "2-digit",
|
|
|
|
second: "2-digit",
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
<DateField
|
|
|
|
source="last_access_ts"
|
|
|
|
showTime
|
|
|
|
options={{
|
|
|
|
year: "numeric",
|
|
|
|
month: "2-digit",
|
|
|
|
day: "2-digit",
|
|
|
|
hour: "2-digit",
|
|
|
|
minute: "2-digit",
|
|
|
|
second: "2-digit",
|
|
|
|
}}
|
|
|
|
/>
|
2021-05-04 17:18:12 +03:00
|
|
|
<TextField source="media_id" />
|
|
|
|
<NumberField source="media_length" />
|
|
|
|
<TextField source="media_type" />
|
|
|
|
<TextField source="upload_name" />
|
|
|
|
<TextField source="quarantined_by" />
|
|
|
|
<BooleanField source="safe_from_quarantine" />
|
2021-05-04 14:52:43 +03:00
|
|
|
<DeleteButton mutationMode="pessimistic" redirect={false} />
|
2021-02-11 23:11:34 +03:00
|
|
|
</Datagrid>
|
|
|
|
</ReferenceManyField>
|
|
|
|
</FormTab>
|
|
|
|
|
2021-02-11 22:45:02 +03:00
|
|
|
<FormTab
|
|
|
|
label={translate("resources.rooms.name", { smart_count: 2 })}
|
|
|
|
icon={<ViewListIcon />}
|
|
|
|
path="rooms"
|
|
|
|
>
|
|
|
|
<ReferenceManyField
|
|
|
|
reference="joined_rooms"
|
|
|
|
target="user_id"
|
|
|
|
addLabel={false}
|
|
|
|
>
|
|
|
|
<Datagrid
|
|
|
|
style={{ width: "100%" }}
|
|
|
|
rowClick={(id, basePath, record) => "/rooms/" + id + "/show"}
|
|
|
|
>
|
|
|
|
<TextField
|
|
|
|
source="id"
|
|
|
|
sortable={false}
|
|
|
|
label="resources.rooms.fields.room_id"
|
|
|
|
/>
|
|
|
|
<ReferenceField
|
|
|
|
label="resources.rooms.fields.name"
|
|
|
|
source="id"
|
|
|
|
reference="rooms"
|
|
|
|
sortable={false}
|
|
|
|
link=""
|
|
|
|
>
|
|
|
|
<TextField source="name" sortable={false} />
|
|
|
|
</ReferenceField>
|
|
|
|
</Datagrid>
|
|
|
|
</ReferenceManyField>
|
|
|
|
</FormTab>
|
|
|
|
|
2021-02-11 22:37:20 +03:00
|
|
|
<FormTab
|
|
|
|
label={translate("resources.pushers.name", { smart_count: 2 })}
|
|
|
|
icon={<NotificationsIcon />}
|
|
|
|
path="pushers"
|
|
|
|
>
|
|
|
|
<ReferenceManyField
|
|
|
|
reference="pushers"
|
|
|
|
target="user_id"
|
|
|
|
addLabel={false}
|
|
|
|
>
|
|
|
|
<Datagrid style={{ width: "100%" }}>
|
|
|
|
<TextField source="kind" sortable={false} />
|
|
|
|
<TextField source="app_display_name" sortable={false} />
|
|
|
|
<TextField source="app_id" sortable={false} />
|
|
|
|
<TextField source="data.url" sortable={false} />
|
|
|
|
<TextField source="device_display_name" sortable={false} />
|
|
|
|
<TextField source="lang" sortable={false} />
|
|
|
|
<TextField source="profile_tag" sortable={false} />
|
|
|
|
<TextField source="pushkey" sortable={false} />
|
|
|
|
</Datagrid>
|
|
|
|
</ReferenceManyField>
|
|
|
|
</FormTab>
|
2020-06-10 15:59:55 +03:00
|
|
|
</TabbedForm>
|
|
|
|
</Edit>
|
|
|
|
);
|
|
|
|
};
|