Browse Source

feat(apps): began work on app pages

Torkel Ödegaard 10 years ago
parent
commit
18eb9d6076

+ 10 - 18
examples/nginx-app/module.js

@@ -1,28 +1,20 @@
 define([
 define([
-  'angular',
-  'app/app'
-], function(angular, app)  {
+], function()  {
+  'use strict';
 
 
-  var module = angular.module('nginx-app', []);
-  app.default.useModule(module);
+  function StreamPageCtrl() {}
+  StreamPageCtrl.templateUrl = 'public/plugins/nginx-app/partials/stream.html';
 
 
-  module.config(function($routeProvider) {
-    $routeProvider
-      .when('/nginx/stream', {
-        templateUrl: 'public/plugins/nginx-app/partials/stream.html',
-      });
-  });
+  function LogsPageCtrl() {}
+  LogsPageCtrl.templateUrl = 'public/plugins/nginx-app/partials/logs.html';
 
 
-  function NginxConfigCtrl() {
-    this.appEditCtrl.beforeUpdate = function() {
-      alert('before!');
-    };
-  }
+  function NginxConfigCtrl() {}
   NginxConfigCtrl.templateUrl = 'public/plugins/nginx-app/partials/config.html';
   NginxConfigCtrl.templateUrl = 'public/plugins/nginx-app/partials/config.html';
 
 
-
   return {
   return {
-    ConfigCtrl: NginxConfigCtrl
+    ConfigCtrl: NginxConfigCtrl,
+    StreamPageCtrl: StreamPageCtrl,
+    LogsPageCtrl: LogsPageCtrl,
   };
   };
 
 
 });
 });

+ 4 - 0
examples/nginx-app/partials/logs.html

@@ -0,0 +1,4 @@
+
+<h1>Nginx logs view</h1>
+
+Logs!

+ 2 - 10
examples/nginx-app/partials/stream.html

@@ -1,12 +1,4 @@
-<topnav title="Nginx" icon="fa fa-fw fa-cubes" subnav="true">
-	<ul class="nav">
-		<li class="active" ><a href="org/apps">Overview</a></li>
-	</ul>
-</topnav>
 
 
-<div class="page-container" style="background: transparent; border: 0;">
-  <div class="page-wide" ng-init="ctrl.init()">
-		<h1>NGINX app</h1>
-	</div>
-</div>
+<h1>Nginx stream view</h1>
 
 
+testing!

+ 2 - 2
examples/nginx-app/plugin.json

