diff --git a/src/.eslintrc.js b/src/.eslintrc.js --- a/src/.eslintrc.js +++ b/src/.eslintrc.js @@ -13,6 +13,7 @@ "vue/html-indent": ["error", 4], "vue/html-self-closing": "off", "vue/max-attributes-per-line": "off", + "vue/no-unused-components": "off", "vue/no-v-html": "off", "vue/singleline-html-element-content-newline": "off", "vue/multiline-html-element-content-newline": "off" diff --git a/src/app/Http/Controllers/API/V4/PaymentsController.php b/src/app/Http/Controllers/API/V4/PaymentsController.php --- a/src/app/Http/Controllers/API/V4/PaymentsController.php +++ b/src/app/Http/Controllers/API/V4/PaymentsController.php @@ -406,9 +406,10 @@ $exists = Payment::where('wallet_id', $wallet->id) ->where('type', PaymentProvider::TYPE_ONEOFF) ->whereIn('status', [ - PaymentProvider::STATUS_OPEN, - PaymentProvider::STATUS_PENDING, - PaymentProvider::STATUS_AUTHORIZED]) + PaymentProvider::STATUS_OPEN, + PaymentProvider::STATUS_PENDING, + PaymentProvider::STATUS_AUTHORIZED + ]) ->exists(); return response()->json([ @@ -437,9 +438,10 @@ $result = Payment::where('wallet_id', $wallet->id) ->where('type', PaymentProvider::TYPE_ONEOFF) ->whereIn('status', [ - PaymentProvider::STATUS_OPEN, - PaymentProvider::STATUS_PENDING, - PaymentProvider::STATUS_AUTHORIZED]) + PaymentProvider::STATUS_OPEN, + PaymentProvider::STATUS_PENDING, + PaymentProvider::STATUS_AUTHORIZED + ]) ->orderBy('created_at', 'desc') ->limit($pageSize + 1) ->offset($pageSize * ($page - 1)) diff --git a/src/app/Http/Controllers/API/V4/UsersController.php b/src/app/Http/Controllers/API/V4/UsersController.php --- a/src/app/Http/Controllers/API/V4/UsersController.php +++ b/src/app/Http/Controllers/API/V4/UsersController.php @@ -76,12 +76,55 @@ public function index() { $user = $this->guard()->user(); + $search = trim(request()->input('search')); + $page = intval(request()->input('page')) ?: 1; + $pageSize = 20; + $hasMore = false; + + $result = $user->users(); + + // Search by user email, alias or name + if (strlen($search) > 0) { + // thanks to cloning we skip some extra queries in $user->users() + $allUsers1 = clone $result; + $allUsers2 = clone $result; + + $result->whereLike('email', $search) + ->union( + $allUsers1->join('user_aliases', 'users.id', '=', 'user_aliases.user_id') + ->whereLike('alias', $search) + ) + ->union( + $allUsers2->join('user_settings', 'users.id', '=', 'user_settings.user_id') + ->whereLike('value', $search) + ->whereIn('key', ['first_name', 'last_name']) + ); + } + + $result = $result->orderBy('email') + ->limit($pageSize + 1) + ->offset($pageSize * ($page - 1)) + ->get(); + + if (count($result) > $pageSize) { + $result->pop(); + $hasMore = true; + } + + // Process the result + $result = $result->map( + function ($user) { + $data = $user->toArray(); + $data = array_merge($data, self::userStatuses($user)); + return $data; + } + ); - $result = $user->users()->orderBy('email')->get()->map(function ($user) { - $data = $user->toArray(); - $data = array_merge($data, self::userStatuses($user)); - return $data; - }); + $result = [ + 'list' => $result, + 'count' => count($result), + 'hasMore' => $hasMore, + ]; return response()->json($result); } diff --git a/src/resources/lang/en/ui.php b/src/resources/lang/en/ui.php --- a/src/resources/lang/en/ui.php +++ b/src/resources/lang/en/ui.php @@ -382,6 +382,7 @@ 'reset-2fa' => "Reset 2-Factor Auth", 'reset-2fa-title' => "2-Factor Authentication Reset", 'title' => "User account", + 'search' => "User email address or name", 'search-pl' => "User ID, email or domain", 'skureq' => "{sku} requires {list}.", 'subscription' => "Subscription", diff --git a/src/resources/vue/Reseller/Invitations.vue b/src/resources/vue/Reseller/Invitations.vue --- a/src/resources/vue/Reseller/Invitations.vue +++ b/src/resources/vue/Reseller/Invitations.vue @@ -7,10 +7,7 @@
-
- - -
+
-
+
@@ -96,21 +87,19 @@ import { Modal } from 'bootstrap' import { library } from '@fortawesome/fontawesome-svg-core' import { faEnvelopeOpenText, faPaperPlane, faRedo } from '@fortawesome/free-solid-svg-icons' + import ListTools from '../Widgets/ListTools' library.add(faEnvelopeOpenText, faPaperPlane, faRedo) export default { + mixins: [ ListTools ], data() { return { - invitations: [], - hasMore: false, - page: 1, - search: '' + invitations: [] } }, mounted() { - this.$root.startLoading() - this.loadInvitations(null, () => this.$root.stopLoading()) + this.loadInvitations({ init: true }) $('#invite-create')[0].addEventListener('shown.bs.modal', event => { $('input', event.target).first().focus() @@ -179,54 +168,8 @@ this.dialog = new Modal(dialog) this.dialog.show() }, - loadInvitations(params, callback) { - let loader - let get = {} - - if (params) { - if (params.reset) { - this.invitations = [] - this.page = 0 - } - - get.page = params.page || (this.page + 1) - - if (typeof params === 'object' && 'search' in params) { - get.search = params.search - this.currentSearch = params.search - } else { - get.search = this.currentSearch - } - - loader = $(get.page > 1 ? '#more-loader' : '#invitations-list tfoot td') - } else { - this.currentSearch = null - } - - this.$root.addLoader(loader) - - axios.get('/api/v4/invitations', { params: get }) - .then(response => { - this.$root.removeLoader(loader) - - // Note: In Vue we can't just use .concat() - for (let i in response.data.list) { - this.$set(this.invitations, this.invitations.length, response.data.list[i]) - } - this.hasMore = response.data.hasMore - this.page = response.data.page || 1 - - if (callback) { - callback() - } - }) - .catch(error => { - this.$root.removeLoader(loader) - - if (callback) { - callback() - } - }) + loadInvitations(params) { + this.listSearch('invitations', '/api/v4/invitations', params) }, resendInvite(id) { axios.post('/api/v4/invitations/' + id + '/resend') @@ -242,8 +185,8 @@ } }) }, - searchInvitations() { - this.loadInvitations({ reset: true, search: this.search }) + searchInvitations(search) { + this.loadInvitations({ reset: true, search }) }, statusClass(invitation) { if (invitation.isCompleted) { diff --git a/src/resources/vue/User/List.vue b/src/resources/vue/User/List.vue --- a/src/resources/vue/User/List.vue +++ b/src/resources/vue/User/List.vue @@ -4,12 +4,17 @@
{{ $t('user.list-title') }} - - {{ $t('user.create') }} -
- +
+ +
+ + {{ $t('user.create') }} + +
+
+
@@ -23,12 +28,9 @@ - - - - - +
{{ $t('form.primary-email') }}
{{ $t('user.users-none') }}
+
@@ -36,22 +38,25 @@ diff --git a/src/resources/vue/Widgets/ListTools.vue b/src/resources/vue/Widgets/ListTools.vue new file mode 100644 --- /dev/null +++ b/src/resources/vue/Widgets/ListTools.vue @@ -0,0 +1,116 @@ + + + diff --git a/src/resources/vue/Widgets/PaymentLog.vue b/src/resources/vue/Widgets/PaymentLog.vue --- a/src/resources/vue/Widgets/PaymentLog.vue +++ b/src/resources/vue/Widgets/PaymentLog.vue @@ -17,56 +17,28 @@ {{ amount(payment) }} - - - {{ $t('wallet.pending-payments-none') }} - - + -
- -
+