client/app/pages/groups/GroupDataSources.jsx (236 lines of code) (raw):
import { filter, map, includes, toLower } from "lodash";
import React from "react";
import Button from "antd/lib/button";
import Dropdown from "antd/lib/dropdown";
import Menu from "antd/lib/menu";
import DownOutlinedIcon from "@ant-design/icons/DownOutlined";
import routeWithUserSession from "@/components/ApplicationArea/routeWithUserSession";
import navigateTo from "@/components/ApplicationArea/navigateTo";
import Paginator from "@/components/Paginator";
import { wrap as itemsList, ControllerType } from "@/components/items-list/ItemsList";
import { ResourceItemsSource } from "@/components/items-list/classes/ItemsSource";
import { StateStorage } from "@/components/items-list/classes/StateStorage";
import LoadingState from "@/components/items-list/components/LoadingState";
import ItemsTable, { Columns } from "@/components/items-list/components/ItemsTable";
import SelectItemsDialog from "@/components/SelectItemsDialog";
import { DataSourcePreviewCard } from "@/components/PreviewCard";
import GroupName from "@/components/groups/GroupName";
import ListItemAddon from "@/components/groups/ListItemAddon";
import Sidebar from "@/components/groups/DetailsPageSidebar";
import Layout from "@/components/layouts/ContentWithSidebar";
import wrapSettingsTab from "@/components/SettingsWrapper";
import notification from "@/services/notification";
import { currentUser } from "@/services/auth";
import Group from "@/services/group";
import DataSource from "@/services/data-source";
import routes from "@/services/routes";
class GroupDataSources extends React.Component {
static propTypes = {
controller: ControllerType.isRequired,
};
groupId = parseInt(this.props.controller.params.groupId, 10);
group = null;
sidebarMenu = [
{
key: "users",
href: `groups/${this.groupId}`,
title: "Members",
},
{
key: "datasources",
href: `groups/${this.groupId}/data_sources`,
title: "Data Sources",
isAvailable: () => currentUser.isAdmin,
},
];
listColumns = [
Columns.custom((text, datasource) => <DataSourcePreviewCard dataSource={datasource} withLink />, {
title: "Name",
field: "name",
width: null,
}),
Columns.custom(
(text, datasource) => {
const menu = (
<Menu
selectedKeys={[datasource.view_only ? "viewonly" : "full"]}
onClick={item => this.setDataSourcePermissions(datasource, item.key)}>
<Menu.Item key="full">Full Access</Menu.Item>
<Menu.Item key="viewonly">View Only</Menu.Item>
</Menu>
);
return (
<Dropdown trigger={["click"]} overlay={menu}>
<Button className="w-100" aria-label="Permissions">
{datasource.view_only ? "View Only" : "Full Access"}
<DownOutlinedIcon aria-hidden="true" />
</Button>
</Dropdown>
);
},
{
width: "1%",
className: "p-r-0",
isAvailable: () => currentUser.isAdmin,
}
),
Columns.custom(
(text, datasource) => (
<Button className="w-100" type="danger" onClick={() => this.removeGroupDataSource(datasource)}>
Remove
</Button>
),
{
width: "1%",
isAvailable: () => currentUser.isAdmin,
}
),
];
componentDidMount() {
Group.get({ id: this.groupId })
.then(group => {
this.group = group;
this.forceUpdate();
})
.catch(error => {
this.props.controller.handleError(error);
});
}
removeGroupDataSource = datasource => {
Group.removeDataSource({ id: this.groupId, dataSourceId: datasource.id })
.then(() => {
this.props.controller.updatePagination({ page: 1 });
this.props.controller.update();
})
.catch(() => {
notification.error("Failed to remove data source from group.");
});
};
setDataSourcePermissions = (datasource, permission) => {
const viewOnly = permission !== "full";
Group.updateDataSource({ id: this.groupId, dataSourceId: datasource.id }, { view_only: viewOnly })
.then(() => {
datasource.view_only = viewOnly;
this.forceUpdate();
})
.catch(() => {
notification.error("Failed change data source permissions.");
});
};
addDataSources = () => {
const allDataSources = DataSource.query();
const alreadyAddedDataSources = map(this.props.controller.allItems, ds => ds.id);
SelectItemsDialog.showModal({
dialogTitle: "Add Data Sources",
inputPlaceholder: "Search data sources...",
selectedItemsTitle: "New Data Sources",
searchItems: searchTerm => {
searchTerm = toLower(searchTerm);
return allDataSources.then(items => filter(items, ds => includes(toLower(ds.name), searchTerm)));
},
renderItem: (item, { isSelected }) => {
const alreadyInGroup = includes(alreadyAddedDataSources, item.id);
return {
content: (
<DataSourcePreviewCard dataSource={item}>
<ListItemAddon isSelected={isSelected} alreadyInGroup={alreadyInGroup} />
</DataSourcePreviewCard>
),
isDisabled: alreadyInGroup,
className: isSelected || alreadyInGroup ? "selected" : "",
};
},
renderStagedItem: (item, { isSelected }) => ({
content: (
<DataSourcePreviewCard dataSource={item}>
<ListItemAddon isSelected={isSelected} isStaged />
</DataSourcePreviewCard>
),
}),
}).onClose(items => {
const promises = map(items, ds => Group.addDataSource({ id: this.groupId }, { data_source_id: ds.id }));
return Promise.all(promises).then(() => this.props.controller.update());
});
};
render() {
const { controller } = this.props;
return (
<div data-test="Group">
<GroupName className="d-block m-t-0 m-b-15" group={this.group} onChange={() => this.forceUpdate()} />
<Layout>
<Layout.Sidebar>
<Sidebar
controller={controller}
group={this.group}
items={this.sidebarMenu}
canAddDataSources={currentUser.isAdmin}
onAddDataSourcesClick={this.addDataSources}
onGroupDeleted={() => navigateTo("groups")}
/>
</Layout.Sidebar>
<Layout.Content>
{!controller.isLoaded && <LoadingState className="" />}
{controller.isLoaded && controller.isEmpty && (
<div className="text-center">
<p>There are no data sources in this group yet.</p>
{currentUser.isAdmin && (
<Button type="primary" onClick={this.addDataSources}>
<i className="fa fa-plus m-r-5" aria-hidden="true" />
Add Data Sources
</Button>
)}
</div>
)}
{controller.isLoaded && !controller.isEmpty && (
<div className="table-responsive">
<ItemsTable
items={controller.pageItems}
columns={this.listColumns}
showHeader={false}
context={this.actions}
orderByField={controller.orderByField}
orderByReverse={controller.orderByReverse}
toggleSorting={controller.toggleSorting}
/>
<Paginator
showPageSizeSelect
totalCount={controller.totalItemsCount}
pageSize={controller.itemsPerPage}
onPageSizeChange={itemsPerPage => controller.updatePagination({ itemsPerPage })}
page={controller.page}
onChange={page => controller.updatePagination({ page })}
/>
</div>
)}
</Layout.Content>
</Layout>
</div>
);
}
}
const GroupDataSourcesPage = wrapSettingsTab(
"Groups.DataSources",
null,
itemsList(
GroupDataSources,
() =>
new ResourceItemsSource({
isPlainList: true,
getRequest(unused, { params: { groupId } }) {
return { id: groupId };
},
getResource() {
return Group.dataSources.bind(Group);
},
}),
() => new StateStorage({ orderByField: "name" })
)
);
routes.register(
"Groups.DataSources",
routeWithUserSession({
path: "/groups/:groupId/data_sources",
title: "Group Data Sources",
render: pageProps => <GroupDataSourcesPage {...pageProps} currentPage="datasources" />,
})
);