mirror of
				https://github.com/NginxProxyManager/nginx-proxy-manager.git
				synced 2025-10-31 07:43:33 +00:00 
			
		
		
		
	Frontend user list and modal dialog fixes
This commit is contained in:
		| @@ -163,17 +163,22 @@ module.exports = { | |||||||
|         }, |         }, | ||||||
|  |  | ||||||
|         /** |         /** | ||||||
|          * @param   {Integer}  [offset] |  | ||||||
|          * @param   {Integer}  [limit] |  | ||||||
|          * @param   {String}   [sort] |  | ||||||
|          * @param   {Array}    [expand] |          * @param   {Array}    [expand] | ||||||
|          * @param   {String}   [query] |          * @param   {String}   [query] | ||||||
|          * @returns {Promise} |          * @returns {Promise} | ||||||
|          */ |          */ | ||||||
|         getAll: function (offset, limit, sort, expand, query) { |         getAll: function (expand, query) { | ||||||
|             return fetch('get', 'users?offset=' + (offset ? offset : 0) + '&limit=' + (limit ? limit : 20) + (sort ? '&sort=' + sort : '') + |             let params = []; | ||||||
|                 (typeof expand === 'object' && expand !== null && expand.length ? '&expand=' + makeExpansionString(expand) : '') + |  | ||||||
|                 (typeof query === 'string' ? '&query=' + query : '')); |             if (typeof expand === 'object' && expand !== null && expand.length) { | ||||||
|  |                 params.push('expand=' + makeExpansionString(expand)); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (typeof query === 'string') { | ||||||
|  |                 params.push('query=' + query); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return fetch('get', 'users' + (params.length ? '?' + params.join('&') : '')); | ||||||
|         }, |         }, | ||||||
|  |  | ||||||
|         /** |         /** | ||||||
|   | |||||||
| @@ -26,34 +26,30 @@ module.exports = { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Users |      * Users | ||||||
|      * |  | ||||||
|      * @param {Number}  [offset] |  | ||||||
|      * @param {Number}  [limit] |  | ||||||
|      * @param {String}  [sort] |  | ||||||
|      */ |      */ | ||||||
|     showUsers: function (offset, limit, sort) { |     showUsers: function () { | ||||||
|         /* |  | ||||||
|         let controller = this; |         let controller = this; | ||||||
|         if (Cache.User.isAdmin()) { |         if (Cache.User.isAdmin()) { | ||||||
|             require(['./main', './users/main'], (App, View) => { |             require(['./main', './users/main'], (App, View) => { | ||||||
|                 controller.navigate('/users'); |                 controller.navigate('/users'); | ||||||
|                 App.UI.showMainLoading(); |                 App.UI.showAppContent(new View()); | ||||||
|                 let view = new View({ |  | ||||||
|                     sort:   (typeof sort !== 'undefined' && sort ? sort : Cache.Session.Users.sort), |  | ||||||
|                     offset: (typeof offset !== 'undefined' ? offset : Cache.Session.Users.offset), |  | ||||||
|                     limit:  (typeof limit !== 'undefined' && limit ? limit : Cache.Session.Users.limit) |  | ||||||
|                 }); |  | ||||||
|  |  | ||||||
|                 view.on('loaded', function () { |  | ||||||
|                     App.UI.hideMainLoading(); |  | ||||||
|                 }); |  | ||||||
|  |  | ||||||
|                 App.UI.showAppContent(view); |  | ||||||
|             }); |             }); | ||||||
|         } else { |         } else { | ||||||
|             this.showRules(); |             this.showDashboard(); | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * User Form | ||||||
|  |      * | ||||||
|  |      * @param model | ||||||
|  |      */ | ||||||
|  |     showUserForm: function (model) { | ||||||
|  |         if (Cache.User.isAdmin()) { | ||||||
|  |             require(['./main', './user/form'], function (App, View) { | ||||||
|  |                 App.UI.showModalDialog(new View({model: model})); | ||||||
|  |             }); | ||||||
|         } |         } | ||||||
|         */ |  | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -12,13 +12,17 @@ const UI         = require('./ui/main'); | |||||||
|  |  | ||||||
| const App = Mn.Application.extend({ | const App = Mn.Application.extend({ | ||||||
|  |  | ||||||
|     region:     '#app', |  | ||||||
|     Cache:      Cache, |     Cache:      Cache, | ||||||
|     Api:        Api, |     Api:        Api, | ||||||
|     UI:         null, |     UI:         null, | ||||||
|     Controller: Controller, |     Controller: Controller, | ||||||
|     version:    null, |     version:    null, | ||||||
|  |  | ||||||
|  |     region: { | ||||||
|  |         el:             '#app', | ||||||
|  |         replaceElement: true | ||||||
|  |     }, | ||||||
|  |  | ||||||
|     onStart: function (app, options) { |     onStart: function (app, options) { | ||||||
|         console.log('Welcome to Nginx Proxy Manager'); |         console.log('Welcome to Nginx Proxy Manager'); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,6 +9,6 @@ | |||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|     <div class="col-12 col-lg-auto mt-3 mt-lg-0 text-center"> |     <div class="col-12 col-lg-auto mt-3 mt-lg-0 text-center"> | ||||||
|         v<%- getVersion() %> © 2018 <a href="https://jc21.com?utm_source=docker-registry-ui" target="_blank">jc21.com</a>. Theme by <a href="https://github.com/tabler/tabler?utm_source=docker-registry-ui" target="_blank">Tabler</a> |         v<%- getVersion() %> © 2018 <a href="https://jc21.com?utm_source=nginx-proxy-manager" target="_blank">jc21.com</a>. Theme by <a href="https://tabler.github.io/?utm_source=nginx-proxy-manager" target="_blank">Tabler</a> | ||||||
|     </div> |     </div> | ||||||
| </div> | </div> | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| <div class="container"> | <div class="container"> | ||||||
|     <div class="d-flex"> |     <div class="d-flex"> | ||||||
|         <a class="navbar-brand" href="/"> |         <a class="navbar-brand" href="/"> | ||||||
|             <img src="/images/favicons/favicon-32x32.png" border="0">   Docker Registry |             <img src="/images/favicons/favicon-32x32.png" border="0">   Nginx Proxy Manager | ||||||
|         </a> |         </a> | ||||||
|  |  | ||||||
|         <div class="d-flex order-lg-2 ml-auto"> |         <div class="d-flex order-lg-2 ml-auto"> | ||||||
| @@ -9,7 +9,7 @@ | |||||||
|                 <a href="#" class="nav-link pr-0 leading-none" data-toggle="dropdown"> |                 <a href="#" class="nav-link pr-0 leading-none" data-toggle="dropdown"> | ||||||
|                     <span class="avatar" style="background-image: url(<%- getUserField('avatar', '/images/default-avatar.jpg') %>)"></span> |                     <span class="avatar" style="background-image: url(<%- getUserField('avatar', '/images/default-avatar.jpg') %>)"></span> | ||||||
|                     <span class="ml-2 d-none d-lg-block"> |                     <span class="ml-2 d-none d-lg-block"> | ||||||
|                       <span class="text-default"><%- getUserField('name', 'Unknown User') %></span> |                       <span class="text-default"><%- getUserField('nickname', null) || getUserField('name', 'Unknown User') %></span> | ||||||
|                       <small class="text-muted d-block mt-1"><%- getRole() %></small> |                       <small class="text-muted d-block mt-1"><%- getRole() %></small> | ||||||
|                     </span> |                     </span> | ||||||
|                 </a> |                 </a> | ||||||
|   | |||||||
| @@ -1,5 +1,4 @@ | |||||||
| <div class="page-main"> | <div class="page-main"> | ||||||
|  |  | ||||||
|     <div class="header" id="header"> |     <div class="header" id="header"> | ||||||
|         <!-- Header View --> |         <!-- Header View --> | ||||||
|     </div> |     </div> | ||||||
| @@ -15,4 +14,6 @@ | |||||||
|  |  | ||||||
| <footer class="footer"> | <footer class="footer"> | ||||||
|     <!-- Footer View --> |     <!-- Footer View --> | ||||||
| </footer> | </footer> | ||||||
|  |  | ||||||
|  | <div class="modal fade" id="modal-dialog" tabindex="-1" role="dialog" aria-hidden="true"></div> | ||||||
|   | |||||||
| @@ -8,8 +8,16 @@ const FooterView = require('./footer/main'); | |||||||
| const Cache      = require('../cache'); | const Cache      = require('../cache'); | ||||||
|  |  | ||||||
| module.exports = Mn.View.extend({ | module.exports = Mn.View.extend({ | ||||||
|     className: 'page', |     id:          'app', | ||||||
|     template:  template, |     className:   'page', | ||||||
|  |     template:    template, | ||||||
|  |     modal_setup: false, | ||||||
|  |  | ||||||
|  |     modal: null, | ||||||
|  |  | ||||||
|  |     ui: { | ||||||
|  |         modal: '#modal-dialog' | ||||||
|  |     }, | ||||||
|  |  | ||||||
|     regions: { |     regions: { | ||||||
|         header_region:      { |         header_region:      { | ||||||
| @@ -21,13 +29,60 @@ module.exports = Mn.View.extend({ | |||||||
|             replaceElement: true |             replaceElement: true | ||||||
|         }, |         }, | ||||||
|         footer_region:      '.footer', |         footer_region:      '.footer', | ||||||
|         app_content_region: '#app-content' |         app_content_region: '#app-content', | ||||||
|  |         modal_region:       '#modal-dialog' | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param {Object}  view | ||||||
|  |      */ | ||||||
|     showAppContent: function (view) { |     showAppContent: function (view) { | ||||||
|         this.showChildView('app_content_region', view); |         this.showChildView('app_content_region', view); | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param {Object}    view | ||||||
|  |      * @param {Function}  [show_callback] | ||||||
|  |      * @param {Function}  [shown_callback] | ||||||
|  |      */ | ||||||
|  |     showModalDialog: function (view, show_callback, shown_callback) { | ||||||
|  |         this.showChildView('modal_region', view); | ||||||
|  |         let modal = this.getRegion('modal_region').$el.modal('show'); | ||||||
|  |  | ||||||
|  |         modal.on('hidden.bs.modal', function (/*e*/) { | ||||||
|  |             if (show_callback) { | ||||||
|  |                 modal.off('show.bs.modal', show_callback); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (shown_callback) { | ||||||
|  |                 modal.off('shown.bs.modal', shown_callback); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             modal.off('hidden.bs.modal'); | ||||||
|  |             view.destroy(); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         if (show_callback) { | ||||||
|  |             modal.on('show.bs.modal', show_callback); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (shown_callback) { | ||||||
|  |             modal.on('shown.bs.modal', shown_callback); | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * | ||||||
|  |      * @param {Function}  [hidden_callback] | ||||||
|  |      */ | ||||||
|  |     closeModal: function (hidden_callback) { | ||||||
|  |         let modal = this.getRegion('modal_region').$el.modal('hide'); | ||||||
|  |  | ||||||
|  |         if (hidden_callback) { | ||||||
|  |             modal.on('hidden.bs.modal', hidden_callback); | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|     onRender: function () { |     onRender: function () { | ||||||
|         this.showChildView('header_region', new HeaderView({ |         this.showChildView('header_region', new HeaderView({ | ||||||
|             model: Cache.User |             model: Cache.User | ||||||
| @@ -40,5 +95,6 @@ module.exports = Mn.View.extend({ | |||||||
|     reset: function () { |     reset: function () { | ||||||
|         this.getRegion('header_region').reset(); |         this.getRegion('header_region').reset(); | ||||||
|         this.getRegion('footer_region').reset(); |         this.getRegion('footer_region').reset(); | ||||||
|  |         this.getRegion('modal_region').reset(); | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -3,10 +3,15 @@ | |||||||
|         <div class="col-lg order-lg-first"> |         <div class="col-lg order-lg-first"> | ||||||
|             <ul class="nav nav-tabs border-0 flex-column flex-lg-row"> |             <ul class="nav nav-tabs border-0 flex-column flex-lg-row"> | ||||||
|                 <li class="nav-item"> |                 <li class="nav-item"> | ||||||
|                     <a href="../index.html" class="nav-link"><i class="fe fe-home"></i> Home</a> |                     <a href="/" class="nav-link"><i class="fe fe-home"></i> Home</a> | ||||||
|                 </li> |                 </li> | ||||||
|  |                 <% if (showUsers()) { %> | ||||||
|                 <li class="nav-item"> |                 <li class="nav-item"> | ||||||
|                     <a href="javascript:void(0)" class="nav-link" data-toggle="dropdown"><i class="fe fe-box"></i> Interface</a> |                     <a href="/users" class="nav-link"><i class="fe fe-users"></i> Users</a> | ||||||
|  |                 </li> | ||||||
|  |                 <% } %> | ||||||
|  |                 <li class="nav-item"> | ||||||
|  |                     <a href="#" class="nav-link" data-toggle="dropdown"><i class="fe fe-box"></i> Interface</a> | ||||||
|                     <div class="dropdown-menu dropdown-menu-arrow"> |                     <div class="dropdown-menu dropdown-menu-arrow"> | ||||||
|                         <a href="../cards.html" class="dropdown-item ">Cards design</a> |                         <a href="../cards.html" class="dropdown-item ">Cards design</a> | ||||||
|                         <a href="../charts.html" class="dropdown-item ">Charts</a> |                         <a href="../charts.html" class="dropdown-item ">Charts</a> | ||||||
| @@ -14,7 +19,7 @@ | |||||||
|                     </div> |                     </div> | ||||||
|                 </li> |                 </li> | ||||||
|                 <li class="nav-item dropdown"> |                 <li class="nav-item dropdown"> | ||||||
|                     <a href="javascript:void(0)" class="nav-link" data-toggle="dropdown"><i class="fe fe-calendar"></i> Components</a> |                     <a href="#" class="nav-link" data-toggle="dropdown"><i class="fe fe-calendar"></i> Components</a> | ||||||
|                     <div class="dropdown-menu dropdown-menu-arrow"> |                     <div class="dropdown-menu dropdown-menu-arrow"> | ||||||
|                         <a href="../maps.html" class="dropdown-item ">Maps</a> |                         <a href="../maps.html" class="dropdown-item ">Maps</a> | ||||||
|                         <a href="../icons.html" class="dropdown-item ">Icons</a> |                         <a href="../icons.html" class="dropdown-item ">Icons</a> | ||||||
| @@ -24,7 +29,7 @@ | |||||||
|                     </div> |                     </div> | ||||||
|                 </li> |                 </li> | ||||||
|                 <li class="nav-item dropdown"> |                 <li class="nav-item dropdown"> | ||||||
|                     <a href="javascript:void(0)" class="nav-link" data-toggle="dropdown"><i class="fe fe-file"></i> Pages</a> |                     <a href="#" class="nav-link" data-toggle="dropdown"><i class="fe fe-file"></i> Pages</a> | ||||||
|                     <div class="dropdown-menu dropdown-menu-arrow"> |                     <div class="dropdown-menu dropdown-menu-arrow"> | ||||||
|                         <a href="../profile.html" class="dropdown-item ">Profile</a> |                         <a href="../profile.html" class="dropdown-item ">Profile</a> | ||||||
|                         <a href="../login.html" class="dropdown-item ">Login</a> |                         <a href="../login.html" class="dropdown-item ">Login</a> | ||||||
|   | |||||||
| @@ -1,10 +1,39 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
|  |  | ||||||
| const Mn       = require('backbone.marionette'); | const $          = require('jquery'); | ||||||
| const template = require('./main.ejs'); | const Mn         = require('backbone.marionette'); | ||||||
|  | const Controller = require('../../controller'); | ||||||
|  | const Cache      = require('../../cache'); | ||||||
|  | const template   = require('./main.ejs'); | ||||||
|  |  | ||||||
| module.exports = Mn.View.extend({ | module.exports = Mn.View.extend({ | ||||||
|     id:        'menu', |     id:        'menu', | ||||||
|     className: 'header collapse d-lg-flex p-0', |     className: 'header collapse d-lg-flex p-0', | ||||||
|     template:  template |     template:  template, | ||||||
|  |  | ||||||
|  |     ui: { | ||||||
|  |         link: 'a' | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     events: { | ||||||
|  |         'click @ui.link': function (e) { | ||||||
|  |             e.preventDefault(); | ||||||
|  |             let href = $(e.currentTarget).attr('href'); | ||||||
|  |  | ||||||
|  |             switch (href) { | ||||||
|  |                 case '/': | ||||||
|  |                     Controller.showDashboard(); | ||||||
|  |                     break; | ||||||
|  |                 case '/users': | ||||||
|  |                     Controller.showUsers(); | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     templateContext: { | ||||||
|  |         showUsers: function () { | ||||||
|  |             return Cache.User.isAdmin(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| }); | }); | ||||||
|   | |||||||
							
								
								
									
										53
									
								
								src/frontend/js/app/user/form.ejs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/frontend/js/app/user/form.ejs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | <div class="modal-content"> | ||||||
|  |     <div class="modal-header"> | ||||||
|  |         <h5 class="modal-title"><% if (typeof id !== 'undefined') { %>Edit<% } else { %>New<% } %> User</h5> | ||||||
|  |         <button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button> | ||||||
|  |     </div> | ||||||
|  |     <div class="modal-body"> | ||||||
|  |         <form> | ||||||
|  |             <div class="row"> | ||||||
|  |                 <div class="col-sm-6 col-md-6"> | ||||||
|  |                     <div class="form-group"> | ||||||
|  |                         <label class="form-label">Full Name <span class="form-required">*</span></label> | ||||||
|  |                         <input name="name" type="text" class="form-control" placeholder="Joe Citizen" value="<%- name %>" required> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="col-sm-6 col-md-6"> | ||||||
|  |                     <div class="form-group"> | ||||||
|  |                         <label class="form-label">Nickname</label> | ||||||
|  |                         <input name="nickname" type="text" class="form-control" placeholder="Joe" value="<%- nickname %>"> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="col-sm-12 col-md-12"> | ||||||
|  |                     <div class="form-group"> | ||||||
|  |                         <label class="form-label">Email <span class="form-required">*</span></label> | ||||||
|  |                         <input name="email" type="email" class="form-control" placeholder="joe@example.com" value="<%- email %>" required> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <% if (!isSelf()) { %> | ||||||
|  |                 <div class="col-sm-12 col-md-12"> | ||||||
|  |                     <div class="form-group"> | ||||||
|  |                         <div class="form-label">Switches</div> | ||||||
|  |                         <div class="custom-switches-stacked"> | ||||||
|  |                             <label class="custom-switch"> | ||||||
|  |                                 <input type="checkbox" class="custom-switch-input" name="is_admin" value="1"<%- isAdmin() ? ' checked' : '' %>> | ||||||
|  |                                 <span class="custom-switch-indicator"></span> | ||||||
|  |                                 <span class="custom-switch-description">Administrator</span> | ||||||
|  |                             </label> | ||||||
|  |                             <label class="custom-switch"> | ||||||
|  |                                 <input type="checkbox" class="custom-switch-input" name="is_disabled" value="1"<%- is_disabled ? ' checked' : '' %>> | ||||||
|  |                                 <span class="custom-switch-indicator"></span> | ||||||
|  |                                 <span class="custom-switch-description">Disabled</span> | ||||||
|  |                             </label> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <% } %> | ||||||
|  |             </div> | ||||||
|  |         </form> | ||||||
|  |     </div> | ||||||
|  |     <div class="modal-footer"> | ||||||
|  |         <button type="button" class="btn btn-secondary cancel" data-dismiss="modal">Cancel</button> | ||||||
|  |         <button type="button" class="btn btn-teal save">Save</button> | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
							
								
								
									
										99
									
								
								src/frontend/js/app/user/form.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/frontend/js/app/user/form.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | const Mn         = require('backbone.marionette'); | ||||||
|  | const template   = require('./form.ejs'); | ||||||
|  | const Controller = require('../controller'); | ||||||
|  | const Cache      = require('../cache'); | ||||||
|  | const Api        = require('../api'); | ||||||
|  | const App        = require('../main'); | ||||||
|  | const UserModel  = require('../../models/user'); | ||||||
|  |  | ||||||
|  | require('jquery-serializejson'); | ||||||
|  |  | ||||||
|  | module.exports = Mn.View.extend({ | ||||||
|  |     template:  template, | ||||||
|  |     className: 'modal-dialog', | ||||||
|  |  | ||||||
|  |     ui: { | ||||||
|  |         form:    'form', | ||||||
|  |         buttons: '.modal-footer button', | ||||||
|  |         cancel:  'button.cancel', | ||||||
|  |         save:    'button.save' | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     events: { | ||||||
|  |         /* | ||||||
|  |         'click @ui.cancel': function (e) { | ||||||
|  |             e.preventDefault(); | ||||||
|  |             App.UI.closeModal(); | ||||||
|  |         }, | ||||||
|  |         */ | ||||||
|  |  | ||||||
|  |         'submit @ui.form': function (e) { | ||||||
|  |             e.preventDefault(); | ||||||
|  |             let view = this; | ||||||
|  |             let data = this.ui.form.serializeJSON(); | ||||||
|  |  | ||||||
|  |             // Manipulate | ||||||
|  |             data.roles = []; | ||||||
|  |             if ( | ||||||
|  |                 (this.model.get('id') === Cache.User.get('id') && this.model.isAdmin()) || | ||||||
|  |                 (typeof data.is_admin !== 'undefined' && data.is_admin)) { | ||||||
|  |                 data.roles.push('admin'); | ||||||
|  |                 delete data.is_admin; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             data.is_disabled = typeof data.is_disabled !== 'undefined' ? !!data.is_disabled : false; | ||||||
|  |             this.ui.buttons.prop('disabled', true).addClass('btn-disabled'); | ||||||
|  |             let method = Api.Users.create; | ||||||
|  |  | ||||||
|  |             if (this.model.get('id')) { | ||||||
|  |                 // edit | ||||||
|  |                 method  = Api.Users.update; | ||||||
|  |                 data.id = this.model.get('id'); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             method(data) | ||||||
|  |                 .then(result => { | ||||||
|  |                     if (result.id === Cache.User.get('id')) { | ||||||
|  |                         Cache.User.set(result); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     view.model.set(result); | ||||||
|  |                     App.UI.closeModal(); | ||||||
|  |  | ||||||
|  |                     if (view.model.get('id') !== Cache.User.get('id')) { | ||||||
|  |                         Controller.showUsers(); | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |                 .catch(err => { | ||||||
|  |                     alert(err.message); | ||||||
|  |                     this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); | ||||||
|  |                 }); | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     templateContext: function () { | ||||||
|  |         let view = this; | ||||||
|  |  | ||||||
|  |         return { | ||||||
|  |             isSelf: function () { | ||||||
|  |                 return view.model.get('id') === Cache.User.get('id'); | ||||||
|  |             }, | ||||||
|  |  | ||||||
|  |             isAdmin: function () { | ||||||
|  |                 return view.model.isAdmin(); | ||||||
|  |             }, | ||||||
|  |  | ||||||
|  |             isDisabled: function () { | ||||||
|  |                 return view.model.isDisabled(); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     initialize: function (options) { | ||||||
|  |         if (typeof options.model === 'undefined' || !options.model) { | ||||||
|  |             this.model = new UserModel.Model(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }); | ||||||
							
								
								
									
										31
									
								
								src/frontend/js/app/users/list/item.ejs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/frontend/js/app/users/list/item.ejs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | <td class="text-center"> | ||||||
|  |     <div class="avatar d-block" style="background-image: url(<%- avatar || '/images/default-avatar.jpg' %>)"> | ||||||
|  |         <span class="avatar-status <%- is_disabled ? 'bg-red' : 'bg-green' %>"></span> | ||||||
|  |     </div> | ||||||
|  | </td> | ||||||
|  | <td> | ||||||
|  |     <div><%- name %></div> | ||||||
|  |     <div class="small text-muted"> | ||||||
|  |         Created: Mar 19, 2018 | ||||||
|  |     </div> | ||||||
|  | </td> | ||||||
|  | <td> | ||||||
|  |     <div><%- email %></div> | ||||||
|  | </td> | ||||||
|  | <td> | ||||||
|  |     <div><%- roles.join(', ') %></div> | ||||||
|  | </td> | ||||||
|  | <td class="text-center"> | ||||||
|  |     <div class="item-action dropdown"> | ||||||
|  |         <a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a> | ||||||
|  |         <div class="dropdown-menu dropdown-menu-right"> | ||||||
|  |             <a href="#" class="edit-user dropdown-item"><i class="dropdown-icon fe fe-edit"></i> Edit User</a> | ||||||
|  |             <a href="#" class="set-password dropdown-item"><i class="dropdown-icon fe fe-shield"></i> Set Password</a> | ||||||
|  |             <% if (!isSelf()) { %> | ||||||
|  |             <a href="#" class="login dropdown-item"><i class="dropdown-icon fe fe-log-in"></i> Sign in as User</a> | ||||||
|  |             <div class="dropdown-divider"></div> | ||||||
|  |             <a href="#" class="delete-user dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> Delete User</a> | ||||||
|  |             <% } %> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | </td> | ||||||
							
								
								
									
										66
									
								
								src/frontend/js/app/users/list/item.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/frontend/js/app/users/list/item.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | const Mn         = require('backbone.marionette'); | ||||||
|  | const Controller = require('../../controller'); | ||||||
|  | const Api        = require('../../api'); | ||||||
|  | const Cache      = require('../../cache'); | ||||||
|  | const Tokens     = require('../../tokens'); | ||||||
|  | const template   = require('./item.ejs'); | ||||||
|  |  | ||||||
|  | module.exports = Mn.View.extend({ | ||||||
|  |     template: template, | ||||||
|  |     tagName:  'tr', | ||||||
|  |  | ||||||
|  |     ui: { | ||||||
|  |         edit:             'a.edit-user', | ||||||
|  |         password:         'a.set-password', | ||||||
|  |         login:            'a.login', | ||||||
|  |         delete:           'a.delete-user' | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     events: { | ||||||
|  |         'click @ui.edit': function (e) { | ||||||
|  |             e.preventDefault(); | ||||||
|  |             Controller.showUserForm(this.model); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         'click @ui.password': function (e) { | ||||||
|  |             e.preventDefault(); | ||||||
|  |             //Controller.showUserPasswordForm(this.model); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         'click @ui.delete': function (e) { | ||||||
|  |             e.preventDefault(); | ||||||
|  |             //Controller.showUserDeleteConfirm(this.model); | ||||||
|  |         }, | ||||||
|  |  | ||||||
|  |         'click @ui.login': function (e) { | ||||||
|  |             e.preventDefault(); | ||||||
|  |  | ||||||
|  |             if (Cache.User.get('id') !== this.model.get('id')) { | ||||||
|  |                 this.ui.login.prop('disabled', true).addClass('btn-disabled'); | ||||||
|  |  | ||||||
|  |                 Api.Users.loginAs(this.model.get('id')) | ||||||
|  |                     .then(res => { | ||||||
|  |                         Tokens.addToken(res.token, res.user.nickname || res.user.name); | ||||||
|  |                         window.location = '/'; | ||||||
|  |                         window.location.reload(); | ||||||
|  |                     }) | ||||||
|  |                     .catch(err => { | ||||||
|  |                         alert(err.message); | ||||||
|  |                         this.ui.login.prop('disabled', false).removeClass('btn-disabled'); | ||||||
|  |                     }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     templateContext: { | ||||||
|  |         isSelf: function () { | ||||||
|  |             return Cache.User.get('id') === this.id; | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     initialize: function () { | ||||||
|  |         this.listenTo(this.model, 'change', this.render); | ||||||
|  |     } | ||||||
|  | }); | ||||||
							
								
								
									
										10
									
								
								src/frontend/js/app/users/list/main.ejs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/frontend/js/app/users/list/main.ejs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | <thead> | ||||||
|  |     <th width="30"> </th> | ||||||
|  |     <th>Name</th> | ||||||
|  |     <th>Email</th> | ||||||
|  |     <th>Roles</th> | ||||||
|  |     <th> </th> | ||||||
|  | </thead> | ||||||
|  | <tbody> | ||||||
|  |     <!-- items --> | ||||||
|  | </tbody> | ||||||
							
								
								
									
										29
									
								
								src/frontend/js/app/users/list/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/frontend/js/app/users/list/main.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | const Mn         = require('backbone.marionette'); | ||||||
|  | const ItemView   = require('./item'); | ||||||
|  | const template   = require('./main.ejs'); | ||||||
|  |  | ||||||
|  | const TableBody = Mn.CollectionView.extend({ | ||||||
|  |     tagName:   'tbody', | ||||||
|  |     childView: ItemView | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | module.exports = Mn.View.extend({ | ||||||
|  |     tagName:   'table', | ||||||
|  |     className: 'table table-hover table-outline table-vcenter text-nowrap card-table', | ||||||
|  |     template:  template, | ||||||
|  |  | ||||||
|  |     regions: { | ||||||
|  |         body: { | ||||||
|  |             el:             'tbody', | ||||||
|  |             replaceElement: true | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     onRender: function () { | ||||||
|  |         this.showChildView('body', new TableBody({ | ||||||
|  |             collection: this.collection | ||||||
|  |         })); | ||||||
|  |     } | ||||||
|  | }); | ||||||
							
								
								
									
										17
									
								
								src/frontend/js/app/users/main.ejs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/frontend/js/app/users/main.ejs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | <div class="card"> | ||||||
|  |     <div class="card-header"> | ||||||
|  |         <h3 class="card-title">Users</h3> | ||||||
|  |         <div class="card-options"> | ||||||
|  |             <a href="#" class="btn btn-outline-teal btn-sm ml-2 add-user">Add User</a> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  |     <div class="card-body no-padding min-100"> | ||||||
|  |         <div class="dimmer active"> | ||||||
|  |             <div class="loader"></div> | ||||||
|  |             <div class="dimmer-content list-region"> | ||||||
|  |                 <!-- List Region --> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
							
								
								
									
										51
									
								
								src/frontend/js/app/users/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/frontend/js/app/users/main.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | const Mn         = require('backbone.marionette'); | ||||||
|  | const UserModel  = require('../../models/user'); | ||||||
|  | const Api        = require('../api'); | ||||||
|  | const Controller = require('../controller'); | ||||||
|  | const ListView   = require('./list/main'); | ||||||
|  | const template   = require('./main.ejs'); | ||||||
|  |  | ||||||
|  | module.exports = Mn.View.extend({ | ||||||
|  |     id:        'users', | ||||||
|  |     template:  template, | ||||||
|  |  | ||||||
|  |     ui: { | ||||||
|  |         list_region: '.list-region', | ||||||
|  |         add_user:    '.add-user', | ||||||
|  |         dimmer:      '.dimmer' | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     regions: { | ||||||
|  |         list_region: '@ui.list_region' | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     events: { | ||||||
|  |         'click @ui.add_user': function (e) { | ||||||
|  |             e.preventDefault(); | ||||||
|  |             Controller.showUserForm(new UserModel.Model()); | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     onRender: function () { | ||||||
|  |         let view = this; | ||||||
|  |  | ||||||
|  |         Api.Users.getAll() | ||||||
|  |             .then(response => { | ||||||
|  |                 if (!view.isDestroyed() && response && response.length) { | ||||||
|  |                     view.showChildView('list_region', new ListView({ | ||||||
|  |                         collection: new UserModel.Collection(response) | ||||||
|  |                     })); | ||||||
|  |  | ||||||
|  |                     // Remove loader | ||||||
|  |                     view.ui.dimmer.removeClass('active'); | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |             .catch(err => { | ||||||
|  |                 console.log(err); | ||||||
|  |                 //Controller.showError(err, 'Could not fetch Users'); | ||||||
|  |                 //view.trigger('loaded'); | ||||||
|  |             }); | ||||||
|  |     } | ||||||
|  | }); | ||||||
							
								
								
									
										25
									
								
								src/frontend/scss/custom.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/frontend/scss/custom.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | $primary-color: #2bcbba; | ||||||
|  |  | ||||||
|  | .loader { | ||||||
|  |     color: $primary-color; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | a { | ||||||
|  |     color: $primary-color; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | a:hover { | ||||||
|  |     color: darken($primary-color, 10%); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .dropdown-item.active, .dropdown-item:active { | ||||||
|  |     background-color: $primary-color; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .custom-switch-input:checked ~ .custom-switch-indicator { | ||||||
|  |     background: $primary-color; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .min-100 { | ||||||
|  |     min-height: 100px; | ||||||
|  | } | ||||||
| @@ -1,4 +1,6 @@ | |||||||
| @import "~tabler-ui/dist/assets/css/dashboard"; | @import "~tabler-ui/dist/assets/css/dashboard"; | ||||||
|  | @import "tabler-extra"; | ||||||
|  | @import "custom"; | ||||||
|  |  | ||||||
| /* Before any JS content is loaded */ | /* Before any JS content is loaded */ | ||||||
| #app > .loader, #login > .loader, .container > .loader { | #app > .loader, #login > .loader, .container > .loader { | ||||||
| @@ -10,4 +12,4 @@ | |||||||
|  |  | ||||||
| .no-js-warning { | .no-js-warning { | ||||||
|     margin-top: 100px; |     margin-top: 100px; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										26
									
								
								src/frontend/scss/tabler-extra.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/frontend/scss/tabler-extra.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | $teal: #2bcbba; | ||||||
|  |  | ||||||
|  | /* For Card bodies where I don't want padding */ | ||||||
|  | .card-body.no-padding { | ||||||
|  |     padding: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Teal Outline Buttons */ | ||||||
|  | .btn-outline-teal { | ||||||
|  |     color: $teal; | ||||||
|  |     background-color: transparent; | ||||||
|  |     background-image: none; | ||||||
|  |     border-color: $teal; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .btn-outline-teal:hover { | ||||||
|  |     color: #fff; | ||||||
|  |     background-color: $teal; | ||||||
|  |     border-color: $teal; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .btn-outline-teal:not(:disabled):not(.disabled):active, .btn-outline-teal:not(:disabled):not(.disabled).active, .show > .btn-outline-teal.dropdown-toggle { | ||||||
|  |     color: #fff; | ||||||
|  |     background-color: $teal; | ||||||
|  |     border-color: $teal; | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user