mirror of
https://github.com/getredash/redash.git
synced 2025-12-19 17:37:19 -05:00
233 lines
7.3 KiB
JavaScript
233 lines
7.3 KiB
JavaScript
import { extend, map, filter, reduce } from "lodash";
|
|
import React, { useMemo } from "react";
|
|
import PropTypes from "prop-types";
|
|
import Button from "antd/lib/button";
|
|
import Dropdown from "antd/lib/dropdown";
|
|
import Menu from "antd/lib/menu";
|
|
import EllipsisOutlinedIcon from "@ant-design/icons/EllipsisOutlined";
|
|
import useMedia from "use-media";
|
|
import EditInPlace from "@/components/EditInPlace";
|
|
import FavoritesControl from "@/components/FavoritesControl";
|
|
import { QueryTagsControl } from "@/components/tags-control/TagsControl";
|
|
import getTags from "@/services/getTags";
|
|
import { clientConfig } from "@/services/auth";
|
|
import useQueryFlags from "../hooks/useQueryFlags";
|
|
import useArchiveQuery from "../hooks/useArchiveQuery";
|
|
import usePublishQuery from "../hooks/usePublishQuery";
|
|
import useUnpublishQuery from "../hooks/useUnpublishQuery";
|
|
import useUpdateQueryTags from "../hooks/useUpdateQueryTags";
|
|
import useRenameQuery from "../hooks/useRenameQuery";
|
|
import useDuplicateQuery from "../hooks/useDuplicateQuery";
|
|
import useApiKeyDialog from "../hooks/useApiKeyDialog";
|
|
import usePermissionsEditorDialog from "../hooks/usePermissionsEditorDialog";
|
|
|
|
import "./QueryPageHeader.less";
|
|
|
|
function getQueryTags() {
|
|
return getTags("api/queries/tags").then(tags => map(tags, t => t.name));
|
|
}
|
|
|
|
function createMenu(menu) {
|
|
const handlers = {};
|
|
|
|
const groups = map(menu, group =>
|
|
filter(
|
|
map(group, (props, key) => {
|
|
props = extend({ isAvailable: true, isEnabled: true, onClick: () => {} }, props);
|
|
if (props.isAvailable) {
|
|
handlers[key] = props.onClick;
|
|
return (
|
|
<Menu.Item key={key} disabled={!props.isEnabled}>
|
|
{props.title}
|
|
</Menu.Item>
|
|
);
|
|
}
|
|
return null;
|
|
})
|
|
)
|
|
);
|
|
|
|
return (
|
|
<Menu onClick={({ key }) => handlers[key]()}>
|
|
{reduce(
|
|
filter(groups, group => group.length > 0),
|
|
(result, items, key) => {
|
|
const divider = result.length > 0 ? <Menu.Divider key={`divider${key}`} /> : null;
|
|
return [...result, divider, ...items];
|
|
},
|
|
[]
|
|
)}
|
|
</Menu>
|
|
);
|
|
}
|
|
|
|
export default function QueryPageHeader({
|
|
query,
|
|
dataSource,
|
|
sourceMode,
|
|
selectedVisualization,
|
|
headerExtra,
|
|
tagsExtra,
|
|
onChange,
|
|
}) {
|
|
const isDesktop = useMedia({ minWidth: 768 });
|
|
const queryFlags = useQueryFlags(query, dataSource);
|
|
const updateName = useRenameQuery(query, onChange);
|
|
const updateTags = useUpdateQueryTags(query, onChange);
|
|
const archiveQuery = useArchiveQuery(query, onChange);
|
|
const publishQuery = usePublishQuery(query, onChange);
|
|
const unpublishQuery = useUnpublishQuery(query, onChange);
|
|
const [isDuplicating, duplicateQuery] = useDuplicateQuery(query);
|
|
const openApiKeyDialog = useApiKeyDialog(query, onChange);
|
|
const openPermissionsEditorDialog = usePermissionsEditorDialog(query);
|
|
|
|
const moreActionsMenu = useMemo(
|
|
() =>
|
|
createMenu([
|
|
{
|
|
fork: {
|
|
isEnabled: !queryFlags.isNew && queryFlags.canFork && !isDuplicating,
|
|
title: (
|
|
<React.Fragment>
|
|
Fork
|
|
<i className="fa fa-external-link m-l-5" />
|
|
</React.Fragment>
|
|
),
|
|
onClick: duplicateQuery,
|
|
},
|
|
},
|
|
{
|
|
archive: {
|
|
isAvailable: !queryFlags.isNew && queryFlags.canEdit && !queryFlags.isArchived,
|
|
title: "Archive",
|
|
onClick: archiveQuery,
|
|
},
|
|
managePermissions: {
|
|
isAvailable:
|
|
!queryFlags.isNew && queryFlags.canEdit && !queryFlags.isArchived && clientConfig.showPermissionsControl,
|
|
title: "Manage Permissions",
|
|
onClick: openPermissionsEditorDialog,
|
|
},
|
|
publish: {
|
|
isAvailable:
|
|
!isDesktop && queryFlags.isDraft && !queryFlags.isArchived && !queryFlags.isNew && queryFlags.canEdit,
|
|
title: "Publish",
|
|
onClick: publishQuery,
|
|
},
|
|
unpublish: {
|
|
isAvailable: !queryFlags.isNew && queryFlags.canEdit && !queryFlags.isDraft,
|
|
title: "Unpublish",
|
|
onClick: unpublishQuery,
|
|
},
|
|
},
|
|
{
|
|
showAPIKey: {
|
|
isAvailable: !queryFlags.isNew,
|
|
title: "Show API Key",
|
|
onClick: openApiKeyDialog,
|
|
},
|
|
},
|
|
]),
|
|
[
|
|
queryFlags.isNew,
|
|
queryFlags.canFork,
|
|
queryFlags.canEdit,
|
|
queryFlags.isArchived,
|
|
queryFlags.isDraft,
|
|
isDuplicating,
|
|
duplicateQuery,
|
|
archiveQuery,
|
|
openPermissionsEditorDialog,
|
|
isDesktop,
|
|
publishQuery,
|
|
unpublishQuery,
|
|
openApiKeyDialog,
|
|
]
|
|
);
|
|
|
|
return (
|
|
<div className="query-page-header">
|
|
<div className="title-with-tags">
|
|
<div className="page-title">
|
|
<div className="d-flex align-items-center">
|
|
{!queryFlags.isNew && <FavoritesControl item={query} />}
|
|
<h3>
|
|
<EditInPlace isEditable={queryFlags.canEdit} onDone={updateName} ignoreBlanks value={query.name} />
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
<div className="query-tags">
|
|
<QueryTagsControl
|
|
tags={query.tags}
|
|
isDraft={queryFlags.isDraft}
|
|
isArchived={queryFlags.isArchived}
|
|
canEdit={queryFlags.canEdit}
|
|
getAvailableTags={getQueryTags}
|
|
onEdit={updateTags}
|
|
tagsExtra={tagsExtra}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="header-actions">
|
|
{headerExtra}
|
|
{isDesktop && queryFlags.isDraft && !queryFlags.isArchived && !queryFlags.isNew && queryFlags.canEdit && (
|
|
<Button className="m-r-5" onClick={publishQuery}>
|
|
<i className="fa fa-paper-plane m-r-5" /> Publish
|
|
</Button>
|
|
)}
|
|
|
|
{!queryFlags.isNew && queryFlags.canViewSource && (
|
|
<span>
|
|
{!sourceMode && (
|
|
<Button className="m-r-5" href={query.getUrl(true, selectedVisualization)}>
|
|
<i className="fa fa-pencil-square-o" aria-hidden="true" />
|
|
<span className="m-l-5">Edit Source</span>
|
|
</Button>
|
|
)}
|
|
{sourceMode && (
|
|
<Button
|
|
className="m-r-5"
|
|
href={query.getUrl(false, selectedVisualization)}
|
|
data-test="QueryPageShowDataOnly">
|
|
<i className="fa fa-table" aria-hidden="true" />
|
|
<span className="m-l-5">Show Data Only</span>
|
|
</Button>
|
|
)}
|
|
</span>
|
|
)}
|
|
|
|
{!queryFlags.isNew && (
|
|
<Dropdown overlay={moreActionsMenu} trigger={["click"]}>
|
|
<Button>
|
|
<EllipsisOutlinedIcon rotate={90} />
|
|
</Button>
|
|
</Dropdown>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
QueryPageHeader.propTypes = {
|
|
query: PropTypes.shape({
|
|
id: PropTypes.number,
|
|
name: PropTypes.string,
|
|
tags: PropTypes.arrayOf(PropTypes.string),
|
|
}).isRequired,
|
|
dataSource: PropTypes.object,
|
|
sourceMode: PropTypes.bool,
|
|
selectedVisualization: PropTypes.number,
|
|
headerExtra: PropTypes.node,
|
|
tagsExtra: PropTypes.node,
|
|
onChange: PropTypes.func,
|
|
};
|
|
|
|
QueryPageHeader.defaultProps = {
|
|
dataSource: null,
|
|
sourceMode: false,
|
|
selectedVisualization: null,
|
|
headerExtra: null,
|
|
tagsExtra: null,
|
|
onChange: () => {},
|
|
};
|