|
1 | 1 | import { toUpper } from "lodash";
|
2 |
| -import React from "react"; |
| 2 | +import React, { useEffect, useRef } from "react"; |
| 3 | +import cx from "classnames"; |
3 | 4 | import routeWithUserSession from "@/components/ApplicationArea/routeWithUserSession";
|
4 | 5 | import Link from "@/components/Link";
|
5 | 6 | import PageHeader from "@/components/PageHeader";
|
6 | 7 | import Paginator from "@/components/Paginator";
|
7 | 8 | import EmptyState, { EmptyStateHelpMessage } from "@/components/empty-state/EmptyState";
|
8 | 9 | import { wrap as itemsList, ControllerType } from "@/components/items-list/ItemsList";
|
| 10 | +import useItemsListExtraActions from "@/components/items-list/hooks/useItemsListExtraActions"; |
9 | 11 | import { ResourceItemsSource } from "@/components/items-list/classes/ItemsSource";
|
| 12 | +import { UrlStateStorage } from "@/components/items-list/classes/StateStorage"; |
10 | 13 | import { StateStorage } from "@/components/items-list/classes/StateStorage";
|
11 | 14 | import DynamicComponent from "@/components/DynamicComponent";
|
12 | 15 |
|
| 16 | +import * as Sidebar from "@/components/items-list/components/Sidebar"; |
13 | 17 | import ItemsTable, { Columns } from "@/components/items-list/components/ItemsTable";
|
14 | 18 |
|
| 19 | +import Layout from "@/components/layouts/ContentWithSidebar"; |
| 20 | + |
15 | 21 | import Alert from "@/services/alert";
|
16 | 22 | import { currentUser } from "@/services/auth";
|
| 23 | +import location from "@/services/location"; |
17 | 24 | import routes from "@/services/routes";
|
18 | 25 |
|
| 26 | +import AlertsListEmptyState from "./AlertsListEmptyState"; |
| 27 | + |
| 28 | +import "./alerts-list.css"; |
| 29 | + |
19 | 30 | export const STATE_CLASS = {
|
20 | 31 | unknown: "label-warning",
|
21 | 32 | ok: "label-success",
|
22 |
| - triggered: "label-danger", |
| 33 | + triggered: "label-danger" |
23 | 34 | };
|
24 | 35 |
|
25 |
| -class AlertsList extends React.Component { |
26 |
| - static propTypes = { |
27 |
| - controller: ControllerType.isRequired, |
28 |
| - }; |
29 |
| - |
30 |
| - listColumns = [ |
31 |
| - Columns.custom.sortable( |
32 |
| - (text, alert) => <i className={`fa fa-bell-${alert.options.muted ? "slash" : "o"} p-r-0`} />, |
33 |
| - { |
34 |
| - title: <i className="fa fa-bell p-r-0" />, |
35 |
| - field: "muted", |
36 |
| - width: "1%", |
37 |
| - } |
| 36 | +const listColumns = [ |
| 37 | + Columns.custom.sortable( |
| 38 | + (text, alert) => <i className={`fa fa-bell-${alert.options.muted ? "slash" : "o"} p-r-0`} />, |
| 39 | + { |
| 40 | + title: <i className="fa fa-bell p-r-0" />, |
| 41 | + field: "muted", |
| 42 | + width: "1%", |
| 43 | + } |
| 44 | + ), |
| 45 | + Columns.custom.sortable( |
| 46 | + (text, alert) => ( |
| 47 | + <div> |
| 48 | + <Link className="table-main-title" href={"alerts/" + alert.id}> |
| 49 | + {alert.name} |
| 50 | + </Link> |
| 51 | + </div> |
38 | 52 | ),
|
39 |
| - Columns.custom.sortable( |
40 |
| - (text, alert) => ( |
41 |
| - <div> |
42 |
| - <Link className="table-main-title" href={"alerts/" + alert.id}> |
43 |
| - {alert.name} |
44 |
| - </Link> |
45 |
| - </div> |
46 |
| - ), |
47 |
| - { |
48 |
| - title: "Name", |
49 |
| - field: "name", |
50 |
| - } |
| 53 | + { |
| 54 | + title: "Name", |
| 55 | + field: "name", |
| 56 | + } |
| 57 | + ), |
| 58 | + Columns.custom((text, item) => item.user.name, { title: "Created By", width: "1%" }), |
| 59 | + Columns.custom.sortable( |
| 60 | + (text, alert) => ( |
| 61 | + <div> |
| 62 | + <span className={`label ${STATE_CLASS[alert.state]}`}>{toUpper(alert.state)}</span> |
| 63 | + </div> |
51 | 64 | ),
|
52 |
| - Columns.custom((text, item) => item.user.name, { title: "Created By", width: "1%" }), |
53 |
| - Columns.custom.sortable( |
54 |
| - (text, alert) => ( |
55 |
| - <div> |
56 |
| - <span className={`label ${STATE_CLASS[alert.state]}`}>{toUpper(alert.state)}</span> |
57 |
| - </div> |
58 |
| - ), |
59 |
| - { |
60 |
| - title: "State", |
61 |
| - field: "state", |
62 |
| - width: "1%", |
63 |
| - className: "text-nowrap", |
| 65 | + { |
| 66 | + title: "State", |
| 67 | + field: "state", |
| 68 | + width: "1%", |
| 69 | + className: "text-nowrap", |
| 70 | + } |
| 71 | + ), |
| 72 | + Columns.timeAgo.sortable({ title: "Last Updated At", field: "updated_at", width: "1%" }), |
| 73 | + Columns.dateTime.sortable({ title: "Created At", field: "created_at", width: "1%" }), |
| 74 | +]; |
| 75 | + |
| 76 | +function AlertsListExtraActions(props) { |
| 77 | + return <DynamicComponent name="AlertsList.Actions" {...props} />; |
| 78 | +} |
| 79 | + |
| 80 | +function AlertsList({ controller }) { |
| 81 | + const controllerRef = useRef(); |
| 82 | + controllerRef.current = controller; |
| 83 | + |
| 84 | + useEffect(() => { |
| 85 | + const unlistenLocationChanges = location.listen((unused, action) => { |
| 86 | + const searchTerm = location.search.q || ""; |
| 87 | + if (action === "PUSH" && searchTerm !== controllerRef.current.searchTerm) { |
| 88 | + controllerRef.current.updateSearch(searchTerm); |
64 | 89 | }
|
65 |
| - ), |
66 |
| - Columns.timeAgo.sortable({ title: "Last Updated At", field: "updated_at", width: "1%" }), |
67 |
| - Columns.dateTime.sortable({ title: "Created At", field: "created_at", width: "1%" }), |
68 |
| - ]; |
69 |
| - |
70 |
| - render() { |
71 |
| - const { controller } = this.props; |
72 |
| - |
73 |
| - return ( |
74 |
| - <div className="page-alerts-list"> |
75 |
| - <div className="container"> |
76 |
| - <PageHeader |
77 |
| - title={controller.params.pageTitle} |
78 |
| - actions={ |
79 |
| - currentUser.hasPermission("list_alerts") ? ( |
80 |
| - <Link.Button block type="primary" href="alerts/new"> |
81 |
| - <i className="fa fa-plus m-r-5" /> |
82 |
| - New Alert |
83 |
| - </Link.Button> |
84 |
| - ) : null |
85 |
| - } |
86 |
| - /> |
87 |
| - <div> |
| 90 | + }); |
| 91 | + |
| 92 | + return () => { |
| 93 | + unlistenLocationChanges(); |
| 94 | + }; |
| 95 | + }, []); |
| 96 | + |
| 97 | + const { |
| 98 | + areExtraActionsAvailable, |
| 99 | + listColumns: tableColumns, |
| 100 | + Component: ExtraActionsComponent, |
| 101 | + selectedItems, |
| 102 | + } = useItemsListExtraActions(controller, listColumns, AlertsListExtraActions); |
| 103 | + |
| 104 | + return ( |
| 105 | + <div className="page-alerts-list"> |
| 106 | + <div className="container"> |
| 107 | + <PageHeader |
| 108 | + title={controller.params.pageTitle} |
| 109 | + actions={ |
| 110 | + currentUser.hasPermission("list_alerts") ? ( |
| 111 | + <Link.Button block type="primary" href="alerts/new"> |
| 112 | + <i className="fa fa-plus m-r-5" /> |
| 113 | + New Alert |
| 114 | + </Link.Button> |
| 115 | + ) : null |
| 116 | + } |
| 117 | + /> |
| 118 | + <Layout> |
| 119 | + <Layout.Sidebar className="m-b-0"> |
| 120 | + <Sidebar.SearchInput |
| 121 | + placeholder="Search Alert..." |
| 122 | + value={controller.searchTerm} |
| 123 | + onChange={controller.updateSearch} |
| 124 | + /> |
| 125 | + </Layout.Sidebar> |
| 126 | + <Layout.Content> |
88 | 127 | {controller.isLoaded && controller.isEmpty ? (
|
89 |
| - <DynamicComponent name="AlertsList.EmptyState"> |
90 |
| - <EmptyState |
91 |
| - icon="fa fa-bell-o" |
92 |
| - illustration="alert" |
93 |
| - description="Get notified on certain events" |
94 |
| - helpMessage={<EmptyStateHelpMessage helpTriggerType="ALERTS" />} |
95 |
| - showAlertStep |
96 |
| - /> |
97 |
| - </DynamicComponent> |
| 128 | + <AlertsListEmptyState |
| 129 | + page={controller.params.currentPage} |
| 130 | + searchTerm={controller.searchTerm} |
| 131 | + /> |
98 | 132 | ) : (
|
99 |
| - <div className="table-responsive bg-white tiled"> |
100 |
| - <ItemsTable |
101 |
| - loading={!controller.isLoaded} |
102 |
| - items={controller.pageItems} |
103 |
| - columns={this.listColumns} |
104 |
| - orderByField={controller.orderByField} |
105 |
| - orderByReverse={controller.orderByReverse} |
106 |
| - toggleSorting={controller.toggleSorting} |
107 |
| - /> |
108 |
| - <Paginator |
109 |
| - showPageSizeSelect |
110 |
| - totalCount={controller.totalItemsCount} |
111 |
| - pageSize={controller.itemsPerPage} |
112 |
| - onPageSizeChange={itemsPerPage => controller.updatePagination({ itemsPerPage })} |
113 |
| - page={controller.page} |
114 |
| - onChange={page => controller.updatePagination({ page })} |
115 |
| - /> |
116 |
| - </div> |
| 133 | + <React.Fragment> |
| 134 | + <div className={cx({ "m-b-10": areExtraActionsAvailable })}> |
| 135 | + <ExtraActionsComponent selectedItems={selectedItems} /> |
| 136 | + </div> |
| 137 | + <div className="bg-white tiled table-responsive"> |
| 138 | + <ItemsTable |
| 139 | + items={controller.pageItems} |
| 140 | + loading={!controller.isLoaded} |
| 141 | + columns={tableColumns} |
| 142 | + orderByField={controller.orderByField} |
| 143 | + orderByReverse={controller.orderByReverse} |
| 144 | + toggleSorting={controller.toggleSorting} |
| 145 | + /> |
| 146 | + <Paginator |
| 147 | + showPageSizeSelect |
| 148 | + totalCount={controller.totalItemsCount} |
| 149 | + pageSize={controller.itemsPerPage} |
| 150 | + onPageSizeChange={itemsPerPage => controller.updatePagination({ itemsPerPage })} |
| 151 | + page={controller.page} |
| 152 | + onChange={page => controller.updatePagination({ page })} |
| 153 | + /> |
| 154 | + </div> |
| 155 | + </React.Fragment> |
117 | 156 | )}
|
118 |
| - </div> |
119 |
| - </div> |
| 157 | + </Layout.Content> |
| 158 | + </Layout> |
120 | 159 | </div>
|
121 |
| - ); |
122 |
| - } |
| 160 | + </div> |
| 161 | + ); |
123 | 162 | }
|
124 | 163 |
|
| 164 | +AlertsList.propTypes = { |
| 165 | + controller: ControllerType.isRequired, |
| 166 | +}; |
| 167 | + |
125 | 168 | const AlertsListPage = itemsList(
|
126 | 169 | AlertsList,
|
127 | 170 | () =>
|
128 | 171 | new ResourceItemsSource({
|
129 |
| - isPlainList: true, |
130 |
| - getRequest() { |
131 |
| - return {}; |
132 |
| - }, |
133 |
| - getResource() { |
134 |
| - return Alert.query.bind(Alert); |
135 |
| - }, |
| 172 | + getResource({ params: { currentPage } }) { |
| 173 | + return { |
| 174 | + all: Alert.query.bind(Alert) |
| 175 | + }[currentPage]; |
| 176 | + } |
136 | 177 | }),
|
137 |
| - () => new StateStorage({ orderByField: "created_at", orderByReverse: true, itemsPerPage: 20 }) |
| 178 | + () => new UrlStateStorage({ orderByField: "created_at", orderByReverse: true }) |
138 | 179 | );
|
139 | 180 |
|
140 | 181 | routes.register(
|
141 | 182 | "Alerts.List",
|
142 | 183 | routeWithUserSession({
|
143 | 184 | path: "/alerts",
|
144 | 185 | title: "Alerts",
|
145 |
| - render: pageProps => <AlertsListPage {...pageProps} currentPage="alerts" />, |
| 186 | + render: pageProps => <AlertsListPage {...pageProps} currentPage="all" />, |
146 | 187 | })
|
147 | 188 | );
|
0 commit comments