diff --git a/DEVELOPER.md b/DEVELOPER.md index 5fc2d358..d009e422 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -16,33 +16,70 @@ Now you should an api key, api secret, and an organization associated to your Us ##### Steps to get impac loaded: -1. Terminal commands to load dependancies +Temporary instructions to get Impac! Angular running for development: + +1. Create an account on http://impac-mnoe-uat.maestrano.io. +2. Fork impac-express (https://github.com/maestrano/impac-express) and clone it to your local machine +3. In the impac-express project, modify config/settings/development.yml as follow: + + ```ruby + impac: + protocol: https + host: api-impac-uat.maestrano.io + mno: + protocol: https + host: api-hub-uat.maestrano.io ``` - npm install - bower install +4. In the impac-express project, create config/application.yml as follow: + + ```ruby + # Tenant Credentials + tenant_id: c6b44271-7334-0134-8df7-35f23abf243c + tenant_key: 4Vcpt_2GnCfhlyBYbN_bUQ + + # Emailing platform + SPARKPOST_API_KEY: changeme + + # Rails secret key + SECRET_KEY_BASE: e48e21d80b2943c1872f69fb3cf4f8a8c24bc9fa3abdc14549df2dc181d46bfb5698271fb4bc1511e45ba0633ef02b494985b67f6e418182014f95a5e600f808 + + # Google Tag Manager + google_tag_container: GTM-TH3MLB ``` - -2. Configuring with credentials & launching workspace - - Add your api_key & api_secret to the settings factory in the `workspace/index.js` file +5. Build and start the impac-express project: + + ```bash + bundle install + foreman start + ``` +6. Fork impac-angular (https://github.com/maestrano/impac-angular) and clone it to your local machine. _Note this method for running impac-angular is only valid from versions >= 1.4.5_. +7. In the impac-angular project, modify workspace/app/services/dev-settings.svc.js as follow: ```javascript - module.factory('settings', function () { - return { - // Credentials and endpoints - mno_url: 'https://uat.maestrano.io', - impac_url: 'https://api-impac-uat.maestrano.io', - api_key: 'YOUR_KEY', - api_secret: 'YOUR_SECRET', + var DEFAULTS = { + // API Endpoints + mnoeUrl: { + host: 'http://localhost:7000', + base: '/mnoe/jpi/v1' + }, + impacUrl: { + host: 'https://api-impac-uat.maestrano.io', + base: '/api' + } ... - } - }); ``` +8. build and start the impac-angular project -3. There a few ways to prepare & launch the Impac Developer Workspace.. - 1. Run `gulp` or `gulp serve`, this will inject dependencies, serve impac-angular, and **live reload** on change. - 2. Run `gulp serve:noreload`, if rebuilding the library on every change is using too much cpu - **you will need to run `gulp workspace` and refresh the page after every change** - 2. Run `gulp workspace` to build and inject dependencies, the open the `workspace/index.html` file in a browser - **you will need to run `gulp workspace` and refresh the page after every change** + ```bash + npm install + bower install + gulp + ``` +9. A new tab with the Developer Workspace will open in your browser, log-in with the email/password you've created at step 1. +10. You should be able to start coding in the project impac-angular: just saving any .coffee or .html file will automatically reload your Developer Workspace, applying your code. +11. To populate the Widgets and KPIs with data, integrate an App! I recommend Xero. +Note: Coming in a patch version soon you will be able to skip steps 2. to 5. ##### Workspace Architecture diff --git a/bower.json b/bower.json index 681e60ee..4b1acabd 100755 --- a/bower.json +++ b/bower.json @@ -38,7 +38,10 @@ }, "devDependencies": { "angular-mocks": "~1.4.0", - "angular-scenario": "~1.4.0" + "angular-scenario": "~1.4.0", + "AngularDevise": "angular-devise#^1.3.0", + "angular-ui-router": "^0.3.1", + "angular-cookies": "^1.5.8" }, "license": "Copyright 2015 Maestrano Pty Ltd", "repository": { @@ -46,6 +49,7 @@ "url": "git://github.com/maestrano/impac-angular.git" }, "resolutions": { - "angular": "~1.4.0" + "angular": "1.4.14", + "angular-cookies": "^1.5.8" } } diff --git a/gulp/server.js b/gulp/server.js index cf44320e..e4f018f2 100644 --- a/gulp/server.js +++ b/gulp/server.js @@ -11,6 +11,8 @@ var $ = require('gulp-load-plugins')({ pattern: ['browser-*'] }); +var proxyMiddleware = require('http-proxy-middleware'); + function browserSyncInit(baseDir, browser) { browser = browser === undefined ? 'default' : browser; diff --git a/gulp/workspace.js b/gulp/workspace.js index a12c2274..9d83d594 100644 --- a/gulp/workspace.js +++ b/gulp/workspace.js @@ -2,12 +2,19 @@ var gulp = require('gulp'); var wiredep = require('wiredep').stream; +var $ = require('gulp-load-plugins')(); /** * Builds and injects dependencies into workspace/index.html */ gulp.task('workspace', ['build'], function () { + var injectFiles = gulp.src(['workspace/app/**/*.js', 'workspace/app/**/*.css'], {read: false}); + var injectOptions = { relative: true }; + return gulp.src('workspace/index.html') - .pipe(wiredep({})) + .pipe(wiredep({ + devDependencies: true + })) + .pipe($.inject(injectFiles, injectOptions)) .pipe(gulp.dest('workspace')); }); diff --git a/package.json b/package.json index f1a30828..74ead509 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "gulp-sourcemaps": "^1.6.0", "gulp-uglify": "^0.3.1", "gulp-util": "^3.0.6", + "http-proxy-middleware": "^0.17.2", "jasmine-core": "^2.4.1", "karma": "~0.12.22", "karma-chrome-launcher": "^0.1.4", diff --git a/src/services/routes/routes.svc.coffee b/src/services/routes/routes.svc.coffee index bf0e85db..040b0dd1 100644 --- a/src/services/routes/routes.svc.coffee +++ b/src/services/routes/routes.svc.coffee @@ -1,4 +1,5 @@ # provider for configuring http endpoints. +# TODO: REFACTOR THIS FOR MNOE AS DEFAULT + REVISE BASE PATHS DEFAULTS. angular .module('impac.services.routes', []) .provider('ImpacRoutes', () -> diff --git a/workspace/app/app.js b/workspace/app/app.js new file mode 100644 index 00000000..dde52ce2 --- /dev/null +++ b/workspace/app/app.js @@ -0,0 +1,133 @@ +// IMPAC! WORKSPACE +//-------------------------------------------------------- +// PLEASE DO NOT COMMIT CHANGES TO THIS FILE. +// You can use `git add -p ./` to selectively add files to your git index. +// ------------------------------------------------------- +var module = angular.module('impacWorkspace', [ + 'maestrano.impac', + 'toastr', + 'Devise', + 'ui.router', + 'ngCookies' +]); + +// -- +// The Developer Toolkit Workspace States. +// ------------------------------------------------------- +module.config(function ($stateProvider, $urlRouterProvider) { + + $urlRouterProvider.otherwise('/login'); + + $stateProvider + .state('workspace', { + abstract: true, + templateUrl: 'app/views/workspace/workspace.html', + controller: 'WorkspaceController', + controllerAs: 'main' + }) + .state('workspace.login', { + url: '/login', + templateUrl: 'app/views/workspace/login/login.html', + controller: 'LoginController', + controllerAs: 'vm' + }) + .state('workspace.impac', { + url: '/impac', + templateUrl: 'app/views/workspace/impac/impac.html', + controller: 'ImpacController', + controllerAs: 'vm' + }) + .state('workspace.settings', { + url: '/settings', + templateUrl: 'app/views/workspace/settings/settings.html', + controller: 'SettingsController', + controllerAs: 'vm' + }); + +}); + +// -- +// Configure Angular Devise paths for mno-enterprise. +// ------------------------------------------------------- +module.config(function (AuthProvider, DevSettingsProvider) { + var mnoeHostUrl = DevSettingsProvider.$get().defaults().mnoeUrl.host; + // Customize login + AuthProvider.loginMethod('POST'); + AuthProvider.loginPath(mnoeHostUrl + '/mnoe/auth/users/sign_in.json'); + + // Customize logout + AuthProvider.logoutMethod('DELETE'); + AuthProvider.logoutPath(mnoeHostUrl + '/mnoe/auth/users/sign_out.json'); + + // Customize register + AuthProvider.registerMethod('POST'); + AuthProvider.registerPath(mnoeHostUrl + '/mnoe/auth/users'); +}); + +// -- +// Configure Angular $http to apply XSRF Token headers to CORS requests. +// ------------------------------------------------------- +module.constant('CSRF', { + "headerTokenKey": 'X-XSRF-TOKEN', + "cookieTokenKey": 'XSRF-TOKEN' +}); +module.config(function($httpProvider) { + // Allow "credentialed" requests that are aware of HTTP cookies and HTTP + // Authentication information. + $httpProvider.defaults.withCredentials = true; +}); +module.run(function($http, DevSession) { + DevSession.create(); +}); + +// -- +// Impac! Angular Provider Service Configurations. +// ------------------------------------------------------- +module.run(function (ImpacLinking, ImpacAssets, ImpacRoutes, ImpacTheming, ImpacDeveloper, DevUser, DevSettings) { + + var defaults = DevSettings.defaults(); + + // Configure ImpacRoutes + // ------------------------------------------------------- + ImpacRoutes.configureRoutes(DevSettings.buildRoutesConfig(defaults.mnoeUrl, defaults.impacUrl, defaults.multipleWatchableMode)); + + + // Configure ImpacTheming - aesthetic and feature customisations across the app + // ------------------------------------------------------- + ImpacTheming.configure({ + dhbKpisConfig: { + enableKpis: true + }, + alertsConfig: { + enableAlerts: true + }, + dhbConfig: { + showDhbHeading: true, + dhbHeadingText: 'Your business at a glance, in real-time' + }, + dhbSelectorConfig: { + selectorType: 'dropdown', + pdfModeEnabled: true + }, + dhbSettings: { + syncApps: { + show: function() { return false; } + } + } + }); + + // Configure ImpacAssets - app asset file configurations + // ------------------------------------------------------- + ImpacAssets.configure({ + defaultImagesPath: '/dist/images', + }) + + // Configure ImpacDeveloper - developer toolkit specifc customisations + // ------------------------------------------------------- + ImpacDeveloper.configure(DevSettings.buildDeveloperConfig(defaults.widgetsTemplates)); + + // Configure ImpacLinking - Links core callbacks required for impac-angular lib to run. + // ------------------------------------------------------- + ImpacLinking.linkData(DevSettings.buildLinkingConfig(defaults.orgUid, defaults.mnoeUrl)); + +}); diff --git a/workspace/app/services/dev-session.svc.js b/workspace/app/services/dev-session.svc.js new file mode 100644 index 00000000..fd12c40f --- /dev/null +++ b/workspace/app/services/dev-session.svc.js @@ -0,0 +1,29 @@ +// ------------------------------------------------------- +// Impac! Angular DevSession Service +// -------- +// Providing CSRF Support for Impac! Workspace. +// ------------------------------------------------------- +angular.module('impacWorkspace').service('DevSession', function ($log, $http, $cookies, CSRF, DevSettings) { + var _self = this; + + // Create a session and xsrf token - ping mnoe api so we get a valid XSRF cookie. + this.create = function () { + return $http.get(DevSettings.defaults().mnoeUrl.host + '/mnoe/auth/users/sign_in.json') + .success(function() { + _self.setCsrfHttpHeader(); + }); + }; + + // Angular Devise methods (e.g `Auth.currentUser()`) generate new sessions, this keeps the + // default http headers up to date, ensuring the session is maintained. + this.update = function () { + this.setCsrfHttpHeader(); + }; + + // Set stored XSRF cookie as $http headers common. + this.setCsrfHttpHeader = function () { + $http.defaults.headers.common[CSRF.headerTokenKey] = $cookies.get(CSRF.cookieTokenKey); + }; + + return this; +}); diff --git a/workspace/app/services/dev-settings.svc.js b/workspace/app/services/dev-settings.svc.js new file mode 100644 index 00000000..a775bc86 --- /dev/null +++ b/workspace/app/services/dev-settings.svc.js @@ -0,0 +1,120 @@ +// ------------------------------------------------------- +// Impac Workspace Settings +// -------- +// Providing Impac! Angular defaults and configuration abilities. +// ------------------------------------------------------- +angular.module('impacWorkspace').service('DevSettings', function ($q, ImpacRoutes, ImpacLinking, ImpacDeveloper, DevUser) { + var _self = this; + + var DEFAULTS = { + // API Endpoints + mnoeUrl: { + host: 'http://localhost:7000', + base: '/mnoe/jpi/v1' + }, + impacUrl: { + host: 'https://api-impac-uat.maestrano.io', + base: '/api' + }, + // Selected Organization - first organisations if unspecified + orgUid: '', + // ----------------------------------------------- + // Kpis configurations + // ----------------------------------------------- + // Change this to `true` to force kpi's to allow multiple watchables, instead of + // using the mno_hub kpis#discover action maps kpis to have single watchables per kpi. + multipleWatchableMode: false, + // Stub widget templates - add new widgets! + //------------------------------------------------ + // Widget templates are stored on Maestrano API, stub your + // new widget templates here, and contact us for permenant additions or + // changes to existing widget templates. + widgetsTemplates: [ + // Example widget template, please see documentation "How-To: Create a Widget" for available + // options, and importantly, note how the path & metadata.template attributes work. + // -- + // { + // path: 'accounts/assets_summary', + // name: 'Assets / Liabilities summary', + // metadata: { template: 'accounts/assets_liability_summary' }, + // desc: 'Compare Assets and Liabilities accounts', + // icon: 'pie-chart', + // width: 3 + // }, + ] + }; + + this._defaults = angular.copy(DEFAULTS); + + this.defaults = function () { + return angular.copy(_self._defaults); + }; + + this.resetDefaults = function () { + return _self._defaults = angular.copy(DEFAULTS); + } + + // NOTE: this will be refactored a lot when mnoe routes are added as default + // in impac-angular. + // TODO: add routes settings to #/settings, 'basic' & 'advance' tabs could be good. + this.buildRoutesConfig = function (mnoeUrl, impacUrl, multipleWatchableMode) { + var routesConfig = { + mnoHub: mnoeUrl.host + mnoeUrl.base, + impacPrefix: "/impac", + impacApi: impacUrl.host + impacUrl.base, + dashboards: { + index: mnoeUrl.host + mnoeUrl.base + "/impac/dashboards" + }, + widgets: { + index: mnoeUrl.host + mnoeUrl.base + "/impac/widgets", + create: mnoeUrl.host + mnoeUrl.base + "/impac/dashboards/:dashboard_id/widgets" + }, + kpis: { + index: mnoeUrl.host + mnoeUrl.base + "/impac/kpis", + create: mnoeUrl.host + mnoeUrl.base + "/impac/dashboards/:dashboard_id/kpis", + update: mnoeUrl.host + mnoeUrl.base + "/impac/kpis/:id", + del: mnoeUrl.host + mnoeUrl.base + "/impac/kpis/:id" + }, + alerts: { + index: mnoeUrl.host + mnoeUrl.base + "/impac/alerts", + create: mnoeUrl.host + mnoeUrl.base + "/impac/kpis/:kpi_id/alerts", + del: mnoeUrl.host + mnoeUrl.base + "/impac/alerts/:id" + } + } + // Removes custom index path, defaulting kpi discovery to impac api, where KPIs with + // multiple watchables are returned. + if (multipleWatchableMode) { delete routesConfig.kpis.index; } + return angular.copy(routesConfig) + }; + + this.buildLinkingConfig = function (orgUid, mnoeUrl) { + return { + organizations: function () { + return DevUser.getOrganizations(orgUid, mnoeUrl); + }, + user: function () { + return DevUser.getUser(mnoeUrl); + }, + // TODO: add pusher key to advance settings on #/settings. + pusher_key: 'e98dfd8e4a359a7faf48' // Maestrano pusher account key. + } + } + + this.buildDeveloperConfig = function (widgetsTemplates) { + return { + status: true, + widgetsTemplates: widgetsTemplates + } + }; + + this.updateProviders = function (data) { + ImpacRoutes.configureRoutes(_self.buildRoutesConfig(data.mnoeUrl, data.impacUrl, data.multipleWatchableMode)); + ImpacLinking.linkData(_self.buildLinkingConfig(data.orgUid, data.mnoeUrl)); + ImpacDeveloper.configure(_self.buildDeveloperConfig(data.widgetsTemplates)); + + // Save settings data to this service's copy of defaults. + _self._defaults = data; + }; + + return this; +}); diff --git a/workspace/app/services/dev-user.svc.js b/workspace/app/services/dev-user.svc.js new file mode 100644 index 00000000..148da8de --- /dev/null +++ b/workspace/app/services/dev-user.svc.js @@ -0,0 +1,81 @@ +// ------------------------------------------------------- +// Impac! Angular DevUser Service +// -------- +// Providing Auth & User data for the Impac! Workspace. +// ------------------------------------------------------- +angular.module('impacWorkspace').service('DevUser', function ($log, $http, $q, Auth, toastr) { + var _self = this; + + // Angular Devise methods exposed via this service + // ------ + this.login = Auth.login; + this.logout = Auth.logout; + this.register = Auth.register; + this.isAuthenticated = Auth.isAuthenticated; + this.currentUser = Auth.currentUser; + + // MnoExpress User & Organization retrieval + // ------ + this.getUserData = function(mnoeUrl) { + var errMsg = 'Unable to retrieve User data'; + var url = mnoeUrl.host + mnoeUrl.base + '/current_user'; + return $http.get(url).then( + function (response) { + var user = (response.data && response.data.current_user); + if (!user.logged_in) { + _self.fail(errMsg); + return $q.reject(errMsg); + } + return user || {}; + }, function (error) { + _self.fail(errMsg, error); + return $q.reject(errMsg); + } + ); + }; + + this.getCurrentOrganization = function(orgUid, mnoeUrl) { + var deferred = $q.defer(); + _self.getOrganizations(orgUid, mnoeUrl).then(function (response) { + var currentOrg = _.find(response.organizations, function (org) { + return org.id == response.currentOrgId; + }); + deferred.notify(currentOrg); + }); + return deferred.promise; + }; + + this.getOrganizations = function(orgUid, mnoeUrl) { + return _self.getUserData(mnoeUrl).then( + function (user) { + var orgs = (user.organizations || []); + var orga = orgs.find(function(orga) { return orga.uid == orgUid }); + var orgId = (orga && orga.id) || orgs[0].id || null; + + return { organizations: orgs, currentOrgId: orgId }; + }, function (err) { + return $q.reject(err); + } + ); + }; + + this.getUser = function(mnoeUrl) { + return _self.getUserData(mnoeUrl).then( + function (user) { + return user; + }, function (err) { + return $q.reject(err); + } + ); + }; + + // Logging and Toastr notifications + // ------ + this.fail = function (msg, error) { + if (error == null) { error = ''; } + $log.error('Develop Workspace Error: ' + msg, error); + toastr.error(msg, 'Error'); + }; + + return this; +}); diff --git a/workspace/app/views/workspace/impac/impac.controller.js b/workspace/app/views/workspace/impac/impac.controller.js new file mode 100644 index 00000000..05357901 --- /dev/null +++ b/workspace/app/views/workspace/impac/impac.controller.js @@ -0,0 +1,11 @@ +angular.module('impacWorkspace').controller('ImpacController', function ($state, DevUser) { + vm = this; + + vm.showImpac = DevUser.isAuthenticated; + + DevUser.currentUser().then(null, function () { + $state.go('workspace.login'); + }); + + return vm; +}); diff --git a/workspace/app/views/workspace/impac/impac.html b/workspace/app/views/workspace/impac/impac.html new file mode 100644 index 00000000..342e5680 --- /dev/null +++ b/workspace/app/views/workspace/impac/impac.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/workspace/app/views/workspace/login/login.controller.js b/workspace/app/views/workspace/login/login.controller.js new file mode 100644 index 00000000..00e04612 --- /dev/null +++ b/workspace/app/views/workspace/login/login.controller.js @@ -0,0 +1,44 @@ +angular.module('impacWorkspace').controller('LoginController', function ($state, DevUser) { + vm = this; + + + if (DevUser.isAuthenticated()) { + $state.go('workspace.impac'); + } else { + DevUser.currentUser().then(function () { + $state.go('workspace.impac'); + }); + } + + vm.showRegisterForm = false; + vm.creds = { email: '', password: '', company: '' }; + + vm.toggleRegisterForm = function () { + vm.showRegisterForm = !vm.showRegisterForm; + }; + + vm.login = function () { + DevUser.login(_.omit(vm.creds, ['company'])).then( + function () { + $state.go('workspace.impac'); + }, function (response) { + var msg = (response.data && response.data.error) || 'Unable to login :('; + DevUser.fail(msg, response); + } + ); + }; + + vm.register = function () { + DevUser.register(vm.creds).then( + function (res) { + console.log(res); + $state.go('workspace.impac'); + }, function (err) { + var msg = (response.data && response.data.error) || 'Unable to sign up :('; + DevUser.fail(msg, response); + } + ); + }; + + return vm; +}); diff --git a/workspace/app/views/workspace/login/login.css b/workspace/app/views/workspace/login/login.css new file mode 100644 index 00000000..0faa660d --- /dev/null +++ b/workspace/app/views/workspace/login/login.css @@ -0,0 +1,28 @@ +#workspace .workspace-login { + font-family: 'Share Tech Mono', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +#workspace .workspace-login .login-box-container { + text-align: center; + margin-top: 100px; +} + +#workspace .workspace-login .login-box { + width: 360px; + margin: auto; + padding: 10px 20px 20px 20px; + border-radius: 3px; +} + +#workspace .workspace-login .login-box-actions a { + color: rgb(117, 129, 146); +} +#workspace .workspace-login .login-box-actions a:hover { + color: black; +} + +#workspace .workspace-login .login-box-title { + color: rgb(117, 129, 146); + margin-bottom: 15px; + text-transform: uppercase; +} diff --git a/workspace/app/views/workspace/login/login.html b/workspace/app/views/workspace/login/login.html new file mode 100644 index 00000000..53932dbb --- /dev/null +++ b/workspace/app/views/workspace/login/login.html @@ -0,0 +1,30 @@ +
+
+ + +
+
diff --git a/workspace/app/views/workspace/settings/settings.controller.js b/workspace/app/views/workspace/settings/settings.controller.js new file mode 100644 index 00000000..064274d2 --- /dev/null +++ b/workspace/app/views/workspace/settings/settings.controller.js @@ -0,0 +1,59 @@ +angular.module('impacWorkspace').controller('SettingsController', function ($scope, $state, DevUser, DevSettings) { + vm = this; + + vm.settings = DevSettings.defaults(); + vm.settings.orgUid = main.currentOrganization.uid || ''; + vm.settings.widgetsTemplates = displayWidgetTemplates(vm.settings.widgetsTemplates); + vm.updateError = ''; + + DevUser.currentUser().then(null, function () { + $state.go('workspace.login'); + }); + + DevUser.getOrganizations(vm.settings.orgUid, vm.settings.mnoeUrl).then(function (data) { + vm.availableOrgUids = _.map(data.organizations, function (org) { + return org.uid; + }); + }); + + // On new session (load), current org uid is broadcasted here when it is has been set in the + // workspace.controller. + $scope.$on('current-org-uid', function (event, orgUid) { + vm.settings.orgUid = orgUid; + }); + + vm.update = function () { + if (!validateWidgetTemplatesJson()) { return; } + DevSettings.updateProviders(vm.settings); + $scope.$emit('updated-providers'); + $state.go('workspace.impac'); + }; + + vm.back = function () { + $state.go('workspace.impac'); + }; + + vm.resetDefaults = function () { + vm.settings = DevSettings.resetDefaults(); + vm.settings.orgUid = main.currentOrganization.uid || ''; + } + + function validateWidgetTemplatesJson () { + var json = document.getElementById('widgets-templates-textarea').value; + if (!json.length) { json = '[]'; } + try { + var array = JSON.parse(json); + } catch (e) { + vm.updateError = 'Invalid JSON for Widgets Templates'; + return false; + } + vm.settings.widgetsTemplates = array; + return true; + } + + function displayWidgetTemplates (templates) { + return templates.length ? JSON.stringify(templates, undefined, 4) : ''; + } + + return vm; +}); diff --git a/workspace/app/views/workspace/settings/settings.css b/workspace/app/views/workspace/settings/settings.css new file mode 100644 index 00000000..56640759 --- /dev/null +++ b/workspace/app/views/workspace/settings/settings.css @@ -0,0 +1,56 @@ +#workspace .workspace-settings { + font-family: 'Share Tech Mono', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +#workspace .workspace-settings .settings-box-container { + text-align: center; + margin-top: 100px; +} + +#workspace .workspace-settings .settings-box { + width: 360px; + margin: auto; + padding: 10px 20px 20px 20px; + border-radius: 3px; +} + +#workspace .workspace-settings .settings-box .double-section-header { + margin: 0; +} + +#workspace .workspace-settings .settings-box .double-section-item label { + color: rgb(117, 129, 146); +} + +#workspace .workspace-settings .settings-box .checkbox { + text-align: left; + margin-left:10px; +} + +#workspace .workspace-settings .settings-box .settings-actions { + margin-top: 30px; +} + +#workspace .workspace-settings .settings-box-container .settings-box .form-group { + text-align: left; +} + +#workspace .workspace-settings .settings-box-title { + color: rgb(117, 129, 146); + margin-bottom: 15px; + text-transform: uppercase; +} + +#workspace .workspace-settings .settings-error { + margin-top: 20px; + color: red; +} + +#workspace .workspace-settings .col-sm-7, +#workspace .workspace-settings .col-xs-7 { + padding-right: 2px; +} +#workspace .workspace-settings .col-sm-5, +#workspace .workspace-settings .col-xs-5 { + padding-left: 0; +} diff --git a/workspace/app/views/workspace/settings/settings.html b/workspace/app/views/workspace/settings/settings.html new file mode 100644 index 00000000..e40e4bdc --- /dev/null +++ b/workspace/app/views/workspace/settings/settings.html @@ -0,0 +1,69 @@ +
+
+
+

Workspace Settings

+
+
+
+
+ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + +
+
+ + +
+
+ +
+
+ + + +
+
+

{{vm.updateError}}

+
+
+
+
+
diff --git a/workspace/app/views/workspace/workspace.controller.js b/workspace/app/views/workspace/workspace.controller.js new file mode 100644 index 00000000..9552a033 --- /dev/null +++ b/workspace/app/views/workspace/workspace.controller.js @@ -0,0 +1,54 @@ +angular.module('impacWorkspace').controller('WorkspaceController', function ($scope, $state, DevUser, DevSettings, DevSession) { + main = this; + + main.isAuthenticated = DevUser.isAuthenticated; + + main.currentOrganization = {}; + + $scope.$on('devise:login', function () { + setCurrentOrganization(); + }); + + $scope.$on('devise:new-registration', function() { + setCurrentOrganization(); + }); + + $scope.$on('devise:new-session', function () { + DevSession.update(); + }); + + $scope.$on('devise:logout', function () { + DevSession.update(); + }); + + $scope.$on('updated-providers', function () { + setCurrentOrganization(); + }); + + main.settings = function () { + $state.go('workspace.settings'); + }; + + main.logout = function () { + DevUser.logout().then( + function () { + main.currentOrganization = {}; + $state.go('workspace.login'); + }, + function (error) { + DevUser.fail('Unable to logout', error); + } + ); + }; + + function setCurrentOrganization() { + defaults = DevSettings.defaults(); + DevUser.getCurrentOrganization(defaults.orgUid, defaults.mnoeUrl).then(null, null, function(org) { + main.currentOrganization = org; + $scope.$broadcast('current-org-uid', org.uid) + }); + } + + return main; + +}); diff --git a/workspace/app/views/workspace/workspace.css b/workspace/app/views/workspace/workspace.css new file mode 100644 index 00000000..ed4db28a --- /dev/null +++ b/workspace/app/views/workspace/workspace.css @@ -0,0 +1,63 @@ +html, body { + box-sizing: border-box; + height: 100%; +} + +*, *:before, *:after { + box-sizing: inherit; +} + +body { + background-color: #E6EDEE; +} + +#workspace { + height: 100%; +} + +#workspace .workspace-header { + position: relative; + background-color: black; + color: white; + min-width: 100%; + min-height: 70px; + font-family: 'Share Tech Mono', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +#workspace .workspace-header .workspace-title { + text-align: center; +} + +#workspace .workspace-header h1 { + margin: 15px 0 0 0; + font-size: 32px; +} + +#workspace .workspace-header .workspace-actions { + text-align: right; + margin: 17px 60px 10px 0px; +} + +#workspace .workspace-header .current-org { + margin-top: 14px; + margin-left: 70px; +} + +#workspace .workspace-header .current-org td, +#workspace .workspace-header .current-org th { + padding: 0px 10px; +} + +#workspace .workspace-header .current-org .current-org-results { + color: #74dd74; +} + +#workspace .workspace-dashboard { + margin: 0 auto; + width: 91%; +} + +/* IMPAC OVERRIDES */ +.analytics { + margin: 0; +} diff --git a/workspace/app/views/workspace/workspace.html b/workspace/app/views/workspace/workspace.html new file mode 100644 index 00000000..6bc80a1d --- /dev/null +++ b/workspace/app/views/workspace/workspace.html @@ -0,0 +1,35 @@ +
+
+
+
+ + + + + + + + + + + + + + + +
UIDNameRole
{{main.currentOrganization.uid || '-'}}{{main.currentOrganization.name || '-'}}{{main.currentOrganization.current_user_role || '-'}}
+
+
+
+

IMPAC! DEVELOPER WORKSPACE

+
+
+
+ + +
+
+
+ +
+
diff --git a/workspace/index.css b/workspace/index.css deleted file mode 100644 index b75b5eab..00000000 --- a/workspace/index.css +++ /dev/null @@ -1,34 +0,0 @@ -/* WORKSPACE */ -body { - background-color: #E6EDEE; -} - -#workspace-header { - text-align: center; - background-color: black; - color: white; - font-family: 'Share Tech Mono', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -#workspace-header h1 { - margin: 0; - padding: 15px 40px; -} - -#workspace-dashboard { - margin: 0 auto; - width: 91%; -} - -footer#workspace-footer { - padding: 20px 40px; -} - -footer#workspace-footer p { - margin: 0; -} - -/* IMPAC OVERRIDES */ -.analytics { - margin: 0; -} diff --git a/workspace/index.html b/workspace/index.html index 2fc92851..da7092fd 100644 --- a/workspace/index.html +++ b/workspace/index.html @@ -10,7 +10,11 @@ - + + + + + @@ -19,19 +23,7 @@ -
-

IMPAC! DEVELOPER WORKSPACE

-
- -
- -
- - +
@@ -48,9 +40,23 @@

IMPAC! DEVELOPER WORKSPACE

+ + + + + - + + + + + + + + + + diff --git a/workspace/index.js b/workspace/index.js deleted file mode 100644 index 717e7ecf..00000000 --- a/workspace/index.js +++ /dev/null @@ -1,180 +0,0 @@ -// IMPAC! WORKSPACE -//-------------------------------------------------------- -// PLEASE DO NOT COMMIT CHANGES TO THIS FILE. -// You can use `git add -p ./` to selectively add files to your git index. -// ------------------------------------------------------- -var module = angular.module('impacWorkspace', ['maestrano.impac', 'toastr']); - -// ------------------------------------------------------- -// Impac Workspace Settings -// ------------------------------------------------------- -// Provide important configurations for the impacWorkspace. -module.factory('settings', function () { - return { - // Credentials and endpoints - mno_url: 'https://get-uat.maestrano.io', - impac_url: 'https://api-impac-uat.maestrano.io', - api_key: '', - api_secret: '', - org_uid: '', // First organisations if unspecified - - // ----------------------------------------------- - // Kpis configurations - // ----------------------------------------------- - // Change this to `true` to force kpi's to allow multiple watchables, instead of - // using the mno_hub kpis#discover action maps kpis to have single watchables per kpi. - multiple_watchables_mode: false, - // Stub widget templates - add new widgets! - //------------------------------------------------ - // Widget templates are stored on Maestrano API, stub your - // new widget templates here, and contact us for permenant additions or - // changes to existing widget templates. - widgetsTemplates: [ - // Example widget template, please see documentation "How-To: Create a Widget" for available - // options, and importantly, note how the path & metadata.template attributes work. - // -- - // { - // path: 'accounts/assets_summary', - // name: 'Assets / Liabilities summary', - // metadata: { template: 'accounts/assets_liability_summary' }, - // desc: 'Compare Assets and Liabilities accounts', - // icon: 'pie-chart', - // width: 3 - // }, - ] - // -- - }; -}); - -//-------------------------------------------------------- -// Do not edit below unless you know what you're doing. -//-------------------------------------------------------- - -// HTTP Interceptor -//-------------------------------------------------------- -// Applies headers / configurations to certain requests. -module.factory('domainSpecificAuthorization', function($window, settings){ - return { - request: function(config) { - // encodes a base64 string - Basic Authentication. - var credentials = $window.btoa(settings.api_key + ':' + settings.api_secret); - var url = config.url; - // merges basic auth with headers if url is to Maestrano's ecosystem. - if (url.indexOf(settings.mno_url) >= 0 || url.indexOf(settings.impac_url) >= 0) { - angular.extend(config.headers, { Authorization: 'Basic ' + credentials }); - } - return config; - } - }; -}); -module.config(function ($httpProvider) { - $httpProvider.interceptors.push('domainSpecificAuthorization'); -}); -// -- -// Impac! Angular Provider configurations -// ------------------------------------------------------- -module.run(function ($log, $q, $http, ImpacLinking, ImpacAssets, ImpacRoutes, ImpacTheming, ImpacDeveloper, settings, toastr) { - - // Check credentials have been provided - if (!settings.api_key || !settings.api_secret) { - fail('Missing authentication credentials!'); - } - - // Configure the ImpacRoutes service options. - var routesConfig = { - mnoHub: settings.mno_url + '/api/v2', - impacPrefix: '/impac', - impacApi: settings.impac_url + '/api', - kpis: { - index: settings.mno_url + '/api/v2/impac/kpis' - } - }; - if (settings.multiple_watchables_mode) { delete routesConfig.kpis; } - ImpacRoutes.configureRoutes(routesConfig); - - // Configure the ImpacTheming service options. - ImpacTheming.configure({ - dhbKpisConfig: { - enableKpis: true - }, - alertsConfig: { - enableAlerts: true - }, - dhbConfig: { - showDhbHeading: true, - dhbHeadingText: 'Your business at a glance, in real-time' - }, - dhbSelectorConfig: { - selectorType: 'dropdown', - pdfModeEnabled: true - }, - dhbSettings: { - syncApps: { - show: function() { return false; } - } - } - }); - - // Link Impac! Assets - ImpacAssets.configure({ - defaultImagesPath: '/dist/images', - }) - - // Configure the ImpacDeveloper service options. - ImpacDeveloper.configure({ - status: true, - widgetsTemplates: settings.widgetsTemplates || [] - }); - - // Link core callbacks required for impac-angular lib to run. - ImpacLinking.linkData({ - organizations: function () { - return getOrganizations(settings.org_uid); - }, - user: function () { - return getUser(); - }, - pusher_key: 'e98dfd8e4a359a7faf48' // Maestrano pusher account key. - }); - - function getOrganizations(orgUid) { - var deferred = $q.defer(); - getUserData().then(function (user) { - var orgs = (user.organizations || []); - var orga = orgs.find(function(orga) { return orga.uid == orgUid }); - var orgId = (orga && orga.id) || orgs[0].id || null; - - deferred.resolve({ organizations: orgs, currentOrgId: orgId }); - }, function (err) { - deferred.reject(err); - }); - return deferred.promise; - } - - function getUser() { - var deferred = $q.defer(); - getUserData().then(function (user) { - deferred.resolve(user); - }, function (err) { - deferred.reject(err); - }); - return deferred.promise; - } - - function getUserData() { - return $http.get(settings.mno_url + '/api/v2/current_user') - .then(function (response) { - return response.data; - }, function () { - var msg = 'Unable to retrieve Organizations'; - fail(msg); - return $q.reject(msg); - }); - } - - function fail(msg) { - $log.error('workspace/index.js Error: ' + msg); - toastr.error(msg, 'Error'); - } - -}); diff --git a/workspace/settings.json b/workspace/settings.json deleted file mode 100644 index 7f8bb0a1..00000000 --- a/workspace/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "url": "http://localhost:3000", - "api_key": "7149f0c91d7e995c10be79ed4881c035d662632995e852f550313345e0f0b982", - "api_secret": "4a776ea4-8134-4f38-b632-85cbe951524e" -}