diff --git a/frontend/app/components/index.js b/frontend/app/components/index.js index 88b71d2b..23ec34b9 100644 --- a/frontend/app/components/index.js +++ b/frontend/app/components/index.js @@ -8,3 +8,5 @@ export { default as queryLink } from './query-link'; export { default as parameters } from './parameters'; export { default as permissionsEditor } from './permissions-editor'; export { default as dynamicTable } from './dynamic-table'; +export { default as paginator } from './paginator'; +export { default as settingsScreen } from './settings-screen'; diff --git a/frontend/app/components/paginator.js b/frontend/app/components/paginator.js new file mode 100644 index 00000000..0b26c558 --- /dev/null +++ b/frontend/app/components/paginator.js @@ -0,0 +1,31 @@ +class PaginatorCtrl { + constructor() { + this.page = this.paginator.page; + } + pageChanged() { + this.paginator.setPage(this.page); + } +} + +export default function (ngModule) { + ngModule.component('paginator', { + template: ` +
+ +
+ `, + bindings: { + paginator: '<', + }, + controller: PaginatorCtrl, + }); +} diff --git a/frontend/app/components/settings-screen.html b/frontend/app/components/settings-screen.html new file mode 100644 index 00000000..e8f1c9ca --- /dev/null +++ b/frontend/app/components/settings-screen.html @@ -0,0 +1,18 @@ + + + +
+
+ + +
+ +
+
+
diff --git a/frontend/app/components/settings-screen.js b/frontend/app/components/settings-screen.js new file mode 100644 index 00000000..2181e7f4 --- /dev/null +++ b/frontend/app/components/settings-screen.js @@ -0,0 +1,24 @@ +import startsWith from 'underscore.string/startsWith'; +import template from './settings-screen.html'; + +export default function (ngModule) { + ngModule.directive('settingsScreen', $location => + ({ + restrict: 'E', + transclude: true, + template, + controller($scope, currentUser) { + $scope.usersPage = startsWith($location.path(), '/users'); + $scope.groupsPage = startsWith($location.path(), '/groups'); + $scope.dsPage = startsWith($location.path(), '/data_sources'); + $scope.destinationsPage = startsWith($location.path(), '/destinations'); + $scope.snippetsPage = startsWith($location.path(), '/query_snippets'); + + $scope.showGroupsLink = currentUser.hasPermission('list_users'); + $scope.showUsersLink = currentUser.hasPermission('list_users'); + $scope.showDsLink = currentUser.hasPermission('admin'); + $scope.showDestinationsLink = currentUser.hasPermission('admin'); + }, + }) + ); +} diff --git a/frontend/app/index.js b/frontend/app/index.js index 99c55ddb..0f9dc3e0 100644 --- a/frontend/app/index.js +++ b/frontend/app/index.js @@ -11,6 +11,10 @@ import uiBootstrap from 'angular-ui-bootstrap'; import uiSelect from 'ui-select'; import toastr from 'angular-toastr'; import 'angular-moment'; + +import 'brace'; +import 'angular-ui-ace'; + import { ngTable } from 'ng-table'; import { each } from 'underscore'; @@ -25,7 +29,7 @@ import registerVisualizations from './visualizations'; import markdownFilter from './filters/markdown'; const requirements = [ - ngRoute, ngResource, ngSanitize, uiBootstrap, uiSelect, ngTable.name, 'angularMoment', toastr, + ngRoute, ngResource, ngSanitize, uiBootstrap, uiSelect, ngTable.name, 'angularMoment', toastr, 'ui.ace', ]; const ngModule = angular.module('app', requirements); @@ -88,6 +92,7 @@ function registerPages() { ngModule.config(($routeProvider) => { each(routes, (route, path) => { + console.log('Route: ', path); $routeProvider.when(path, route); }); }); diff --git a/frontend/app/pages/index.js b/frontend/app/pages/index.js index cdebbb75..a1d62ba7 100644 --- a/frontend/app/pages/index.js +++ b/frontend/app/pages/index.js @@ -4,3 +4,4 @@ export { default as alertsList } from './alerts-list'; export { default as alert } from './alert'; export { default as admin } from './admin'; export { default as dashboards } from './dashboards'; +export { default as querySnippets } from './query-snippets'; diff --git a/frontend/app/pages/query-snippets/edit.html b/frontend/app/pages/query-snippets/edit.html new file mode 100644 index 00000000..9565c48e --- /dev/null +++ b/frontend/app/pages/query-snippets/edit.html @@ -0,0 +1,29 @@ + +
+
+
+ + +
+ +
+ + +
+ +
+ +
{{$ctrl.snippet.snippet}}
+
+
+ +
+ + +
+ + Created by: {{$ctrl.snippet.user.name}} + +
+
+
diff --git a/frontend/app/pages/query-snippets/edit.js b/frontend/app/pages/query-snippets/edit.js new file mode 100644 index 00000000..acb52a05 --- /dev/null +++ b/frontend/app/pages/query-snippets/edit.js @@ -0,0 +1,64 @@ +import 'brace/mode/snippets'; +import template from './edit.html'; + +function SnippetCtrl($routeParams, $http, $location, toastr, currentUser, Events, QuerySnippet) { + // $scope.$parent.pageTitle = 'Query Snippets'; + this.snippetId = $routeParams.snippetId; + Events.record(currentUser, 'view', 'query_snippet', this.snippetId); + + this.editorOptions = { + mode: 'snippets', + advanced: { + behavioursEnabled: true, + enableSnippets: false, + autoScrollEditorIntoView: true, + }, + onLoad(editor) { + editor.$blockScrolling = Infinity; + editor.getSession().setUseWrapMode(true); + editor.setShowPrintMargin(false); + }, + }; + + this.saveChanges = () => { + this.snippet.$save((snippet) => { + toastr.success('Saved.'); + if (this.snippetId === 'new') { + $location.path(`/query_snippets/${snippet.id}`).replace(); + } + }, () => { + toastr.error('Failed saving snippet.'); + }); + }; + + this.delete = () => { + this.snippet.$delete(() => { + $location.path('/query_snippets'); + toastr.sucess('Query snippet deleted.'); + }, () => { + toastr.error('Failed deleting query snippet.'); + }); + }; + + if (this.snippetId === 'new') { + this.snippet = new QuerySnippet({ description: '' }); + this.canEdit = true; + } else { + this.snippet = QuerySnippet.get({ id: this.snippetId }, (snippet) => { + this.canEdit = currentUser.canEdit(snippet); + }); + } +} + +export default function (ngModule) { + ngModule.component('snippetPage', { + template, + controller: SnippetCtrl, + }); + + return { + '/query_snippets/:snippetId': { + template: '', + }, + }; +} diff --git a/frontend/app/pages/query-snippets/index.js b/frontend/app/pages/query-snippets/index.js new file mode 100644 index 00000000..8f51f294 --- /dev/null +++ b/frontend/app/pages/query-snippets/index.js @@ -0,0 +1,8 @@ +import registerList from './list'; +import registerEdit from './edit'; + +export default function (ngModule) { + const routes = Object.assign({}, registerList(ngModule), + registerEdit(ngModule)); + return routes; +} diff --git a/frontend/app/pages/query-snippets/list.html b/frontend/app/pages/query-snippets/list.html new file mode 100644 index 00000000..a3d105b0 --- /dev/null +++ b/frontend/app/pages/query-snippets/list.html @@ -0,0 +1,41 @@ + +
+
+

+ New Snippet +

+ + + + + + + + + + + + + + + + + + + + +
TriggerDescriptionSnippetCreated ByUpdated At
+ {{row.trigger}} + + {{row.description}} + + {{row.snippet}} + + {{row.user.name}} + + +
+ +
+
+
diff --git a/frontend/app/pages/query-snippets/list.js b/frontend/app/pages/query-snippets/list.js new file mode 100644 index 00000000..d7a3b853 --- /dev/null +++ b/frontend/app/pages/query-snippets/list.js @@ -0,0 +1,25 @@ +import { Paginator } from '../../utils'; +import template from './list.html'; + +function SnippetsCtrl($location, currentUser, Events, QuerySnippet) { + Events.record(currentUser, 'view', 'page', 'query_snippets'); + // $scope.$parent.pageTitle = 'Query Snippets'; + + this.snippets = new Paginator([], { itemsPerPage: 20 }); + QuerySnippet.query((snippets) => { + this.snippets.updateRows(snippets); + }); +} + +export default function (ngModule) { + ngModule.component('snippetsListPage', { + template, + controller: SnippetsCtrl, + }); + + return { + '/query_snippets': { + template: '', + }, + }; +} diff --git a/frontend/app/utils/index.js b/frontend/app/utils/index.js new file mode 100644 index 00000000..eb5749b0 --- /dev/null +++ b/frontend/app/utils/index.js @@ -0,0 +1 @@ +export { default as Paginator } from './paginator'; diff --git a/frontend/app/utils/paginator.js b/frontend/app/utils/paginator.js new file mode 100644 index 00000000..4d28c1fa --- /dev/null +++ b/frontend/app/utils/paginator.js @@ -0,0 +1,27 @@ +export default class Paginator { + constructor(rows, { page = 1, itemsPerPage = 20, totalCount = undefined } = {}) { + this.page = page; + this.itemsPerPage = itemsPerPage; + this.updateRows(rows, totalCount); + } + + setPage(page) { + this.page = page; + } + + getPageRows() { + const first = this.itemsPerPage * (this.page - 1); + const last = this.itemsPerPage * (this.page); + + return this.rows.slice(first, last); + } + + updateRows(rows, totalCount = undefined) { + this.rows = rows; + if (this.rows) { + this.totalCount = totalCount || rows.length; + } else { + this.totalCount = 0; + } + } +} diff --git a/frontend/package.json b/frontend/package.json index 423e749f..ee73826c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,11 +25,13 @@ "angular-route": "^1.5.8", "angular-sanitize": "^1.5.8", "angular-toastr": "^2.1.1", + "angular-ui-ace": "^0.2.3", "angular-ui-bootstrap": "^2.2.0", "babel-core": "^6.18.0", "babel-loader": "^6.2.7", "babel-preset-es2015": "^6.18.0", "babel-preset-stage-2": "^6.18.0", + "brace": "^0.9.0", "css-loader": "^0.25.0", "d3": "^3.5.17", "debug": "^2.2.0",