diff --git a/client/app/admin/admin.js b/client/app/admin/admin.js deleted file mode 100644 index d82e625..0000000 --- a/client/app/admin/admin.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -angular.module('manticoreApp') - .config(function ($stateProvider) { - $stateProvider - .state('admin', { - url: '/admin', - templateUrl: 'app/admin/admin.html', - controller: 'AdminCtrl' - }); - }); \ No newline at end of file diff --git a/client/app/admin/admin.styl b/client/app/admin/admin.styl deleted file mode 100644 index d57e50d..0000000 --- a/client/app/admin/admin.styl +++ /dev/null @@ -1,2 +0,0 @@ -.trash - color rgb(209, 91, 71) \ No newline at end of file diff --git a/client/app/app.styl b/client/app/app.styl index e170c9c..1293ce7 100644 --- a/client/app/app.styl +++ b/client/app/app.styl @@ -1,56 +1,61 @@ @import "font-awesome/css/font-awesome.css" @import "bootstrap/dist/css/bootstrap.css" // // Bootstrap Fonts // @font-face font-family: 'Glyphicons Halflings' src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot') src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.woff') format('woff'), url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); // // Font Awesome Fonts // @font-face font-family: 'FontAwesome' src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?v=4.1.0') src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'), url('../bower_components/font-awesome/fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'), url('../bower_components/font-awesome/fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'), url('../bower_components/font-awesome/fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg'); font-weight: normal font-style: normal // // App-wide Styles // .browsehappy background #ccc color #000 margin 0.2em 0 padding 0.2em 0 body background-color whitesmoke +.text-strong + font-weight bold + // Component styles are injected through grunt // injector @import 'account/login/login.styl'; -@import 'admin/admin.styl'; @import 'editor/editor.styl'; @import 'main/main.styl'; +@import 'templates/templates.styl'; +@import 'users/users.styl'; @import 'documentList/documentList.styl'; @import 'exportButton/exportButton.styl'; @import 'import/import.styl'; +@import 'labelEditor/labelEditor.styl'; @import 'modal/modal.styl'; @import 'navbar/navbar.styl'; @import 'titleEditor/titleEditor.styl'; @import 'wodo/editor.styl'; // endinjector diff --git a/client/app/templates/templates.controller.js b/client/app/templates/templates.controller.js new file mode 100644 index 0000000..8ba03af --- /dev/null +++ b/client/app/templates/templates.controller.js @@ -0,0 +1,43 @@ +'use strict'; + +angular.module('manticoreApp') + .controller('TemplatesCtrl', function ($scope, $http, Auth, FileUploader) { + var uploader = new FileUploader({ + url: '/api/templates/upload', + headers: { + 'Authorization': 'Bearer ' + Auth.getToken() + }, + removeAfterUpload: true, + autoUpload: true, + onCompleteAll: function () { + // Wait a little before firing this event, as the upload may not + // be accessible from MongoDB immediately + window.setTimeout(function () { + uploader.clearQueue(); + refresh(); + }, 1000); + } + }); + + $scope.uploader = uploader; + + $scope.update = function (template) { + $http.put('/api/templates/' + template._id, template); + }; + $scope.delete = function(template) { + $http.delete('/api/templates/' + template._id); + angular.forEach($scope.templates, function(t, i) { + if (t === template) { + $scope.templates.splice(i, 1); + } + }); + }; + + $scope.$watch('templates') + function refresh() { + $http.get('/api/templates').success(function (templates) { + $scope.templates = templates; + }); + } + refresh(); + }); diff --git a/client/app/templates/templates.jade b/client/app/templates/templates.jade new file mode 100644 index 0000000..7d96fdc --- /dev/null +++ b/client/app/templates/templates.jade @@ -0,0 +1,31 @@ +div(ng-include='"components/navbar/navbar.html"') +.container + div.btn.btn-danger.upload-button( + type='button' + uploader='uploader' + ng-disabled='uploader.isUploading' + ) + input( + type='file' + accept='.odt' + nv-file-select='' + uploader='uploader' + ) + span.glyphicon.glyphicon-upload + | {{uploader.isUploading ? 'Uploading...' : 'Upload'}} + + ul.list-group + li.list-group-item(ng-repeat='template in templates') + label-editor.text-strong( + ng-model='template.title' + ng-change='update(template)' + placeholder='Untitled template' + ) + br + label-editor.text-muted( + ng-model='template.description' + ng-change='update(template)' + placeholder='No description' + ) + a.trash(ng-click='delete(template)') + span.glyphicon.glyphicon-trash.pull-right diff --git a/client/app/templates/templates.js b/client/app/templates/templates.js new file mode 100644 index 0000000..6572cd3 --- /dev/null +++ b/client/app/templates/templates.js @@ -0,0 +1,11 @@ +'use strict'; + +angular.module('manticoreApp') + .config(function ($stateProvider) { + $stateProvider + .state('templates', { + url: '/templates', + templateUrl: 'app/templates/templates.html', + controller: 'TemplatesCtrl' + }); + }); \ No newline at end of file diff --git a/client/app/templates/templates.styl b/client/app/templates/templates.styl new file mode 100644 index 0000000..fea8563 --- /dev/null +++ b/client/app/templates/templates.styl @@ -0,0 +1,18 @@ +.upload-button + position relative + overflow hidden + display block + width 100px + margin-left auto + margin-right auto + margin-bottom 15px + input[type=file] + opacity 0; + position absolute + top 0 + bottom 0 + outline none + cursor inherit + display block + width 100% + height: 100% diff --git a/client/app/admin/admin.controller.js b/client/app/users/users.controller.js similarity index 84% rename from client/app/admin/admin.controller.js rename to client/app/users/users.controller.js index b2d24af..31fec48 100644 --- a/client/app/admin/admin.controller.js +++ b/client/app/users/users.controller.js @@ -1,17 +1,17 @@ 'use strict'; angular.module('manticoreApp') - .controller('AdminCtrl', function ($scope, $http, Auth, User) { + .controller('UsersCtrl', function ($scope, $http, Auth, User) { // Use the User $resource to fetch all users $scope.users = User.query(); $scope.delete = function(user) { User.remove({ id: user._id }); angular.forEach($scope.users, function(u, i) { if (u === user) { $scope.users.splice(i, 1); } }); }; }); diff --git a/client/app/admin/admin.jade b/client/app/users/users.jade similarity index 61% rename from client/app/admin/admin.jade rename to client/app/users/users.jade index fd80a0b..2b18af8 100644 --- a/client/app/admin/admin.jade +++ b/client/app/users/users.jade @@ -1,11 +1,9 @@ div(ng-include='"components/navbar/navbar.html"') .container - p - | The delete user and user index api routes are restricted to users with the 'admin' role. ul.list-group li.list-group-item(ng-repeat='user in users') strong {{user.name}} br span.text-muted {{user.email}} a.trash(ng-click='delete(user)') - span.glyphicon.glyphicon-trash.pull-right \ No newline at end of file + span.glyphicon.glyphicon-trash.pull-right diff --git a/client/app/users/users.js b/client/app/users/users.js new file mode 100644 index 0000000..8564f3e --- /dev/null +++ b/client/app/users/users.js @@ -0,0 +1,11 @@ +'use strict'; + +angular.module('manticoreApp') + .config(function ($stateProvider) { + $stateProvider + .state('users', { + url: '/users', + templateUrl: 'app/users/users.html', + controller: 'UsersCtrl' + }); + }); diff --git a/client/app/users/users.styl b/client/app/users/users.styl new file mode 100644 index 0000000..cdc0c97 --- /dev/null +++ b/client/app/users/users.styl @@ -0,0 +1,3 @@ +.trash + color rgb(209, 91, 71) + cursor pointer diff --git a/client/components/import/import.jade b/client/components/import/import.jade index bb34755..0a65d64 100644 --- a/client/components/import/import.jade +++ b/client/components/import/import.jade @@ -1,80 +1,80 @@ div.import-box(ng-controller='ImportCtrl') .row.upload-widget - div.upload-button( + div.upload-area( type='button' nv-file-over='' uploader='uploader' ) input( type='file' accept='.odt' nv-file-select='' uploader='uploader' multiple ) | Click to select (.odt) files to upload, or drag them here .row(ng-hide='!uploader.queue.length') table.table thead tr th(width='50%') Name th(ng-show='uploader.isHTML5') Size th(ng-show='uploader.isHTML5') Progress th.status Status th.actions Actions tbody tr(ng-repeat='item in uploader.queue') td.name-cell span(tooltip='{{item.file.name}}' data-container='body') | {{item.file.name}} td(ng-show='uploader.isHTML5' nowrap) | {{item.file.size/1024/1024|number:2}} MB td(ng-show='uploader.isHTML5') progressbar(value='item.progress') {{item.progress}}% td.status.text-center span(ng-show='item.isSuccess') i.glyphicon.glyphicon-ok span(ng-show='item.isCancel') i.glyphicon.glyphicon-ban-circle span(ng-show='item.isError') i.glyphicon.glyphicon-remove td.actions(nowrap) button.btn.btn-success.btn-xs( ng-click='item.upload()' ng-disabled='item.isReady || item.isUploading || item.isSuccess' ) i.glyphicon.glyphicon-upload button.btn.btn-warning.btn-xs( ng-click='item.cancel()' ng-disabled='!item.isUploading' ) i.glyphicon.glyphicon-ban-circle button.btn.btn-danger.btn-xs( ng-click='item.remove()' ) i.glyphicon.glyphicon-trash //- div.progress-overall progressbar(value='uploader.progress') {{uploader.progress}}% div.btn-group.btn-group-justified div.btn.btn-success.btn-s( type='button' ng-click='uploader.uploadAll()' ng-disabled='!uploader.getNotUploadedItems().length' ) span.glyphicon.glyphicon-upload | Upload all div.btn.btn-warning.btn-s( type='button' ng-click='uploader.cancelAll()' ng-disabled='!uploader.isUploading' ) span.glyphicon.glyphicon-ban-circle | Cancel all div.btn.btn-danger.btn-s( type='button' ng-click='uploader.removeAll()' ng-disabled='!uploader.queue.length' ) span.glyphicon.glyphicon-trash | Remove all diff --git a/client/components/import/import.styl b/client/components/import/import.styl index 993713e..dc310b2 100644 --- a/client/components/import/import.styl +++ b/client/components/import/import.styl @@ -1,55 +1,55 @@ .import-box width 400px .row margin: 0 .upload-widget margin-left 5px !important margin-right 5px !important -.upload-button +.upload-area width 100% position relative overflow hidden padding 20px border 2px dashed lightgray border-radius 5px cursor pointer text-align center background-color #eee input[type=file] opacity 0; position absolute top 0 bottom 0 outline none cursor inherit display block width 100% height: 100% &:hover border 2px dashed red background-color #ddd td .progress margin-bottom 0 .progress-overall progress padding-left 8px padding-right 8px th, td border-bottom-width 1px !important .table .actions display none .status display none .name-cell max-width 0 word-wrap break-word text-overflow ellipsis overflow hidden white-space nowrap diff --git a/client/components/labelEditor/labelEditor.directive.js b/client/components/labelEditor/labelEditor.directive.js new file mode 100644 index 0000000..463a63b --- /dev/null +++ b/client/components/labelEditor/labelEditor.directive.js @@ -0,0 +1,25 @@ +'use strict'; + +angular.module('manticoreApp') +.directive('labelEditor', function ($timeout) { + return { + templateUrl: 'components/labelEditor/labelEditor.html', + restrict: 'E', + scope: { + model: '=ngModel' + }, + link: function (scope, element, attrs) { + scope.placeholder = attrs.placeholder; + scope.handleEnterKey = function ($event) { + if ($event.keyCode === 13) { + $event.target.blur(); + } + }; + scope.change = function () { + $timeout(function () { + scope.$parent.$eval(attrs.ngChange); + }); + }; + } + }; +}); diff --git a/client/components/labelEditor/labelEditor.jade b/client/components/labelEditor/labelEditor.jade new file mode 100644 index 0000000..f8927d8 --- /dev/null +++ b/client/components/labelEditor/labelEditor.jade @@ -0,0 +1,12 @@ +input.label-editor( + ng-model='model' + ng-model-options='{updateOn: "blur"}' + ng-change='change(model)' + ng-keypress='handleEnterKey($event)' + ng-mouseenter='hover = true' + ng-mouseleave='hover = false' + ng-focus='focus = true' + ng-blur='focus = false' + placeholder='{{placeholder}}' +) +span.glyphicon.glyphicon-pencil.label-editor-pencil(ng-show='hover && !focus') diff --git a/client/components/labelEditor/labelEditor.styl b/client/components/labelEditor/labelEditor.styl new file mode 100644 index 0000000..4ba93ed --- /dev/null +++ b/client/components/labelEditor/labelEditor.styl @@ -0,0 +1,17 @@ +.label-editor + border none + outline none + text-overflow ellipsis + padding-left 5px + margin-left -5px + width 100% + max-width calc(100% - 50px) + &:hover + background-color #eeeeee + &:focus + background-color #eeeeee + width 100% + +.label-editor-pencil + margin-left 5px + color gray diff --git a/client/components/navbar/navbar.jade b/client/components/navbar/navbar.jade index 3fe98cb..9db98be 100644 --- a/client/components/navbar/navbar.jade +++ b/client/components/navbar/navbar.jade @@ -1,37 +1,40 @@ div.navbar.navbar-default.navbar-static-top(ng-controller='NavbarCtrl') div.container div.navbar-header button.navbar-toggle(type='button', ng-click='isCollapsed = !isCollapsed') span.sr-only Toggle navigation span.icon-bar span.icon-bar span.icon-bar a.navbar-brand(href='/') manticore div#navbar-main.navbar-collapse.collapse(collapse='isCollapsed') ul.nav.navbar-nav - li(ng-show='isAdmin()', ng-class='{active: isActive("/admin")}') - a(href='/admin') Admin + li(ng-show='isAdmin()', ng-class='{active: isActive("/users")}') + a(ui-sref='users') Users + + li(ng-show='isAdmin()', ng-class='{active: isActive("/templates")}') + a(ui-sref='templates') Templates li.dropdown(ng-show='isLoggedIn() && isActive("/")' dropdown auto-close='disabled') a.dropdown-toggle(href='#' dropdown-toggle role='button') | Import span.caret div.dropdown-menu(ng-include='"components/import/import.html"') ul.nav.navbar-nav.navbar-right li(ng-hide='isLoggedIn()', ng-class='{active: isActive("/signup")}') a(href='/signup') Sign up li(ng-hide='isLoggedIn()', ng-class='{active: isActive("/login")}') a(href='/login') Login li(ng-show='isLoggedIn()') p.navbar-text Hello {{ getCurrentUser().name }} li(ng-show='isLoggedIn()', ng-class='{active: isActive("/settings")}') a(href='/settings') span.glyphicon.glyphicon-cog li(ng-show='isLoggedIn()', ng-class='{active: isActive("/logout")}') a(href='', ng-click='logout()') Logout diff --git a/client/index.html b/client/index.html index 5c637a0..2808f8e 100644 --- a/client/index.html +++ b/client/index.html @@ -1,87 +1,90 @@