mirror of
https://github.com/valitydev/redash.git
synced 2024-11-07 09:28:51 +00:00
Implement empty states logic.
This commit is contained in:
parent
4d44be76ac
commit
3a840fcc5d
@ -1,18 +1,29 @@
|
||||
<div class="empty-state bg-white tiled">
|
||||
<div class="empty-state__summary">
|
||||
<h4 ng-if="$ctrl.title">{{$ctrl.title}}</h4>
|
||||
<h2 ng-if="$ctrl.icon"><i ng-class="$ctrl.icon" aria-hidden="true"></i></h2>
|
||||
<p>{{$ctrl.description}}</p>
|
||||
</div>
|
||||
<div class="empty-state__steps">
|
||||
<h4>Let's get started</h4>
|
||||
<ol>
|
||||
<li><del><a href="/data_sources">Connect</a> a Data Source</del></li>
|
||||
<li><del><a href="/queries/new/">Create</a> your first Query</del></li>
|
||||
<li ng-if="$ctrl.showAlertStep"><a href="/alerts/new/">Create</a> your first Alert</li>
|
||||
<li ng-if="$ctrl.showDashboardStep"><a href="/alerts/new/">Create</a> your first Dashboard</li>
|
||||
<li ng-if="$ctrl.showInviteStep"><a href="/alerts/new/">Invite</a> your team members</li>
|
||||
</ol>
|
||||
<p>Need more support? <a href="{{$ctrl.helpLink}}" target="_blank">See our Help <i class="fa fa-external-link" aria-hidden="true"></i></a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="empty-state bg-white tiled" ng-if="!$ctrl.loading && $ctrl.shouldShowOnboarding()">
|
||||
<div class="empty-state__summary">
|
||||
<h4 ng-if="$ctrl.title">{{$ctrl.title}}</h4>
|
||||
<h2 ng-if="$ctrl.icon">
|
||||
<i ng-class="$ctrl.icon" aria-hidden="true"></i>
|
||||
</h2>
|
||||
<p>{{$ctrl.description}}</p>
|
||||
</div>
|
||||
<div class="empty-state__steps">
|
||||
<h4>Let's get started</h4>
|
||||
<ol>
|
||||
<li ng-class="{done: $ctrl.dataSourceStepCompleted}">
|
||||
<a href="/data_sources">Connect</a> a Data Source</li>
|
||||
<li ng-class="{done: $ctrl.queryStepCompleted}">
|
||||
<a href="/queries/new">Create</a> your first Query</li>
|
||||
<li ng-if="$ctrl.showAlertStep" ng-class="{done: $ctrl.alertStepCompleted}">
|
||||
<a href="/alerts/new">Create</a> your first Alert</li>
|
||||
<li ng-if="$ctrl.showDashboardStep" ng-class="{done: $ctrl.dashboardStepCompleted}">
|
||||
<a ng-click="$ctrl.newDashboard()">Create</a> your first Dashboard</li>
|
||||
<li ng-if="$ctrl.showInviteStep" ng-class="{done: $ctrl.inviteStepCompleted}">
|
||||
<a href="/users/new">Invite</a> your team members</li>
|
||||
</ol>
|
||||
<p>Need more support?
|
||||
<a href="{{$ctrl.helpLink}}" target="_blank">See our Help
|
||||
<i class="fa fa-external-link" aria-hidden="true"></i>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
@ -12,8 +12,47 @@ const EmptyStateComponent = {
|
||||
showAlertStep: '<',
|
||||
showDashboardStep: '<',
|
||||
showInviteStep: '<',
|
||||
onboardingMode: '<',
|
||||
},
|
||||
controller() {
|
||||
controller($http, $uibModal) {
|
||||
this.loading = true;
|
||||
|
||||
$http.get('api/organization/status').then((response) => {
|
||||
this.loading = false;
|
||||
|
||||
const counters = response.data.object_counters;
|
||||
this.dataSourceStepCompleted = counters.data_sources > 0;
|
||||
this.queryStepCompleted = counters.queries > 0;
|
||||
this.dashboardStepCompleted = counters.dashboards > 0;
|
||||
this.alertStepCompleted = counters.alerts > 0;
|
||||
this.inviteStepCompleted = counters.users > 1;
|
||||
});
|
||||
|
||||
this.shouldShowOnboarding = () => {
|
||||
if (this.loading) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.onboardingMode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !(
|
||||
this.dataSourceStepCompleted &&
|
||||
this.queryStepCompleted &&
|
||||
this.dashboardStepCompleted &&
|
||||
this.inviteStepCompleted
|
||||
);
|
||||
};
|
||||
|
||||
this.newDashboard = () => {
|
||||
$uibModal.open({
|
||||
component: 'editDashboardDialog',
|
||||
resolve: {
|
||||
dashboard: () => ({ name: null, layout: null }),
|
||||
},
|
||||
});
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -9,7 +9,8 @@
|
||||
font-size: 14px;
|
||||
line-height: 21px;
|
||||
|
||||
.empty-state__summary, .empty-state__steps {
|
||||
.empty-state__summary,
|
||||
.empty-state__steps {
|
||||
width: 48%;
|
||||
}
|
||||
|
||||
@ -22,6 +23,10 @@
|
||||
padding: 17px;
|
||||
}
|
||||
|
||||
li.done {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0 0 15px;
|
||||
}
|
||||
@ -43,7 +48,8 @@
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.empty-state__summary, .empty-state__steps {
|
||||
.empty-state__summary,
|
||||
.empty-state__steps {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,10 @@
|
||||
<empty-state icon="fa fa-bell-o"
|
||||
description="Get notified on certain events"
|
||||
show-alert-step="true"
|
||||
help-link="http://help.redash.io/category/23-alerts"></empty-state>
|
||||
help-link="http://help.redash.io/category/23-alerts"
|
||||
ng-if="$ctrl.showEmptyState"></empty-state>
|
||||
|
||||
<div class="bg-white tiled">
|
||||
<div class="bg-white tiled" ng-if="$ctrl.showList">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -11,8 +11,17 @@ class AlertsListCtrl {
|
||||
constructor(Events, Alert) {
|
||||
Events.record('view', 'page', 'alerts');
|
||||
|
||||
this.showEmptyState = false;
|
||||
this.showList = false;
|
||||
|
||||
this.alerts = new Paginator([], { itemsPerPage: 20 });
|
||||
Alert.query((alerts) => {
|
||||
if (alerts.length > 0) {
|
||||
this.showList = true;
|
||||
} else {
|
||||
this.showEmptyState = true;
|
||||
}
|
||||
|
||||
this.alerts.updateRows(alerts.map(alert => ({
|
||||
id: alert.id,
|
||||
name: alert.name,
|
||||
|
@ -4,9 +4,10 @@
|
||||
<empty-state icon="zmdi zmdi-view-quilt"
|
||||
description="See the big picture"
|
||||
show-dashboard-step="true"
|
||||
help-link="http://help.redash.io/category/22-dashboards"></empty-state>
|
||||
help-link="http://help.redash.io/category/22-dashboards"
|
||||
ng-if="$ctrl.showEmptyState"></empty-state>
|
||||
|
||||
<div class="row">
|
||||
<div class="row" ng-if="$ctrl.showList">
|
||||
<div class="col-lg-3">
|
||||
<input type='text' class='form-control' placeholder="Search Dashboards..."
|
||||
ng-change="$ctrl.update()" ng-model="$ctrl.searchText" autofocus/>
|
||||
|
@ -35,7 +35,15 @@ function DashboardListCtrl(Dashboard, $location) {
|
||||
};
|
||||
|
||||
this.allTags = [];
|
||||
this.showList = false;
|
||||
this.showEmptyState = false;
|
||||
|
||||
this.dashboards.$promise.then((data) => {
|
||||
if (data.length > 0) {
|
||||
this.showList = true;
|
||||
} else {
|
||||
this.showEmptyState = true;
|
||||
}
|
||||
const out = data.map(dashboard => dashboard.name.match(TAGS_REGEX));
|
||||
this.allTags = _.unique(_.flatten(out)).filter(e => e).map(tag => tag.replace(/:$/, ''));
|
||||
this.allTags.sort();
|
||||
|
@ -1,10 +1,9 @@
|
||||
<div class="container">
|
||||
|
||||
|
||||
<empty-state title="Welcome to Redash 👋"
|
||||
description="Connect to any data source, easily visualize and share your data"
|
||||
show-dashboard-step="true"
|
||||
show-invite-step="true"
|
||||
onboarding-mode="true"
|
||||
help-link="http://help.redash.io/article/32-getting-started"></empty-state>
|
||||
|
||||
<div class="tile">
|
||||
|
@ -50,6 +50,9 @@ class QueriesListCtrl {
|
||||
{ name: 'My Queries', path: 'queries/my' },
|
||||
{ name: 'Search', path: 'queries/search' },
|
||||
];
|
||||
|
||||
this.showList = () => this.paginator.getPageRows() !== undefined && this.paginator.getPageRows().length > 0;
|
||||
this.showEmptyState = () => this.paginator.getPageRows() !== undefined && this.paginator.getPageRows().length === 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,38 +1,41 @@
|
||||
<div class="container">
|
||||
<page-header title="Queries"></page-header>
|
||||
|
||||
<empty-state icon="fa fa-code"
|
||||
description="Getting the data from your datasources."
|
||||
help-link="http://help.redash.io/category/21-querying"></empty-state>
|
||||
<empty-state icon="fa fa-code" description="Getting the data from your datasources." help-link="http://help.redash.io/category/21-querying"
|
||||
ng-if="$ctrl.showEmptyState()"></empty-state>
|
||||
|
||||
<tab-nav tabs="$ctrl.tabs"></tab-nav>
|
||||
<div ng-if="$ctrl.showList()">
|
||||
<tab-nav tabs="$ctrl.tabs"></tab-nav>
|
||||
|
||||
<div class="bg-white tiled">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Created By</th>
|
||||
<th>Created At</th>
|
||||
<th>Runtime</th>
|
||||
<th>Last Executed At</th>
|
||||
<th>Update Schedule</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="query in $ctrl.paginator.getPageRows()">
|
||||
<td><a href="queries/{{query.id}}">{{query.name}}</a> <span class="label label-default" ng-if="query.is_draft">Unpublished</span></td>
|
||||
<td>
|
||||
<img ng-src="{{query.user.profile_image_url}}" class="profile__image_thumb"/>
|
||||
{{query.user.name}}
|
||||
</td>
|
||||
<td>{{query.created_at | dateTime}}</td>
|
||||
<td>{{query.runtime | durationHumanize}}</td>
|
||||
<td>{{query.retrieved_at | dateTime}}</td>
|
||||
<td>{{query.schedule | scheduleHumanize}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<paginator paginator="$ctrl.paginator"></paginator>
|
||||
<div class="bg-white tiled">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Created By</th>
|
||||
<th>Created At</th>
|
||||
<th>Runtime</th>
|
||||
<th>Last Executed At</th>
|
||||
<th>Update Schedule</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="query in $ctrl.paginator.getPageRows()">
|
||||
<td>
|
||||
<a href="queries/{{query.id}}">{{query.name}}</a>
|
||||
<span class="label label-default" ng-if="query.is_draft">Unpublished</span>
|
||||
</td>
|
||||
<td>
|
||||
<img ng-src="{{query.user.profile_image_url}}" class="profile__image_thumb" /> {{query.user.name}}
|
||||
</td>
|
||||
<td>{{query.created_at | dateTime}}</td>
|
||||
<td>{{query.runtime | durationHumanize}}</td>
|
||||
<td>{{query.retrieved_at | dateTime}}</td>
|
||||
<td>{{query.schedule | scheduleHumanize}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<paginator paginator="$ctrl.paginator"></paginator>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -21,6 +21,6 @@ def status_api():
|
||||
|
||||
|
||||
def init_app(app):
|
||||
from redash.handlers import embed, queries, static, authentication, admin, setup
|
||||
from redash.handlers import embed, queries, static, authentication, admin, setup, organization
|
||||
app.register_blueprint(routes)
|
||||
api.init_app(app)
|
||||
|
23
redash/handlers/organization.py
Normal file
23
redash/handlers/organization.py
Normal file
@ -0,0 +1,23 @@
|
||||
import json
|
||||
|
||||
from flask import request
|
||||
from flask_login import login_required
|
||||
from redash import models
|
||||
from redash.handlers import routes
|
||||
from redash.handlers.base import json_response
|
||||
from redash.permissions import require_admin
|
||||
|
||||
|
||||
@routes.route('/api/organization/status', methods=['GET'])
|
||||
@require_admin
|
||||
@login_required
|
||||
def organization_status():
|
||||
counters = {
|
||||
'users': models.User.query.count(),
|
||||
'alerts': models.Alert.query.count(),
|
||||
'data_sources': models.DataSource.query.count(),
|
||||
# todo: not archived
|
||||
'queries': models.Query.query.count(),
|
||||
'dashboards': models.Dashboard.query.count(),
|
||||
}
|
||||
return json_response(dict(object_counters=counters))
|
Loading…
Reference in New Issue
Block a user