mirror of
https://github.com/getredash/redash.git
synced 2025-12-19 17:37:19 -05:00
* Make favorite queries/dashboard order by starred at(favorited at) * fix styling for restyled error
252 lines
8.0 KiB
JavaScript
252 lines
8.0 KiB
JavaScript
import React, { useCallback, useEffect, useRef } from "react";
|
|
import cx from "classnames";
|
|
|
|
import routeWithUserSession from "@/components/ApplicationArea/routeWithUserSession";
|
|
import Link from "@/components/Link";
|
|
import PageHeader from "@/components/PageHeader";
|
|
import Paginator from "@/components/Paginator";
|
|
import DynamicComponent from "@/components/DynamicComponent";
|
|
import { QueryTagsControl } from "@/components/tags-control/TagsControl";
|
|
import SchedulePhrase from "@/components/queries/SchedulePhrase";
|
|
|
|
import { wrap as itemsList, ControllerType } from "@/components/items-list/ItemsList";
|
|
import useItemsListExtraActions from "@/components/items-list/hooks/useItemsListExtraActions";
|
|
import { ResourceItemsSource } from "@/components/items-list/classes/ItemsSource";
|
|
import { UrlStateStorage } from "@/components/items-list/classes/StateStorage";
|
|
|
|
import * as Sidebar from "@/components/items-list/components/Sidebar";
|
|
import ItemsTable, { Columns } from "@/components/items-list/components/ItemsTable";
|
|
|
|
import Layout from "@/components/layouts/ContentWithSidebar";
|
|
|
|
import { Query } from "@/services/query";
|
|
import { clientConfig, currentUser } from "@/services/auth";
|
|
import location from "@/services/location";
|
|
import routes from "@/services/routes";
|
|
|
|
import QueriesListEmptyState from "./QueriesListEmptyState";
|
|
|
|
import "./queries-list.css";
|
|
|
|
const sidebarMenu = [
|
|
{
|
|
key: "all",
|
|
href: "queries",
|
|
title: "All Queries",
|
|
icon: () => <Sidebar.MenuIcon icon="fa fa-code" />,
|
|
},
|
|
{
|
|
key: "my",
|
|
href: "queries/my",
|
|
title: "My Queries",
|
|
icon: () => <Sidebar.ProfileImage user={currentUser} />,
|
|
},
|
|
{
|
|
key: "favorites",
|
|
href: "queries/favorites",
|
|
title: "Favorites",
|
|
icon: () => <Sidebar.MenuIcon icon="fa fa-star" />,
|
|
},
|
|
{
|
|
key: "archive",
|
|
href: "queries/archive",
|
|
title: "Archived",
|
|
icon: () => <Sidebar.MenuIcon icon="fa fa-archive" />,
|
|
},
|
|
];
|
|
|
|
const listColumns = [
|
|
Columns.favorites({ className: "p-r-0" }),
|
|
Columns.custom.sortable(
|
|
(text, item) => (
|
|
<React.Fragment>
|
|
<Link className="table-main-title" href={"queries/" + item.id}>
|
|
{item.name}
|
|
</Link>
|
|
<QueryTagsControl className="d-block" tags={item.tags} isDraft={item.is_draft} isArchived={item.is_archived} />
|
|
</React.Fragment>
|
|
),
|
|
{
|
|
title: "Name",
|
|
field: "name",
|
|
width: null,
|
|
}
|
|
),
|
|
Columns.custom((text, item) => item.user.name, { title: "Created By", width: "1%" }),
|
|
Columns.dateTime.sortable({ title: "Created At", field: "created_at", width: "1%" }),
|
|
Columns.dateTime.sortable({
|
|
title: "Last Executed At",
|
|
field: "retrieved_at",
|
|
orderByField: "executed_at",
|
|
width: "1%",
|
|
}),
|
|
Columns.custom.sortable((text, item) => <SchedulePhrase schedule={item.schedule} isNew={item.isNew()} />, {
|
|
title: "Refresh Schedule",
|
|
field: "schedule",
|
|
width: "1%",
|
|
}),
|
|
];
|
|
|
|
function QueriesListExtraActions(props) {
|
|
return <DynamicComponent name="QueriesList.Actions" {...props} />;
|
|
}
|
|
|
|
function QueriesList({ controller }) {
|
|
const controllerRef = useRef();
|
|
controllerRef.current = controller;
|
|
|
|
const updateSearch = useCallback(
|
|
(searchTemm) => {
|
|
controller.updateSearch(searchTemm, { isServerSideFTS: !clientConfig.multiByteSearchEnabled });
|
|
},
|
|
[controller]
|
|
);
|
|
|
|
useEffect(() => {
|
|
const unlistenLocationChanges = location.listen((unused, action) => {
|
|
const searchTerm = location.search.q || "";
|
|
if (action === "PUSH" && searchTerm !== controllerRef.current.searchTerm) {
|
|
updateSearch(searchTerm);
|
|
}
|
|
});
|
|
|
|
return () => {
|
|
unlistenLocationChanges();
|
|
};
|
|
}, [updateSearch]);
|
|
|
|
let usedListColumns = listColumns;
|
|
if (controller.params.currentPage === "favorites") {
|
|
usedListColumns = [
|
|
...usedListColumns,
|
|
Columns.dateTime.sortable({ title: "Starred At", field: "starred_at", width: "1%" }),
|
|
];
|
|
}
|
|
const {
|
|
areExtraActionsAvailable,
|
|
listColumns: tableColumns,
|
|
Component: ExtraActionsComponent,
|
|
selectedItems,
|
|
} = useItemsListExtraActions(controller, usedListColumns, QueriesListExtraActions);
|
|
|
|
return (
|
|
<div className="page-queries-list">
|
|
<div className="container">
|
|
<PageHeader
|
|
title={controller.params.pageTitle}
|
|
actions={
|
|
currentUser.hasPermission("create_query") ? (
|
|
<Link.Button block type="primary" href="queries/new">
|
|
<i className="fa fa-plus m-r-5" aria-hidden="true" />
|
|
New Query
|
|
</Link.Button>
|
|
) : null
|
|
}
|
|
/>
|
|
<Layout>
|
|
<Layout.Sidebar className="m-b-0">
|
|
<Sidebar.SearchInput
|
|
placeholder="Search Queries..."
|
|
label="Search queries"
|
|
value={controller.searchTerm}
|
|
onChange={updateSearch}
|
|
/>
|
|
<Sidebar.Menu items={sidebarMenu} selected={controller.params.currentPage} />
|
|
<Sidebar.Tags url="api/queries/tags" onChange={controller.updateSelectedTags} showUnselectAll />
|
|
</Layout.Sidebar>
|
|
<Layout.Content>
|
|
{controller.isLoaded && controller.isEmpty ? (
|
|
<QueriesListEmptyState
|
|
page={controller.params.currentPage}
|
|
searchTerm={controller.searchTerm}
|
|
selectedTags={controller.selectedTags}
|
|
/>
|
|
) : (
|
|
<React.Fragment>
|
|
<div className={cx({ "m-b-10": areExtraActionsAvailable })}>
|
|
<ExtraActionsComponent selectedItems={selectedItems} />
|
|
</div>
|
|
<div className="bg-white tiled table-responsive">
|
|
<ItemsTable
|
|
items={controller.pageItems}
|
|
loading={!controller.isLoaded}
|
|
columns={tableColumns}
|
|
orderByField={controller.orderByField}
|
|
orderByReverse={controller.orderByReverse}
|
|
toggleSorting={controller.toggleSorting}
|
|
setSorting={controller.setSorting}
|
|
/>
|
|
<Paginator
|
|
showPageSizeSelect
|
|
totalCount={controller.totalItemsCount}
|
|
pageSize={controller.itemsPerPage}
|
|
onPageSizeChange={(itemsPerPage) => controller.updatePagination({ itemsPerPage })}
|
|
page={controller.page}
|
|
onChange={(page) => controller.updatePagination({ page })}
|
|
/>
|
|
</div>
|
|
</React.Fragment>
|
|
)}
|
|
</Layout.Content>
|
|
</Layout>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
QueriesList.propTypes = {
|
|
controller: ControllerType.isRequired,
|
|
};
|
|
|
|
const QueriesListPage = itemsList(
|
|
QueriesList,
|
|
() =>
|
|
new ResourceItemsSource({
|
|
getResource({ params: { currentPage } }) {
|
|
return {
|
|
all: Query.query.bind(Query),
|
|
my: Query.myQueries.bind(Query),
|
|
favorites: Query.favorites.bind(Query),
|
|
archive: Query.archive.bind(Query),
|
|
}[currentPage];
|
|
},
|
|
getItemProcessor() {
|
|
return (item) => new Query(item);
|
|
},
|
|
}),
|
|
({ ...props }) => new UrlStateStorage({ orderByField: props.orderByField ?? "created_at", orderByReverse: true })
|
|
);
|
|
|
|
routes.register(
|
|
"Queries.List",
|
|
routeWithUserSession({
|
|
path: "/queries",
|
|
title: "Queries",
|
|
render: (pageProps) => <QueriesListPage {...pageProps} currentPage="all" />,
|
|
})
|
|
);
|
|
routes.register(
|
|
"Queries.Favorites",
|
|
routeWithUserSession({
|
|
path: "/queries/favorites",
|
|
title: "Favorite Queries",
|
|
render: (pageProps) => <QueriesListPage {...pageProps} currentPage="favorites" orderByField="starred_at" />,
|
|
})
|
|
);
|
|
routes.register(
|
|
"Queries.Archived",
|
|
routeWithUserSession({
|
|
path: "/queries/archive",
|
|
title: "Archived Queries",
|
|
render: (pageProps) => <QueriesListPage {...pageProps} currentPage="archive" />,
|
|
})
|
|
);
|
|
routes.register(
|
|
"Queries.My",
|
|
routeWithUserSession({
|
|
path: "/queries/my",
|
|
title: "My Queries",
|
|
render: (pageProps) => <QueriesListPage {...pageProps} currentPage="my" />,
|
|
})
|
|
);
|