@@ -6,8 +6,8 @@
   "staticRoot": ".",
   "staticRoot": ".",
 
 
   "pages": [
   "pages": [
-    {"name": "Live stream", "url": "nginx/stream", "reqRole": "Editor"},
-    {"name": "Log view", "url": "nginx/log", "reqRole": "Editor"}
+    { "name": "Live stream", "component": "StreamPageCtrl", "role": "Editor"},
+    { "name": "Log view", "component": "LogsPageCtrl", "role": "Viewer"}
   ],
   ],
 
 
   "css": {
   "css": {

+ 9 - 9
pkg/api/dtos/apps.go

@@ -6,15 +6,15 @@ import (
 )
 )
 
 
 type AppSettings struct {
 type AppSettings struct {
-	Name     string                   `json:"name"`
-	AppId    string                   `json:"appId"`
-	Enabled  bool                     `json:"enabled"`
-	Pinned   bool                     `json:"pinned"`
-	Module   string                   `json:"module"`
-	Info     *plugins.PluginInfo      `json:"info"`
-	Pages    []plugins.AppPluginPage  `json:"pages"`
-	Includes []plugins.AppIncludeInfo `json:"includes"`
-	JsonData map[string]interface{}   `json:"jsonData"`
+	Name     string                    `json:"name"`
+	AppId    string                    `json:"appId"`
+	Enabled  bool                      `json:"enabled"`
+	Pinned   bool                      `json:"pinned"`
+	Module   string                    `json:"module"`
+	Info     *plugins.PluginInfo       `json:"info"`
+	Pages    []*plugins.AppPluginPage  `json:"pages"`
+	Includes []*plugins.AppIncludeInfo `json:"includes"`
+	JsonData map[string]interface{}    `json:"jsonData"`
 }
 }
 
 
 func NewAppSettingsDto(def *plugins.AppPlugin, data *models.AppSettings) *AppSettings {
 func NewAppSettingsDto(def *plugins.AppPlugin, data *models.AppSettings) *AppSettings {

+ 0 - 4
pkg/api/index.go

@@ -86,10 +86,6 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 	}
 	}
 
 
 	for _, plugin := range enabledPlugins.Apps {
 	for _, plugin := range enabledPlugins.Apps {
-		if plugin.Module != "" {
-			data.PluginModules = append(data.PluginModules, plugin.Module)
-		}
-
 		if plugin.Css != nil {
 		if plugin.Css != nil {
 			data.PluginCss = append(data.PluginCss, &dtos.PluginCss{Light: plugin.Css.Light, Dark: plugin.Css.Dark})
 			data.PluginCss = append(data.PluginCss, &dtos.PluginCss{Light: plugin.Css.Light, Dark: plugin.Css.Dark})
 		}
 		}

+ 14 - 6
pkg/plugins/app_plugin.go

@@ -4,13 +4,15 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"strings"
 	"strings"
 
 
+	"github.com/gosimple/slug"
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/models"
 )
 )
 
 
 type AppPluginPage struct {
 type AppPluginPage struct {
-	Name    string          `json:"name"`
-	Url     string          `json:"url"`
-	ReqRole models.RoleType `json:"reqRole"`
+	Name      string          `json:"name"`
+	Slug      string          `json:"slug"`
+	Component string          `json:"component"`
+	Role      models.RoleType `json:"role"`
 }
 }
 
 
 type AppPluginCss struct {
 type AppPluginCss struct {
@@ -27,9 +29,9 @@ type AppIncludeInfo struct {
 type AppPlugin struct {
 type AppPlugin struct {
 	FrontendPluginBase
 	FrontendPluginBase
 	Css      *AppPluginCss     `json:"css"`
 	Css      *AppPluginCss     `json:"css"`
-	Pages    []AppPluginPage   `json:"pages"`
+	Pages    []*AppPluginPage  `json:"pages"`
 	Routes   []*AppPluginRoute `json:"routes"`
 	Routes   []*AppPluginRoute `json:"routes"`
-	Includes []AppIncludeInfo  `json:"-"`
+	Includes []*AppIncludeInfo `json:"-"`
 
 
 	Pinned  bool `json:"-"`
 	Pinned  bool `json:"-"`
 	Enabled bool `json:"-"`
 	Enabled bool `json:"-"`
@@ -67,7 +69,7 @@ func (app *AppPlugin) Load(decoder *json.Decoder, pluginDir string) error {
 	for _, panel := range Panels {
 	for _, panel := range Panels {
 		if strings.HasPrefix(panel.PluginDir, app.PluginDir) {
 		if strings.HasPrefix(panel.PluginDir, app.PluginDir) {
 			panel.IncludedInAppId = app.Id
 			panel.IncludedInAppId = app.Id
-			app.Includes = append(app.Includes, AppIncludeInfo{
+			app.Includes = append(app.Includes, &AppIncludeInfo{
 				Name: panel.Name,
 				Name: panel.Name,
 				Id:   panel.Id,
 				Id:   panel.Id,
 				Type: panel.Type,
 				Type: panel.Type,
@@ -75,6 +77,12 @@ func (app *AppPlugin) Load(decoder *json.Decoder, pluginDir string) error {
 		}
 		}
 	}
 	}
 
 
+	for _, page := range app.Pages {
+		if page.Slug == "" {
+			page.Slug = slug.Make(page.Name)
+		}
+	}
+
 	Apps[app.Id] = app
 	Apps[app.Id] = app
 	return nil
 	return nil
 }
 }

+ 0 - 6
public/app/app.ts

@@ -72,12 +72,6 @@ export class GrafanaApp {
     this.useModule(coreModule);
     this.useModule(coreModule);
 
 
     var preBootRequires = [System.import('app/features/all')];
     var preBootRequires = [System.import('app/features/all')];
-    var pluginModules = config.bootData.pluginModules || [];
-
-    // add plugin modules
-    for (var i = 0; i < pluginModules.length; i++) {
-      preBootRequires.push(System.import(pluginModules[i]));
-    }
 
 
     Promise.all(preBootRequires).then(() => {
     Promise.all(preBootRequires).then(() => {
       // disable tool tip animation
       // disable tool tip animation

+ 1 - 1
public/app/core/core.ts

@@ -28,7 +28,7 @@ import {navbarDirective} from './components/navbar/navbar';
 import {arrayJoin} from './directives/array_join';
 import {arrayJoin} from './directives/array_join';
 import 'app/core/controllers/all';
 import 'app/core/controllers/all';
 import 'app/core/services/all';
 import 'app/core/services/all';
-import 'app/core/routes/all';
+import 'app/core/routes/routes';
 import './filters/filters';
 import './filters/filters';
 import coreModule from './core_module';
 import coreModule from './core_module';
 
 

+ 15 - 2
public/app/core/directives/plugin_component.ts

@@ -143,15 +143,28 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
       }
       }
       // AppConfigCtrl
       // AppConfigCtrl
       case 'app-config-ctrl': {
       case 'app-config-ctrl': {
-        return System.import(scope.ctrl.appModel.module).then(function(appModule) {
+        let appModel = scope.ctrl.appModel;
+        return System.import(appModel.module).then(function(appModule) {
           return {
           return {
-            name: 'app-config-' + scope.ctrl.appModel.appId,
+            name: 'app-config-' + appModel.appId,
             bindings: {appModel: "=", appEditCtrl: "="},
             bindings: {appModel: "=", appEditCtrl: "="},
             attrs: {"app-model": "ctrl.appModel", "app-edit-ctrl": "ctrl"},
             attrs: {"app-model": "ctrl.appModel", "app-edit-ctrl": "ctrl"},
             Component: appModule.ConfigCtrl,
             Component: appModule.ConfigCtrl,
           };
           };
         });
         });
       }
       }
+      // App Page
+      case 'app-page': {
+        let appModel = scope.ctrl.appModel;
+        return System.import(appModel.module).then(function(appModule) {
+          return {
+            name: 'app-page-' + appModel.appId + '-' + scope.ctrl.page.slug,
+            bindings: {appModel: "="},
+            attrs: {"app-model": "ctrl.appModel"},
+            Component: appModule[scope.ctrl.page.component],
+          };
+        });
+      }
       // Panel
       // Panel
       case 'panel': {
       case 'panel': {
         return loadPanelComponentInfo(scope, attrs);
         return loadPanelComponentInfo(scope, attrs);

+ 0 - 166
public/app/core/routes/all.js

@@ -1,166 +0,0 @@
-define([
-  'angular',
-  '../core_module',
-  './bundle_loader',
-  './dashboard_loaders',
-], function(angular, coreModule, BundleLoader) {
-  "use strict";
-
-  coreModule.default.config(function($routeProvider, $locationProvider) {
-    $locationProvider.html5Mode(true);
-
-    var loadOrgBundle = new BundleLoader.BundleLoader('app/features/org/all');
-    var loadAppsBundle = new BundleLoader.BundleLoader('app/features/apps/all');
-
-    $routeProvider
-      .when('/', {
-        templateUrl: 'public/app/partials/dashboard.html',
-        controller : 'LoadDashboardCtrl',
-        reloadOnSearch: false,
-      })
-      .when('/dashboard/:type/:slug', {
-        templateUrl: 'public/app/partials/dashboard.html',
-        controller : 'LoadDashboardCtrl',
-        reloadOnSearch: false,
-      })
-      .when('/dashboard-solo/:type/:slug', {
-        templateUrl: 'public/app/features/panel/partials/soloPanel.html',
-        controller : 'SoloPanelCtrl',
-      })
-      .when('/dashboard-import/:file', {
-        templateUrl: 'public/app/partials/dashboard.html',
-        controller : 'DashFromImportCtrl',
-        reloadOnSearch: false,
-      })
-      .when('/dashboard/new', {
-        templateUrl: 'public/app/partials/dashboard.html',
-        controller : 'NewDashboardCtrl',
-        reloadOnSearch: false,
-      })
-      .when('/import/dashboard', {
-        templateUrl: 'public/app/features/dashboard/partials/import.html',
-        controller : 'DashboardImportCtrl',
-      })
-      .when('/datasources', {
-        templateUrl: 'public/app/features/datasources/partials/list.html',
-        controller : 'DataSourcesCtrl',
-        resolve: loadOrgBundle,
-      })
-      .when('/datasources/edit/:id', {
-        templateUrl: 'public/app/features/datasources/partials/edit.html',
-        controller : 'DataSourceEditCtrl',
-        resolve: loadOrgBundle,
-      })
-      .when('/datasources/new', {
-        templateUrl: 'public/app/features/datasources/partials/edit.html',
-        controller : 'DataSourceEditCtrl',
-        resolve: loadOrgBundle,
-      })
-      .when('/org', {
-        templateUrl: 'public/app/features/org/partials/orgDetails.html',
-        controller : 'OrgDetailsCtrl',
-        resolve: loadOrgBundle,
-      })
-      .when('/org/new', {
-        templateUrl: 'public/app/features/org/partials/newOrg.html',
-        controller : 'NewOrgCtrl',
-        resolve: loadOrgBundle,
-      })
-      .when('/org/users', {
-        templateUrl: 'public/app/features/org/partials/orgUsers.html',
-        controller : 'OrgUsersCtrl',
-        resolve: loadOrgBundle,
-      })
-      .when('/org/apikeys', {
-        templateUrl: 'public/app/features/org/partials/orgApiKeys.html',
-        controller : 'OrgApiKeysCtrl',
-        resolve: loadOrgBundle,
-      })
-      .when('/profile', {
-        templateUrl: 'public/app/features/profile/partials/profile.html',
-        controller : 'ProfileCtrl',
-      })
-      .when('/profile/password', {
-        templateUrl: 'public/app/features/profile/partials/password.html',
-        controller : 'ChangePasswordCtrl',
-      })
-      .when('/profile/select-org', {
-        templateUrl: 'public/app/features/profile/partials/select_org.html',
-        controller : 'SelectOrgCtrl',
-      })
-      .when('/admin/settings', {
-        templateUrl: 'public/app/features/admin/partials/settings.html',
-        controller : 'AdminSettingsCtrl',
-      })
-      .when('/admin/users', {
-        templateUrl: 'public/app/features/admin/partials/users.html',
-        controller : 'AdminListUsersCtrl',
-      })
-      .when('/admin/users/create', {
-        templateUrl: 'public/app/features/admin/partials/new_user.html',
-        controller : 'AdminEditUserCtrl',
-      })
-      .when('/admin/users/edit/:id', {
-        templateUrl: 'public/app/features/admin/partials/edit_user.html',
-        controller : 'AdminEditUserCtrl',
-      })
-      .when('/admin/orgs', {
-        templateUrl: 'public/app/features/admin/partials/orgs.html',
-        controller : 'AdminListOrgsCtrl',
-      })
-      .when('/admin/orgs/edit/:id', {
-        templateUrl: 'public/app/features/admin/partials/edit_org.html',
-        controller : 'AdminEditOrgCtrl',
-      })
-      .when('/admin/stats', {
-        templateUrl: 'public/app/features/admin/partials/stats.html',
-        controller : 'AdminStatsCtrl',
-        controllerAs: 'ctrl',
-      })
-      .when('/login', {
-        templateUrl: 'public/app/partials/login.html',
-        controller : 'LoginCtrl',
-      })
-      .when('/invite/:code', {
-        templateUrl: 'public/app/partials/signup_invited.html',
-        controller : 'InvitedCtrl',
-      })
-      .when('/signup', {
-        templateUrl: 'public/app/partials/signup_step2.html',
-        controller : 'SignUpCtrl',
-      })
-      .when('/user/password/send-reset-email', {
-        templateUrl: 'public/app/partials/reset_password.html',
-        controller : 'ResetPasswordCtrl',
-      })
-      .when('/user/password/reset', {
-        templateUrl: 'public/app/partials/reset_password.html',
-        controller : 'ResetPasswordCtrl',
-      })
-      .when('/dashboard/snapshots', {
-        templateUrl: 'public/app/features/snapshot/partials/snapshots.html',
-        controller : 'SnapshotsCtrl',
-        controllerAs: 'ctrl',
-      })
-      .when('/apps', {
-        templateUrl: 'public/app/features/apps/partials/list.html',
-        controller: 'AppListCtrl',
-        controllerAs: 'ctrl',
-        resolve: loadAppsBundle,
-      })
-      .when('/apps/edit/:appId', {
-        templateUrl: 'public/app/features/apps/partials/edit.html',
-        controller: 'AppEditCtrl',
-        controllerAs: 'ctrl',
-        resolve: loadAppsBundle,
-      })
-      .when('/global-alerts', {
-        templateUrl: 'public/app/features/dashboard/partials/globalAlerts.html',
-      })
-      .otherwise({
-        templateUrl: 'public/app/partials/error.html',
-        controller: 'ErrorCtrl'
-      });
-  });
-
-});

+ 171 - 0
public/app/core/routes/routes.ts

@@ -0,0 +1,171 @@
+///<reference path="../../headers/common.d.ts" />
+
+import angular from 'angular';
+import coreModule from 'app/core/core_module';
+import {BundleLoader} from './bundle_loader';
+
+/** @ngInject **/
+function setupAngularRoutes($routeProvider, $locationProvider) {
+  $locationProvider.html5Mode(true);
+
+  var loadOrgBundle = new BundleLoader('app/features/org/all');
+  var loadAppsBundle = new BundleLoader('app/features/apps/all');
+
+  $routeProvider
+  .when('/', {
+    templateUrl: 'public/app/partials/dashboard.html',
+    controller : 'LoadDashboardCtrl',
+    reloadOnSearch: false,
+  })
+  .when('/dashboard/:type/:slug', {
+    templateUrl: 'public/app/partials/dashboard.html',
+    controller : 'LoadDashboardCtrl',
+    reloadOnSearch: false,
+  })
+  .when('/dashboard-solo/:type/:slug', {
+    templateUrl: 'public/app/features/panel/partials/soloPanel.html',
+    controller : 'SoloPanelCtrl',
+  })
+  .when('/dashboard-import/:file', {
+    templateUrl: 'public/app/partials/dashboard.html',
+    controller : 'DashFromImportCtrl',
+    reloadOnSearch: false,
+  })
+  .when('/dashboard/new', {
+    templateUrl: 'public/app/partials/dashboard.html',
+    controller : 'NewDashboardCtrl',
+    reloadOnSearch: false,
+  })
+  .when('/import/dashboard', {
+    templateUrl: 'public/app/features/dashboard/partials/import.html',
+    controller : 'DashboardImportCtrl',
+  })
+  .when('/datasources', {
+    templateUrl: 'public/app/features/datasources/partials/list.html',
+    controller : 'DataSourcesCtrl',
+    resolve: loadOrgBundle,
+  })
+  .when('/datasources/edit/:id', {
+    templateUrl: 'public/app/features/datasources/partials/edit.html',
+    controller : 'DataSourceEditCtrl',
+    resolve: loadOrgBundle,
+  })
+  .when('/datasources/new', {
+    templateUrl: 'public/app/features/datasources/partials/edit.html',
+    controller : 'DataSourceEditCtrl',
+    resolve: loadOrgBundle,
+  })
+  .when('/org', {
+    templateUrl: 'public/app/features/org/partials/orgDetails.html',
+    controller : 'OrgDetailsCtrl',
+    resolve: loadOrgBundle,
+  })
+  .when('/org/new', {
+    templateUrl: 'public/app/features/org/partials/newOrg.html',
+    controller : 'NewOrgCtrl',
+    resolve: loadOrgBundle,
+  })
+  .when('/org/users', {
+    templateUrl: 'public/app/features/org/partials/orgUsers.html',
+    controller : 'OrgUsersCtrl',
+    resolve: loadOrgBundle,
+  })
+  .when('/org/apikeys', {
+    templateUrl: 'public/app/features/org/partials/orgApiKeys.html',
+    controller : 'OrgApiKeysCtrl',
+    resolve: loadOrgBundle,
+  })
+  .when('/profile', {
+    templateUrl: 'public/app/features/profile/partials/profile.html',
+    controller : 'ProfileCtrl',
+  })
+  .when('/profile/password', {
+    templateUrl: 'public/app/features/profile/partials/password.html',
+    controller : 'ChangePasswordCtrl',
+  })
+  .when('/profile/select-org', {
+    templateUrl: 'public/app/features/profile/partials/select_org.html',
+    controller : 'SelectOrgCtrl',
+  })
+  .when('/admin/settings', {
+    templateUrl: 'public/app/features/admin/partials/settings.html',
+    controller : 'AdminSettingsCtrl',
+  })
+  .when('/admin/users', {
+    templateUrl: 'public/app/features/admin/partials/users.html',
+    controller : 'AdminListUsersCtrl',
+  })
+  .when('/admin/users/create', {
+    templateUrl: 'public/app/features/admin/partials/new_user.html',
+    controller : 'AdminEditUserCtrl',
+  })
+  .when('/admin/users/edit/:id', {
+    templateUrl: 'public/app/features/admin/partials/edit_user.html',
+    controller : 'AdminEditUserCtrl',
+  })
+  .when('/admin/orgs', {
+    templateUrl: 'public/app/features/admin/partials/orgs.html',
+    controller : 'AdminListOrgsCtrl',
+  })
+  .when('/admin/orgs/edit/:id', {
+    templateUrl: 'public/app/features/admin/partials/edit_org.html',
+    controller : 'AdminEditOrgCtrl',
+  })
+  .when('/admin/stats', {
+    templateUrl: 'public/app/features/admin/partials/stats.html',
+    controller : 'AdminStatsCtrl',
+    controllerAs: 'ctrl',
+  })
+  .when('/login', {
+    templateUrl: 'public/app/partials/login.html',
+    controller : 'LoginCtrl',
+  })
+  .when('/invite/:code', {
+    templateUrl: 'public/app/partials/signup_invited.html',
+    controller : 'InvitedCtrl',
+  })
+  .when('/signup', {
+    templateUrl: 'public/app/partials/signup_step2.html',
+    controller : 'SignUpCtrl',
+  })
+  .when('/user/password/send-reset-email', {
+    templateUrl: 'public/app/partials/reset_password.html',
+    controller : 'ResetPasswordCtrl',
+  })
+  .when('/user/password/reset', {
+    templateUrl: 'public/app/partials/reset_password.html',
+    controller : 'ResetPasswordCtrl',
+  })
+  .when('/dashboard/snapshots', {
+    templateUrl: 'public/app/features/snapshot/partials/snapshots.html',
+    controller : 'SnapshotsCtrl',
+    controllerAs: 'ctrl',
+  })
+  .when('/apps', {
+    templateUrl: 'public/app/features/apps/partials/list.html',
+    controller: 'AppListCtrl',
+    controllerAs: 'ctrl',
+    resolve: loadAppsBundle,
+  })
+  .when('/apps/:appId/edit', {
+    templateUrl: 'public/app/features/apps/partials/edit.html',
+    controller: 'AppEditCtrl',
+    controllerAs: 'ctrl',
+    resolve: loadAppsBundle,
+  })
+  .when('/apps/:appId/page/:slug', {
+    templateUrl: 'public/app/features/apps/partials/page.html',
+    controller: 'AppPageCtrl',
+    controllerAs: 'ctrl',
+    resolve: loadAppsBundle,
+  })
+  .when('/global-alerts', {
+    templateUrl: 'public/app/features/dashboard/partials/globalAlerts.html',
+  })
+  .otherwise({
+    templateUrl: 'public/app/partials/error.html',
+    controller: 'ErrorCtrl'
+  });
+}
+
+coreModule.config(setupAngularRoutes);

+ 1 - 0
public/app/features/apps/all.ts

@@ -1,2 +1,3 @@
 import './edit_ctrl';
 import './edit_ctrl';
+import './page_ctrl';
 import './list_ctrl';
 import './list_ctrl';

+ 23 - 0
public/app/features/apps/page_ctrl.ts

@@ -0,0 +1,23 @@
+///<reference path="../../headers/common.d.ts" />
+
+import angular from 'angular';
+import _ from 'lodash';
+
+export class AppPageCtrl {
+  page: any;
+  appModel: any;
+
+  /** @ngInject */
+  constructor(private backendSrv, private $routeParams: any, private $rootScope) {
+    this.backendSrv.get(`/api/org/apps/${this.$routeParams.appId}/settings`).then(app => {
+      this.appModel = app;
+      this.page = _.findWhere(app.pages, {slug: this.$routeParams.slug});
+      if (!this.page) {
+        $rootScope.appEvent('alert-error', ['App Page Not Found', '']);
+      }
+    });
+  }
+}
+
+angular.module('grafana.controllers').controller('AppPageCtrl', AppPageCtrl);
+

+ 1 - 1
public/app/features/apps/partials/list.html

@@ -24,7 +24,7 @@
               </div>
               </div>
             </div>
             </div>
 						<span class="filter-list-card-title">
 						<span class="filter-list-card-title">
-							<a href="apps/edit/{{app.appId}}">
+							<a href="apps/{{app.appId}}/edit">
 								{{app.name}}
 								{{app.name}}
 							</a>
 							</a>
 							&nbsp; &nbsp;
 							&nbsp; &nbsp;

+ 14 - 0
public/app/features/apps/partials/page.html

@@ -0,0 +1,14 @@
+<navbar icon="fa fa-fw fa-cubes" title="{{ctrl.appModel.name}}" subnav="true">
+	<ul class="nav">
+		<li class="active"><a href="apps/nginx">{{ctrl.page.name}}</a></li>
+	</ul>
+</navbar>
+
+<h2>
+	App page
+</h2>
+
+<div ng-if="ctrl.page">
+	<plugin-component type="app-page" page="ctrl.page">
+	</plugin-component>
+</div>

+ 0 - 1
public/views/index.html

@@ -50,7 +50,6 @@
 		window.grafanaBootData = {
 		window.grafanaBootData = {
 			user:[[.User]],
 			user:[[.User]],
 			settings: [[.Settings]],
 			settings: [[.Settings]],
-			pluginModules: [[.PluginModules]],
 			mainNavLinks: [[.MainNavLinks]]
 			mainNavLinks: [[.MainNavLinks]]
 		};
 		};
 	</script>
 	</script>