diff --git a/client/js/app.js b/client/js/app.js index 246c890d..8dc062ea 100644 --- a/client/js/app.js +++ b/client/js/app.js @@ -6,12 +6,13 @@ import $ from 'jquery' import _ from 'lodash' import Vue from 'vue' -import Vuex from 'vuex' +import VueResource from 'vue-resource' +import store from './store' import io from 'socket.io-client' import i18next from 'i18next' import i18nextXHR from 'i18next-xhr-backend' import VueI18Next from '@panter/vue-i18next' -import Alerts from './components/alerts.js' +// import Alerts from './components/alerts.js' import 'jquery-smooth-scroll' import 'jquery-sticky' @@ -19,18 +20,22 @@ import 'jquery-sticky' // Load Vue Components // ==================================== +import alertComponent from './components/alert.vue' import anchorComponent from './components/anchor.vue' import colorPickerComponent from './components/color-picker.vue' import loadingSpinnerComponent from './components/loading-spinner.vue' import searchComponent from './components/search.vue' +import adminUsersCreateComponent from './modals/admin-users-create.vue' + import adminProfileComponent from './pages/admin-profile.component.js' import adminSettingsComponent from './pages/admin-settings.component.js' // ==================================== -// Initialize i18next +// Initialize Vue Modules // ==================================== +Vue.use(VueResource) Vue.use(VueI18Next) i18next @@ -43,46 +48,18 @@ i18next fallbackLng: siteLang }) -// ==================================== -// Initialize Vuex -// ==================================== - -Vue.use(Vuex) - -const store = new Vuex.Store({ - state: { - loading: false - }, - mutations: { - startLoading: state => { state.loading = true }, - stopLoading: state => { state.loading = false } - } -}) - $(() => { - // ==================================== - // Scroll - // ==================================== - - $('a').smoothScroll({ - speed: 500, - offset: -50 - }) - - $('#header').sticky({ topSpacing: 0 }) - $('.sidebar-pagecontents').sticky({ topSpacing: 15, bottomSpacing: 75 }) - // ==================================== // Notifications // ==================================== $(window).bind('beforeunload', () => { - store.commit('startLoading') + store.dispatch('startLoading') }) $(document).ajaxSend(() => { - store.commit('startLoading') + store.dispatch('startLoading') }).ajaxComplete(() => { - store.commit('stopLoading') + store.dispatch('stopLoading') }) var alerts = {} @@ -107,27 +84,41 @@ $(() => { const i18n = new VueI18Next(i18next) new Vue({ components: { + alert: alertComponent, adminProfile: adminProfileComponent, adminSettings: adminSettingsComponent, + adminUsersCreate: adminUsersCreateComponent, anchor: anchorComponent, colorPicker: colorPickerComponent, loadingSpinner: loadingSpinnerComponent, search: searchComponent }, + directives: { + // sticky: VueSticky + }, store, i18n, - el: '#root' + el: '#root', + mounted() { + $('a').smoothScroll({ + speed: 500, + offset: -50 + }) + + $('#header').sticky({ topSpacing: 0 }) + $('.sidebar-pagecontents').sticky({ topSpacing: 15, bottomSpacing: 75 }) + + // ==================================== + // Pages logic + // ==================================== + + require('./pages/view.js')(alerts) + require('./pages/all.js')(alerts, socket) + require('./pages/create.js')(alerts, socket) + require('./pages/edit.js')(alerts, socket) + require('./pages/source.js')(alerts) + require('./pages/history.js')(alerts) + require('./pages/admin.js')(alerts) + } }) - - // ==================================== - // Pages logic - // ==================================== - - require('./pages/view.js')(alerts) - require('./pages/all.js')(alerts, socket) - require('./pages/create.js')(alerts, socket) - require('./pages/edit.js')(alerts, socket) - require('./pages/source.js')(alerts) - require('./pages/history.js')(alerts) - require('./pages/admin.js')(alerts) }) diff --git a/client/js/components/alert.vue b/client/js/components/alert.vue new file mode 100644 index 00000000..bb5826d8 --- /dev/null +++ b/client/js/components/alert.vue @@ -0,0 +1,21 @@ + + + diff --git a/client/js/modals/admin-users-create.js b/client/js/modals/admin-users-create.js deleted file mode 100644 index 751eabce..00000000 --- a/client/js/modals/admin-users-create.js +++ /dev/null @@ -1,56 +0,0 @@ -'use strict' - -import $ from 'jquery' -import Vue from 'vue' - -// Vue Create User instance - -module.exports = (alerts) => { - let vueCreateUser = new Vue({ - el: '#modal-admin-users-create', - data: { - email: '', - provider: 'local', - password: '', - name: '', - loading: false - }, - methods: { - open: (ev) => { - $('#modal-admin-users-create').addClass('is-active') - $('#modal-admin-users-create input').first().focus() - }, - cancel: (ev) => { - $('#modal-admin-users-create').removeClass('is-active') - vueCreateUser.email = '' - vueCreateUser.provider = 'local' - }, - create: (ev) => { - vueCreateUser.loading = true - $.ajax('/admin/users/create', { - data: { - email: vueCreateUser.email, - provider: vueCreateUser.provider, - password: vueCreateUser.password, - name: vueCreateUser.name - }, - dataType: 'json', - method: 'POST' - }).then((rData, rStatus, rXHR) => { - vueCreateUser.loading = false - if (rData.ok) { - vueCreateUser.cancel() - window.location.reload(true) - } else { - alerts.pushError('Something went wrong', rData.msg) - } - }, (rXHR, rStatus, err) => { - vueCreateUser.loading = false - alerts.pushError('Error', rXHR.responseJSON.msg) - }) - } - } - }) - - $('.btn-create-prompt').on('click', vueCreateUser.open) -} diff --git a/client/js/modals/admin-users-create.vue b/client/js/modals/admin-users-create.vue new file mode 100644 index 00000000..73b30142 --- /dev/null +++ b/client/js/modals/admin-users-create.vue @@ -0,0 +1,93 @@ + + + diff --git a/client/js/pages/admin-profile.component.js b/client/js/pages/admin-profile.component.js index 4c4486b4..a12dcad5 100644 --- a/client/js/pages/admin-profile.component.js +++ b/client/js/pages/admin-profile.component.js @@ -1,7 +1,5 @@ 'use strict' -import * as $ from 'jquery' - export default { name: 'admin-profile', props: ['email', 'name', 'provider'], @@ -13,17 +11,29 @@ export default { }, methods: { saveUser() { + let self = this if (this.password !== this.passwordVerify) { - //alerts.pushError('Error', "Passwords don't match!") - return + return self.$store.dispatch('alert', { + style: 'red', + icon: 'square-cross', + msg: 'The passwords don\'t match. Try again.' + }) } - $.post(window.location.href, { + this.$http.post(window.location.href, { password: this.password, name: this.name - }).done((resp) => { - //alerts.pushSuccess('Saved successfully', 'Changes have been applied.') - }).fail((jqXHR, txtStatus, resp) => { - //alerts.pushError('Error', resp) + }).then(resp => { + self.$store.dispatch('alert', { + style: 'green', + icon: 'check', + msg: 'Changes have been applied successfully.' + }) + }).catch(err => { + self.$store.dispatch('alert', { + style: 'red', + icon: 'square-cross', + msg: 'Error: ' + err.body.msg + }) }) } } diff --git a/client/js/pages/admin.js b/client/js/pages/admin.js index 030d7c96..2d0cd649 100644 --- a/client/js/pages/admin.js +++ b/client/js/pages/admin.js @@ -7,9 +7,7 @@ import _ from 'lodash' import Vue from 'vue' module.exports = (alerts) => { - if ($('#page-type-admin-users').length) { - require('../modals/admin-users-create.js')(alerts) - } else if ($('#page-type-admin-users-edit').length) { + if ($('#page-type-admin-users-edit').length) { let vueEditUser = new Vue({ el: '#page-type-admin-users-edit', data: { diff --git a/client/js/pages/view.js b/client/js/pages/view.js index 0a6f4922..7e124e02 100644 --- a/client/js/pages/view.js +++ b/client/js/pages/view.js @@ -4,8 +4,6 @@ import $ from 'jquery' import MathJax from 'mathjax' -// import * as CopyPath from '../components/copy-path.vue' -import Vue from 'vue' module.exports = (alerts) => { if ($('#page-type-view').length) { diff --git a/client/js/store/index.js b/client/js/store/index.js new file mode 100644 index 00000000..0ba9bfdd --- /dev/null +++ b/client/js/store/index.js @@ -0,0 +1,25 @@ +import Vue from 'vue' +import Vuex from 'vuex' + +import alert from './modules/alert' +import adminUsersCreate from './modules/admin-users-create' + +Vue.use(Vuex) + +export default new Vuex.Store({ + state: { + loading: false + }, + mutations: { + loadingChange: (state, loadingState) => { state.loading = loadingState } + }, + actions: { + startLoading({ commit }) { commit('loadingChange', true) }, + stopLoading({ commit }) { commit('loadingChange', false) } + }, + getters: {}, + modules: { + alert, + adminUsersCreate + } +}) diff --git a/client/js/store/modules/admin-users-create.js b/client/js/store/modules/admin-users-create.js new file mode 100644 index 00000000..038f5577 --- /dev/null +++ b/client/js/store/modules/admin-users-create.js @@ -0,0 +1,15 @@ +'use strict' + +export default { + state: { + shown: false + }, + getters: {}, + mutations: { + shownChange: (state, shownState) => { state.shown = shownState } + }, + actions: { + adminUsersCreateOpen({ commit }) { commit('shownChange', true) }, + adminUsersCreateClose({ commit }) { commit('shownChange', false) } + } +} diff --git a/client/js/store/modules/alert.js b/client/js/store/modules/alert.js new file mode 100644 index 00000000..79d5cee1 --- /dev/null +++ b/client/js/store/modules/alert.js @@ -0,0 +1,32 @@ +'use strict' + +import _ from 'lodash' + +export default { + state: { + shown: false, + style: 'green', + icon: 'check', + msg: '' + }, + getters: {}, + mutations: { + alertChange: (state, opts) => { + state.shown = (opts.shown === true) + state.style = opts.style || 'green' + state.icon = opts.icon || 'check' + state.msg = opts.msg || '' + } + }, + actions: { + alert({ commit, dispatch }, opts) { + opts.shown = true + commit('alertChange', opts) + dispatch('alertDismiss') + }, + alertDismiss: _.debounce(({ commit }) => { + let opts = { shown: false } + commit('alertChange', opts) + }, 3000) + } +} diff --git a/client/scss/components/alert.scss b/client/scss/components/alert.scss index 61336257..2425796f 100644 --- a/client/scss/components/alert.scss +++ b/client/scss/components/alert.scss @@ -1,114 +1,48 @@ -/*#alerts { - position: fixed; - top: 60px; - right: 10px; - width: 350px; - z-index: 10; - text-shadow: 1px 1px 0 rgba(0,0,0,0.1); +.alert { + background-color: #FFF; + border-right: 3px solid mc('grey', '500'); + position: fixed; + top: 60px; + margin-left: 10px; + right: 10px; + max-width: 500px; + z-index: 1000; + display: flex; + box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); + animation-duration: .6s; - .notification { - animation: 0.5s ease slideInRight; - margin-top: 5px; + &-icon { + width: 50px; + height: 50px; + background-color: mc('grey', '500'); + color: #FFF; + display: flex; + align-items: center; + justify-content: center; + } - &.exit { - animation: 0.5s ease fadeOutRight; - } + &-msg { + padding: 0 15px; + display: flex; + align-items: center; + justify-content: flex-start; + border-top: 1px solid mc('grey', '200'); + font-size: 14px; + font-weight: 500; + } - } - - h3 { - font-size: 16px; - font-size: 500; - } - -}*/ - -#alerts { - position: fixed; - top: 55px; - right: 10px; - width: 350px; - z-index: 100; - - > ul { - margin: 0; - padding: 0; - list-style-type: none; - - > li { - background-color: material-color('blue-grey', '800'); - box-shadow: 5px 5px 0 transparentize(material-color('blue-grey', '900'), 0.7); - border: 1px solid material-color('blue-grey', '500'); - border-left-width: 5px; - margin-top: 5px; - padding: 8px 12px; - animation-name: slideFromRight; - animation-duration: 1s; - cursor: pointer; - position: relative; - - &:hover { - background-color: material-color('blue-grey', '900'); - } - - &.exit { - animation-name: zoomOut; - animation-duration: 1s; - transform-origin: top center; - } - - > button { - background-color: transparent; - border: none; - color: #FFF; - width: 15px; - height: 15px; - padding: 0; - position: absolute; - top: 10px; - right: 10px; - - &:before { - content: 'X'; - } + @each $color, $colorvalue in $material-colors { + &.is-#{$color} { + border-right-color: mc($color, '500'); + .alert-icon { + background-color: mc($color, '500'); + } + .alert-msg { + color: mc($color, '900'); } - > strong { - display: block; - font-size: 13px; - font-weight: 500; - color: #FFF; - - > i { - margin-right: 5px; - } - - } - - > span { - font-size: 12px; - font-weight: 500; - color: material-color('blue-grey', '100'); - } - - &.error { - border-color: material-color('red', '400'); - background-color: material-color('red', '600'); - > span { - color: material-color('red', '50'); - } - } - &.success { - border-color: material-color('green', '400'); - background-color: material-color('green', '700'); - > span { - color: material-color('green', '50'); - } - } - } - } } diff --git a/package.json b/package.json index 17503c36..8e94f910 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "i18next-express-middleware": "^1.0.5", "i18next-node-fs-backend": "^1.0.0", "image-size": "^0.5.4", - "jimp": "https://github.com/ngpixel/jimp.git", + "jimp": "^0.2.28", "js-yaml": "^3.8.4", "jsonwebtoken": "^7.4.1", "klaw": "^1.3.1", @@ -157,6 +157,7 @@ "uglify-js": "latest", "vee-validate": "^2.0.0-rc.3", "vue": "^2.3.3", + "vue-resource": "^1.3.1", "vue-template-compiler": "^2.3.3", "vue-template-es2015-compiler": "^1.5.2", "vuex": "^2.3.1" diff --git a/server/views/layout.pug b/server/views/layout.pug index 66bc8dcc..4c00e88c 100644 --- a/server/views/layout.pug +++ b/server/views/layout.pug @@ -29,7 +29,7 @@ html body #root.has-stickynav include ./common/header.pug - //-include ./common/alerts.pug + alert main block content include ./common/footer.pug diff --git a/server/views/pages/admin/users.pug b/server/views/pages/admin/users.pug index 48acd039..89da943c 100644 --- a/server/views/pages/admin/users.pug +++ b/server/views/pages/admin/users.pug @@ -3,45 +3,44 @@ extends ./_layout.pug block rootNavRight i.nav-item#notifload .nav-item - a.button.btn-create-prompt + a.button(v-on:click='$store.dispatch("adminUsersCreateOpen")') i.icon-plus span= t('admin:users.createauthorize') block adminContent - #page-type-admin-users - .hero - h1.title#title= t('nav.users') - h2.subtitle= t('admin:users.subtitle') - table.table - thead + .hero + h1.title#title= t('nav.users') + h2.subtitle= t('admin:users.subtitle') + table.table + thead + tr + th + th= t('admin:users.name') + th= t('admin:users.email') + th= t('admin:users.provider') + th= t('admin:users.createdon') + th= t('admin:users.updatedon') + tbody + each usr in usrs tr - th - th= t('admin:users.name') - th= t('admin:users.email') - th= t('admin:users.provider') - th= t('admin:users.createdon') - th= t('admin:users.updatedon') - tbody - each usr in usrs - tr - td.is-icon - i.icon-user.is-grey - td - a(href='/admin/users/' + usr._id)= usr.name - td= usr.email - td.is-centered.has-icons - case usr.provider - when 'local': i.icon-server - when 'windowslive': i.icon-windows2.is-blue - when 'azure': i.icon-windows2.is-blue - when 'google': i.icon-google.is-blue - when 'facebook': i.icon-facebook.is-indigo - when 'github': i.icon-github.is-grey - when 'slack': i.icon-slack.is-purple - when 'ldap': i.icon-arrow-repeat-outline - default: i.icon-warning - = t('auth:providers.' + usr.provider) - td.is-centered= moment(usr.createdAt).format('lll') - td.is-centered= moment(usr.updatedAt).format('lll') + td.is-icon + i.icon-user.is-grey + td + a(href='/admin/users/' + usr._id)= usr.name + td= usr.email + td.is-centered.has-icons + case usr.provider + when 'local': i.icon-server + when 'windowslive': i.icon-windows2.is-blue + when 'azure': i.icon-windows2.is-blue + when 'google': i.icon-google.is-blue + when 'facebook': i.icon-facebook.is-indigo + when 'github': i.icon-github.is-grey + when 'slack': i.icon-slack.is-purple + when 'ldap': i.icon-arrow-repeat-outline + default: i.icon-warning + = t('auth:providers.' + usr.provider) + td.is-centered= moment(usr.createdAt).format('lll') + td.is-centered= moment(usr.updatedAt).format('lll') - include ../../modals/admin-createuser.pug + admin-users-create diff --git a/yarn.lock b/yarn.lock index fc32004a..e3501980 100644 --- a/yarn.lock +++ b/yarn.lock @@ -929,9 +929,9 @@ bluebird@^3.0, bluebird@^3.1.1, bluebird@^3.4.1, bluebird@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" -bmp-js@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.0.2.tgz#c268321f8085df177dfcaaa059c19254862fa158" +bmp-js@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.0.3.tgz#64113e9c7cf1202b376ed607bf30626ebe57b18a" body-parser@^1.14.2, body-parser@^1.17.2: version "1.17.2" @@ -1496,7 +1496,7 @@ crc@3.4.4, crc@^3.4.0: version "3.4.4" resolved "https://registry.yarnpkg.com/crc/-/crc-3.4.4.tgz#9da1e980e3bd44fc5c93bf5ab3da3378d85e466b" -create-error-class@^3.0.1: +create-error-class@^3.0.0, create-error-class@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" dependencies: @@ -1778,6 +1778,10 @@ duplexer2@^0.1.4: dependencies: readable-stream "^2.0.2" +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + duplexer@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" @@ -2780,6 +2784,22 @@ got@^5.0.0: unzip-response "^1.0.2" url-parse-lax "^1.0.0" +got@^6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" + dependencies: + create-error-class "^3.0.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-redirect "^1.0.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + lowercase-keys "^1.0.0" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + unzip-response "^2.0.1" + url-parse-lax "^1.0.0" + graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -3586,18 +3606,19 @@ jest@latest: dependencies: jest-cli "^20.0.3" -"jimp@https://github.com/ngpixel/jimp.git": - version "0.2.27" - resolved "https://github.com/ngpixel/jimp.git#d5c3e98eb45875dce412397ff56d8a2caa714916" +jimp@^0.2.28: + version "0.2.28" + resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.2.28.tgz#dd529a937190f42957a7937d1acc3a7762996ea2" dependencies: bignumber.js "^2.1.0" - bmp-js "0.0.2" + bmp-js "0.0.3" es6-promise "^3.0.2" exif-parser "^0.1.9" file-type "^3.1.0" jpeg-js "^0.2.0" load-bmfont "^1.2.3" mime "^1.3.4" + mkdirp "0.5.1" pixelmatch "^4.0.0" pngjs "^3.0.0" read-chunk "^1.0.1" @@ -4345,14 +4366,10 @@ mime-types@^2.1.12, mime-types@^2.1.15, mime-types@~2.1.11, mime-types@~2.1.15, dependencies: mime-db "~1.27.0" -mime@1.3.4: +mime@1.3.4, mime@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" -mime@^1.3.4: - version "1.3.6" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0" - mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" @@ -6620,6 +6637,10 @@ timed-out@^3.0.0: version "3.1.3" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-3.1.3.tgz#95860bfcc5c76c277f8f8326fd0f5b2e20eba217" +timed-out@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + tinycolor2@^1.1.2: version "1.4.1" resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8" @@ -6801,6 +6822,10 @@ unzip-response@^1.0.0, unzip-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe" +unzip-response@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" + update-notifier@0.5.0, update-notifier@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.5.0.tgz#07b5dc2066b3627ab3b4f530130f7eddda07a4cc" @@ -6967,6 +6992,12 @@ void-elements@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" +vue-resource@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-1.3.1.tgz#bf2f7b70bfe21b397c9d7607878f776a3acea2cf" + dependencies: + got "^6.7.1" + vue-template-compiler@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.3.3.tgz#b5bab9ec57309c906b82a78c81a02179dbc2f470"