mirror of
https://github.com/valitydev/redash.git
synced 2024-11-07 09:28:51 +00:00
Migrate Home to React (#4379)
This commit is contained in:
parent
e72d7a8cca
commit
0563ecf648
@ -85,7 +85,7 @@ strong {
|
|||||||
|
|
||||||
// Fixed width layout for specific pages
|
// Fixed width layout for specific pages
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
|
.settings-screen, .home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
|
||||||
.container {
|
.container {
|
||||||
width: 750px;
|
width: 750px;
|
||||||
}
|
}
|
||||||
@ -93,7 +93,7 @@ strong {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 992px) {
|
||||||
.settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
|
.settings-screen, .home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
|
||||||
.container {
|
.container {
|
||||||
width: 970px;
|
width: 970px;
|
||||||
}
|
}
|
||||||
@ -101,7 +101,7 @@ strong {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
.settings-screen, home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
|
.settings-screen, .home-page, page-dashboard-list, page-queries-list, page-alerts-list, alert-page, queries-search-results-page, .fixed-container {
|
||||||
.container {
|
.container {
|
||||||
width: 1170px;
|
width: 1170px;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { react2angular } from 'react2angular';
|
|
||||||
import Card from 'antd/lib/card';
|
import Card from 'antd/lib/card';
|
||||||
import Button from 'antd/lib/button';
|
import Button from 'antd/lib/button';
|
||||||
import Typography from 'antd/lib/typography';
|
import Typography from 'antd/lib/typography';
|
||||||
@ -10,7 +9,7 @@ import OrgSettings from '@/services/organizationSettings';
|
|||||||
|
|
||||||
const Text = Typography.Text;
|
const Text = Typography.Text;
|
||||||
|
|
||||||
export function BeaconConsent() {
|
function BeaconConsent() {
|
||||||
const [hide, setHide] = useState(false);
|
const [hide, setHide] = useState(false);
|
||||||
|
|
||||||
if (!clientConfig.showBeaconConsentMessage || hide) {
|
if (!clientConfig.showBeaconConsentMessage || hide) {
|
||||||
@ -76,8 +75,4 @@ export function BeaconConsent() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function init(ngModule) {
|
export default BeaconConsent;
|
||||||
ngModule.component('beaconConsent', react2angular(BeaconConsent));
|
|
||||||
}
|
|
||||||
|
|
||||||
init.init = true;
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { keys, some } from 'lodash';
|
import { keys, some } from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { react2angular } from 'react2angular';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import CreateDashboardDialog from '@/components/dashboards/CreateDashboardDialog';
|
import CreateDashboardDialog from '@/components/dashboards/CreateDashboardDialog';
|
||||||
import { currentUser } from '@/services/auth';
|
import { currentUser } from '@/services/auth';
|
||||||
@ -38,7 +37,7 @@ Step.defaultProps = {
|
|||||||
onClick: null,
|
onClick: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function EmptyState({
|
function EmptyState({
|
||||||
icon,
|
icon,
|
||||||
header,
|
header,
|
||||||
description,
|
description,
|
||||||
@ -169,8 +168,4 @@ EmptyState.defaultProps = {
|
|||||||
showInviteStep: false,
|
showInviteStep: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function init(ngModule) {
|
export default EmptyState;
|
||||||
ngModule.component('emptyState', react2angular(EmptyState));
|
|
||||||
}
|
|
||||||
|
|
||||||
init.init = true;
|
|
||||||
|
@ -4,7 +4,7 @@ import { react2angular } from 'react2angular';
|
|||||||
import { toUpper } from 'lodash';
|
import { toUpper } from 'lodash';
|
||||||
import { PageHeader } from '@/components/PageHeader';
|
import { PageHeader } from '@/components/PageHeader';
|
||||||
import { Paginator } from '@/components/Paginator';
|
import { Paginator } from '@/components/Paginator';
|
||||||
import { EmptyState } from '@/components/empty-state/EmptyState';
|
import EmptyState from '@/components/empty-state/EmptyState';
|
||||||
|
|
||||||
import { wrap as liveItemsList, ControllerType } from '@/components/items-list/ItemsList';
|
import { wrap as liveItemsList, ControllerType } from '@/components/items-list/ItemsList';
|
||||||
import { ResourceItemsSource } from '@/components/items-list/classes/ItemsSource';
|
import { ResourceItemsSource } from '@/components/items-list/classes/ItemsSource';
|
||||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { BigMessage } from '@/components/BigMessage';
|
import { BigMessage } from '@/components/BigMessage';
|
||||||
import { NoTaggedObjectsFound } from '@/components/NoTaggedObjectsFound';
|
import { NoTaggedObjectsFound } from '@/components/NoTaggedObjectsFound';
|
||||||
import { EmptyState } from '@/components/empty-state/EmptyState';
|
import EmptyState from '@/components/empty-state/EmptyState';
|
||||||
|
|
||||||
export default function DashboardListEmptyState({ page, searchTerm, selectedTags }) {
|
export default function DashboardListEmptyState({ page, searchTerm, selectedTags }) {
|
||||||
if (searchTerm !== '') {
|
if (searchTerm !== '') {
|
||||||
|
184
client/app/pages/home/Home.jsx
Normal file
184
client/app/pages/home/Home.jsx
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { includes, isEmpty } from 'lodash';
|
||||||
|
import { react2angular } from 'react2angular';
|
||||||
|
import Alert from 'antd/lib/alert';
|
||||||
|
import Icon from 'antd/lib/icon';
|
||||||
|
import EmptyState from '@/components/empty-state/EmptyState';
|
||||||
|
import DynamicComponent from '@/components/DynamicComponent';
|
||||||
|
import BeaconConsent from '@/components/BeaconConsent';
|
||||||
|
import recordEvent from '@/services/recordEvent';
|
||||||
|
import { messages } from '@/services/auth';
|
||||||
|
import { $http } from '@/services/ng';
|
||||||
|
import notification from '@/services/notification';
|
||||||
|
import { Dashboard } from '@/services/dashboard';
|
||||||
|
import { Query } from '@/services/query';
|
||||||
|
|
||||||
|
function DeprecatedEmbedFeatureAlert() {
|
||||||
|
return (
|
||||||
|
<Alert
|
||||||
|
className="m-b-15"
|
||||||
|
type="warning"
|
||||||
|
message={(
|
||||||
|
<>
|
||||||
|
You have enabled <code>ALLOW_PARAMETERS_IN_EMBEDS</code>. This setting is
|
||||||
|
now deprecated and should be turned off. Parameters in embeds are supported
|
||||||
|
by default.{' '}
|
||||||
|
<a
|
||||||
|
href="https://discuss.redash.io/t/support-for-parameters-in-embedded-visualizations/3337"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Read more
|
||||||
|
</a>.
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function EmailNotVerifiedAlert() {
|
||||||
|
const verifyEmail = () => {
|
||||||
|
$http.post('verification_email').then(({ data }) => {
|
||||||
|
notification.success(data.message);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Alert
|
||||||
|
className="m-b-15"
|
||||||
|
type="warning"
|
||||||
|
message={(
|
||||||
|
<>
|
||||||
|
We have sent an email with a confirmation link to your email address. Please
|
||||||
|
follow the link to verify your email address.{' '}
|
||||||
|
<a className="clickable" onClick={verifyEmail}>Resend email</a>.
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FavoriteList({ title, resource, itemUrl, emptyState }) {
|
||||||
|
const [items, setItems] = useState([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(true);
|
||||||
|
resource.favorites().$promise
|
||||||
|
.then(({ results }) => setItems(results))
|
||||||
|
.finally(() => setLoading(false));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="d-flex align-items-center m-b-20">
|
||||||
|
<p className="flex-fill f-500 c-black m-0">{title}</p>
|
||||||
|
{loading && <Icon type="loading" />}
|
||||||
|
</div>
|
||||||
|
{!isEmpty(items) && (
|
||||||
|
<div className="list-group">
|
||||||
|
{items.map(item => (
|
||||||
|
<a key={itemUrl(item)} className="list-group-item" href={itemUrl(item)}>
|
||||||
|
<span className="btn-favourite m-r-5">
|
||||||
|
<i className="fa fa-star" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
{item.name}
|
||||||
|
{item.is_draft && <span className="label label-default m-l-5">Unpublished</span>}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{(isEmpty(items) && !loading) && emptyState}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
FavoriteList.propTypes = {
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
resource: PropTypes.func.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||||
|
itemUrl: PropTypes.func.isRequired,
|
||||||
|
emptyState: PropTypes.node,
|
||||||
|
};
|
||||||
|
FavoriteList.defaultProps = { emptyState: null };
|
||||||
|
|
||||||
|
function DashboardAndQueryFavoritesList() {
|
||||||
|
return (
|
||||||
|
<div className="tile">
|
||||||
|
<div className="t-body tb-padding">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<FavoriteList
|
||||||
|
title="Favorite Dashboards"
|
||||||
|
resource={Dashboard}
|
||||||
|
itemUrl={dashboard => `dashboard/${dashboard.slug}`}
|
||||||
|
emptyState={(
|
||||||
|
<p>
|
||||||
|
<span className="btn-favourite m-r-5">
|
||||||
|
<i className="fa fa-star" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
Favorite <a href="dashboards">Dashboards</a> will appear here
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-sm-6">
|
||||||
|
<FavoriteList
|
||||||
|
title="Favorite Queries"
|
||||||
|
resource={Query}
|
||||||
|
itemUrl={query => `queries/${query.id}`}
|
||||||
|
emptyState={(
|
||||||
|
<p>
|
||||||
|
<span className="btn-favourite m-r-5">
|
||||||
|
<i className="fa fa-star" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
Favorite <a href="queries">Queries</a> will appear here
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Home() {
|
||||||
|
useEffect(() => {
|
||||||
|
recordEvent('view', 'page', 'personal_homepage');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="home-page">
|
||||||
|
<div className="container">
|
||||||
|
{includes(messages, 'using-deprecated-embed-feature') && <DeprecatedEmbedFeatureAlert />}
|
||||||
|
{includes(messages, 'email-not-verified') && <EmailNotVerifiedAlert />}
|
||||||
|
<EmptyState
|
||||||
|
header="Welcome to Redash 👋"
|
||||||
|
description="Connect to any data source, easily visualize and share your data"
|
||||||
|
illustration="dashboard"
|
||||||
|
helpLink="https://redash.io/help/user-guide/getting-started"
|
||||||
|
showDashboardStep
|
||||||
|
showInviteStep
|
||||||
|
onboardingMode
|
||||||
|
/>
|
||||||
|
<DynamicComponent name="HomeExtra" />
|
||||||
|
<DashboardAndQueryFavoritesList />
|
||||||
|
<BeaconConsent />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function init(ngModule) {
|
||||||
|
ngModule.component('homePage', react2angular(Home));
|
||||||
|
|
||||||
|
return {
|
||||||
|
'/': {
|
||||||
|
template: '<home-page></home-page>',
|
||||||
|
title: 'Redash',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
init.init = true;
|
@ -1,95 +0,0 @@
|
|||||||
<div class="container">
|
|
||||||
<div
|
|
||||||
ng-if="$ctrl.messages.includes('using-deprecated-embed-feature')"
|
|
||||||
class="alert alert-warning"
|
|
||||||
>
|
|
||||||
You have enabled <code>ALLOW_PARAMETERS_IN_EMBEDS</code>. This setting is
|
|
||||||
now deprecated and should be turned off. Parameters in embeds are supported
|
|
||||||
by default.
|
|
||||||
<a
|
|
||||||
href="https://discuss.redash.io/t/support-for-parameters-in-embedded-visualizations/3337"
|
|
||||||
target="_blank"
|
|
||||||
>Read more</a
|
|
||||||
>.
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
ng-if="$ctrl.messages.includes('email-not-verified')"
|
|
||||||
class="alert alert-warning"
|
|
||||||
>
|
|
||||||
We have sent an email with a confirmation link to your email address. Please
|
|
||||||
follow the link to verify your email address.
|
|
||||||
<a ng-click="$ctrl.verifyEmail()">Resend email</a>.
|
|
||||||
</div>
|
|
||||||
<empty-state
|
|
||||||
header="'Welcome to Redash 👋'"
|
|
||||||
description="'Connect to any data source, easily visualize and share your data'"
|
|
||||||
illustration="'dashboard'"
|
|
||||||
help-link="'https://redash.io/help/user-guide/getting-started'"
|
|
||||||
show-dashboard-step="true"
|
|
||||||
show-invite-step="true"
|
|
||||||
onboarding-mode="true"
|
|
||||||
></empty-state>
|
|
||||||
|
|
||||||
<home-extra></home-extra>
|
|
||||||
|
|
||||||
<div class="tile">
|
|
||||||
<div class="t-body tb-padding">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<p class="f-500 m-b-20 c-black">Favorite Dashboards</p>
|
|
||||||
|
|
||||||
<p ng-if="$ctrl.noDashboards">
|
|
||||||
<span class="btn-favourite">
|
|
||||||
<i class="fa fa-star" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
Favorite <a href="dashboards">Dashboards</a> will appear here
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="list-group">
|
|
||||||
<a
|
|
||||||
ng-href="dashboard/{{ dashboard.slug }}"
|
|
||||||
class="list-group-item"
|
|
||||||
ng-repeat="dashboard in $ctrl.favoriteDashboards"
|
|
||||||
ng-if="dashboard.is_favorite"
|
|
||||||
>
|
|
||||||
<span class="btn-favourite">
|
|
||||||
<i class="fa fa-star" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
{{ dashboard.name }}
|
|
||||||
<span class="label label-default" ng-if="dashboard.is_draft"
|
|
||||||
>Unpublished</span
|
|
||||||
>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<p class="f-500 m-b-20 c-black">Favorite Queries</p>
|
|
||||||
<p ng-if="$ctrl.noQueries">
|
|
||||||
<span class="btn-favourite">
|
|
||||||
<i class="fa fa-star" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
Favorite <a href="queries">Queries</a> will appear here
|
|
||||||
</p>
|
|
||||||
<div class="list-group">
|
|
||||||
<a
|
|
||||||
ng-href="queries/{{ query.id }}"
|
|
||||||
class="list-group-item"
|
|
||||||
ng-repeat="query in $ctrl.favoriteQueries"
|
|
||||||
ng-if="query.is_favorite"
|
|
||||||
>
|
|
||||||
<span class="btn-favourite">
|
|
||||||
<i class="fa fa-star" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
{{ query.name }}
|
|
||||||
<span class="label label-default" ng-if="query.is_draft"
|
|
||||||
>Unpublished</span
|
|
||||||
>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<beacon-consent></beacon-consent>
|
|
||||||
</div>
|
|
@ -1,42 +0,0 @@
|
|||||||
import template from './home.html';
|
|
||||||
import notification from '@/services/notification';
|
|
||||||
|
|
||||||
function HomeCtrl(Events, Dashboard, Query, $http, messages) {
|
|
||||||
Events.record('view', 'page', 'personal_homepage');
|
|
||||||
|
|
||||||
this.noDashboards = false;
|
|
||||||
this.noQueries = false;
|
|
||||||
|
|
||||||
this.messages = messages;
|
|
||||||
|
|
||||||
Dashboard.favorites().$promise.then((data) => {
|
|
||||||
this.favoriteDashboards = data.results;
|
|
||||||
this.noDashboards = data.results.length === 0;
|
|
||||||
});
|
|
||||||
Query.favorites().$promise.then((data) => {
|
|
||||||
this.favoriteQueries = data.results;
|
|
||||||
this.noQueries = data.results.length === 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.verifyEmail = () => {
|
|
||||||
$http.post('verification_email/').success(({ message }) => {
|
|
||||||
notification.success(message);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function init(ngModule) {
|
|
||||||
ngModule.component('homePage', {
|
|
||||||
template,
|
|
||||||
controller: HomeCtrl,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
'/': {
|
|
||||||
template: '<home-page></home-page>',
|
|
||||||
title: 'Redash',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
init.init = true;
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { BigMessage } from '@/components/BigMessage';
|
import { BigMessage } from '@/components/BigMessage';
|
||||||
import { NoTaggedObjectsFound } from '@/components/NoTaggedObjectsFound';
|
import { NoTaggedObjectsFound } from '@/components/NoTaggedObjectsFound';
|
||||||
import { EmptyState } from '@/components/empty-state/EmptyState';
|
import EmptyState from '@/components/empty-state/EmptyState';
|
||||||
|
|
||||||
export default function QueriesListEmptyState({ page, searchTerm, selectedTags }) {
|
export default function QueriesListEmptyState({ page, searchTerm, selectedTags }) {
|
||||||
if (searchTerm !== '') {
|
if (searchTerm !== '') {
|
||||||
|
Loading…
Reference in New Issue
Block a user