diff --git a/app/Http/Requests/UserListRequest.php b/app/Http/Requests/UserListRequest.php index e5fee77..6782162 100644 --- a/app/Http/Requests/UserListRequest.php +++ b/app/Http/Requests/UserListRequest.php @@ -21,7 +21,8 @@ public function rules() { return [ 'orderBy' => 'in:first_name,last_name,organisation,industry_type,created_at,last_logged_in_at', - 'sort' => 'in:asc,desc' + 'sort' => 'in:asc,desc', + 'filters.search' => 'nullable|string|min:3|max:191' ]; } @@ -36,8 +37,10 @@ public function getUserQuery(): UserQuery $userQuery->setOrderBy($orderBy); $userQuery->setSort($sort); - foreach ($this->get('filters', []) as $column => $value) { - $userQuery->addFilter($column, $value); + $filters = $this->get('filters', $this->get('filters\\', [])); + + foreach ($filters as $column => $value) { + $userQuery->addFilter(rtrim($column, '\\'), $value); } return $userQuery; diff --git a/app/Repositories/Access/UserRepository.php b/app/Repositories/Access/UserRepository.php index d2a57bd..183d956 100644 --- a/app/Repositories/Access/UserRepository.php +++ b/app/Repositories/Access/UserRepository.php @@ -186,6 +186,11 @@ public function queryUsers(UserQuery $userQuery): Builder }); }); + $search = trim($userQuery->getFilters()->get('search', '')); + if (mb_strlen($search) >= 3) { + $this->applySearchFilter($builder, $search); + } + $society = $userQuery->getFilters()->only(['society'])->first(); if ($society) { $builder->whereHas('organisations', function ($query) use ($society) { @@ -214,12 +219,25 @@ public function queryUsers(UserQuery $userQuery): Builder } if (in_array($userQuery->getOrderBy(), ['first_name', 'last_name', 'organisation', 'industry_type'])) { - $builder->orderByRaw('(SELECT ' . $userQuery->getOrderBy() . ' FROM user_profiles WHERE user_profiles.user_id = users.id) ' . $userQuery->getSort()); + $builder->orderByRaw('(SELECT ' . $userQuery->getOrderBy() . ' FROM user_profiles WHERE user_profiles.user_id = users.id AND user_profiles.deleted_at IS NULL ORDER BY user_profiles.id DESC LIMIT 1) ' . $userQuery->getSort()); } return $builder; } + private function applySearchFilter(Builder $builder, string $search) + { + $profileMatch = 'MATCH(user_profiles.first_name, user_profiles.last_name) AGAINST (? IN NATURAL LANGUAGE MODE)'; + $emailMatch = 'MATCH(users.email) AGAINST (? IN NATURAL LANGUAGE MODE)'; + $profileScore = "(SELECT {$profileMatch} FROM user_profiles WHERE user_profiles.user_id = users.id AND user_profiles.deleted_at IS NULL ORDER BY user_profiles.id DESC LIMIT 1)"; + $score = "({$emailMatch} + COALESCE({$profileScore}, 0))"; + + $builder->select('users.*') + ->selectRaw("{$score} as search_score", [$search, $search]) + ->whereRaw("({$emailMatch} > 0 OR EXISTS (SELECT 1 FROM user_profiles WHERE user_profiles.user_id = users.id AND user_profiles.deleted_at IS NULL AND {$profileMatch} > 0))", [$search, $search]) + ->orderBy('search_score', 'desc'); + } + public function deactivate(User $user) { $user->activated = false; diff --git a/database/migrations/2026_05_29_000000_add_fulltext_indexes_for_user_search.php b/database/migrations/2026_05_29_000000_add_fulltext_indexes_for_user_search.php new file mode 100644 index 0000000..1780884 --- /dev/null +++ b/database/migrations/2026_05_29_000000_add_fulltext_indexes_for_user_search.php @@ -0,0 +1,51 @@ +indexExists('users', 'users_email_fulltext_search')) { + DB::statement('ALTER TABLE users ADD FULLTEXT users_email_fulltext_search(email)'); + } + + $this->dropIndexIfExists('user_profiles', 'user_profiles_search_fulltext'); + DB::statement('ALTER TABLE user_profiles ADD FULLTEXT user_profiles_search_fulltext(first_name, last_name)'); + } + + + public function down() + { + if (env('DB_CONNECTION') !== 'mysql') { + return; + } + + $this->dropIndexIfExists('users', 'users_email_fulltext_search'); + $this->dropIndexIfExists('user_profiles', 'user_profiles_search_fulltext'); + } + + private function dropIndexIfExists(string $table, string $index) + { + if ($this->indexExists($table, $index)) { + DB::statement("ALTER TABLE {$table} DROP INDEX {$index}"); + } + } + + private function indexExists(string $table, string $index): bool + { + return (bool) DB::select( + DB::raw( + "SHOW KEYS + FROM {$table} + WHERE Key_name='{$index}'" + ) + ); + } +} diff --git a/resources/assets/js/pages/users/list.vue b/resources/assets/js/pages/users/list.vue index 7342c94..0f75338 100644 --- a/resources/assets/js/pages/users/list.vue +++ b/resources/assets/js/pages/users/list.vue @@ -67,6 +67,16 @@ :staynull="true" v-if="!apiUsers"/> + +

{{ $t('users.list.search') }}

+ + +
{{ $t('users.list.clear_filters') }} @@ -226,6 +236,7 @@ export default { fetchingUsers: false, roles: null, locationOptions: null, + searchDebounce: null, countries: require('country-list')() } }, @@ -239,6 +250,21 @@ export default { deep: true }, activatedFilter: fetchHandler, + searchFilter: { + handler (val, oldVal) { + if (val !== oldVal) { + clearTimeout(this.searchDebounce) + const search = val ? val.trim() : '' + if (search.length > 0 && search.length < 3) { + return + } + this.searchDebounce = setTimeout(() => { + this.currentPage = 1 + this.fetchUsers() + }, 350) + } + } + }, roleFilter: fetchHandler, countryFilter: fetchHandler, termsFilter: fetchHandler, @@ -259,6 +285,9 @@ export default { this.fetchUsers() this.fetchTerms() }, + beforeDestroy () { + clearTimeout(this.searchDebounce) + }, metaInfo () { return { title: this.$t('users.list.manage') } }, @@ -268,6 +297,7 @@ export default { this.roleFilter = null this.countryFilter = null this.selectedSoc = null + this.searchFilter = '' this.termsFilter = termsDefault }, getSocietyByCode (code) { @@ -310,12 +340,14 @@ export default { }, async fetchUsers () { this.fetchingUsers = true - await this.fetchRoles() + if (this.rolesEmpty) { + await this.fetchRoles() + } - let apiUserRole = this.roleOptions.find(role => role.name === 'API User') + const apiUserRole = this.roleOptions.find(role => role.name === 'API User') let filterRoleId = this.roleFilter if (filterRoleId === null) { - filterRoleId = this.apiUsers ? apiUserRole.id : null + filterRoleId = this.apiUsers && apiUserRole ? apiUserRole.id : null } if (!apiUserRole && this.apiUsers) { @@ -330,6 +362,7 @@ export default { role: filterRoleId, society: this.selectedSoc ? this.selectedSoc.countryCode : null, country_code: this.countryFilter, + search: this.searchFilter, terms_version: this.termsFilter === termsDefault ? null : this.termsFilter }, admin: !this.apiUsers && filterRoleId === null, @@ -408,6 +441,14 @@ export default { return this.$store.state.users.filters.roleFilter } }, + searchFilter: { + set: function (newVal) { + this.$store.dispatch('users/setFilter', { searchFilter: newVal }) + }, + get: function () { + return this.$store.state.users.filters.searchFilter + } + }, countryFilter: { set: function(newVal) { this.$store.dispatch('users/setFilter', { countryFilter: newVal }) @@ -451,6 +492,7 @@ export default { this.roleFilter === null && this.countryFilter === null && this.selectedSoc === null && + !this.searchFilter && this.termsFilter === termsDefault }, ...mapGetters({ @@ -487,5 +529,11 @@ export default { .text-nowrap { white-space: nowrap; } + .search-filter-input { + background: #E9E9E9; + border: none; + border-radius: 10px; + height: 2.45rem; + } diff --git a/resources/assets/js/plugins/axios.js b/resources/assets/js/plugins/axios.js index fce96eb..9325132 100644 --- a/resources/assets/js/plugins/axios.js +++ b/resources/assets/js/plugins/axios.js @@ -23,6 +23,10 @@ axios.interceptors.request.use(request => { // Response interceptor axios.interceptors.response.use(response => response, error => { + if (axios.isCancel(error)) { + return Promise.reject(error) + } + if (error.response && error.response.status) { const { status } = error.response diff --git a/resources/assets/js/store/modules/users.js b/resources/assets/js/store/modules/users.js index 57667a1..8f23349 100644 --- a/resources/assets/js/store/modules/users.js +++ b/resources/assets/js/store/modules/users.js @@ -2,6 +2,8 @@ import axios from 'axios' import * as types from '../mutation-types' import querystring from 'querystring' +let latestFetchUsersRequest = 0 + // state export const state = { users: { @@ -24,6 +26,7 @@ export const state = { roleFilter: null, countryFilter: null, selectedSoc: null, + searchFilter: '', termsFilter: 'Terms and Conditions' } } @@ -104,6 +107,7 @@ export const actions = { roleFilter: null, countryFilter: null, selectedSoc: null, + searchFilter: '', termsFilter: 'Terms and Conditions' }) commit(types.SET_ORDER_BY, null) @@ -120,6 +124,7 @@ export const actions = { commit(types.SET_SORT_DESC, sortDesc) }, async fetchUsers ({ commit }, { page, filters, excludes, admin, orderBy, sort }) { + const requestId = ++latestFetchUsersRequest const queryOptions = { page } if (orderBy !== null) { queryOptions.orderBy = orderBy @@ -131,11 +136,19 @@ export const actions = { filterString += filters.society !== null ? `&filters[society]=${filters.society}` : '' filterString += filters.country_code !== null ? `&filters[country_code]=${filters.country_code}` : '' filterString += filters.terms_version !== null ? `&filters[terms_version]=${filters.terms_version}` : '' + const search = filters.search ? filters.search.trim() : '' + filterString += search.length >= 3 ? `&filters[search]=${encodeURIComponent(search)}` : '' const url = admin ? '/api/users/admins' : '/api/users' const { data } = await axios.get(`${url}?${querystring.stringify(queryOptions)}${filterString}`) + if (requestId !== latestFetchUsersRequest) { + return + } commit(types.FETCH_USERS_SUCCESS, { users: data }) } catch (e) { + if (requestId !== latestFetchUsersRequest) { + return + } commit(types.FETCH_USERS_FAILURE, { error: e }) } }, diff --git a/resources/lang/am.json b/resources/lang/am.json index 382243a..6847c33 100644 --- a/resources/lang/am.json +++ b/resources/lang/am.json @@ -343,6 +343,8 @@ "select_country": "ሀገር ምረጥ", "select_terms": "የውሎች ስሪት ምረጥ", "select_society": "የብሔራዊ ማህበር ምረጥ", + "search": "ፈልግ", + "search_placeholder": "ስም ወይም ኢሜይል (ቢያንስ 3 ቁምፊዎች)", "create": "አዲስ መጠቀሚያ ይፍጠሩ፤", "download_report": "ሪፖርት ያውርዱ፤", "empty": "ምንም መጠቀሚያ ማግኘት አልተቻለም፤", diff --git a/resources/lang/ar.json b/resources/lang/ar.json index d370c9d..9a595f5 100644 --- a/resources/lang/ar.json +++ b/resources/lang/ar.json @@ -349,6 +349,8 @@ "select_country": "اختر بلدًا", "select_terms": "اختر إصدار الشروط", "select_society": "اختر جمعية وطنية", + "search": "بحث", + "search_placeholder": "الاسم أو البريد الإلكتروني (3 أحرف على الأقل)", "create": "إنشاء مستخدم جديد", "download_report": "تنزيل التقرير", "empty": "لم يتم العثور على أي مستخدمين", diff --git a/resources/lang/bn.json b/resources/lang/bn.json index 373b02e..50d42bf 100644 --- a/resources/lang/bn.json +++ b/resources/lang/bn.json @@ -343,6 +343,8 @@ "select_country": "একটি দেশ নির্বাচন করুন", "select_terms": "একটি শর্তাবলী সংস্করণ নির্বাচন করুন", "select_society": "জাতীয় সমিতি নির্বাচন করুন", + "search": "অনুসন্ধান", + "search_placeholder": "নাম বা ইমেল (কমপক্ষে ৩ অক্ষর)", "create": "নতুন ব্যবহারকারী তৈরি করুন", "download_report": "রিপোর্ট ডাউনলোড করুন", "empty": "কোন ব্যবহারকারী খুঁজে পাইনি", diff --git a/resources/lang/de.json b/resources/lang/de.json index 94d8c85..288973d 100644 --- a/resources/lang/de.json +++ b/resources/lang/de.json @@ -343,6 +343,8 @@ "select_country": "Land auswählen", "select_terms": "Version der Bedingungen auswählen", "select_society": "Nationale Gesellschaft auswählen", + "search": "Suchen", + "search_placeholder": "Name oder E-Mail (mind. 3 Zeichen)", "create": "Neuen Benutzer anlegen", "download_report": "Bericht herunterladen", "empty": "Es konnten keine Benutzer gefunden werden", diff --git a/resources/lang/en.json b/resources/lang/en.json index ea34e40..4178116 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -349,6 +349,8 @@ "select_country": "Select a country", "select_terms": "Select a terms version", "select_society": "Select a nacional society", + "search": "Search", + "search_placeholder": "Name or email (min. 3 characters)", "create": "Create new user", "download_report": "Download report", "empty": "Couldn't find any users", diff --git a/resources/lang/es.json b/resources/lang/es.json index 8164518..99741e6 100644 --- a/resources/lang/es.json +++ b/resources/lang/es.json @@ -349,6 +349,8 @@ "select_country": "Seleccionar un país", "select_terms": "Seleccionar una versión de términos", "select_society": "Seleccionar una sociedad nacional", + "search": "Buscar", + "search_placeholder": "Nombre o correo (mín. 3 caracteres)", "create": "Crear nuevo usuario", "download_report": "Descargar informe", "empty": "No se encontraron usuarios", diff --git a/resources/lang/fr.json b/resources/lang/fr.json index f5a4ec8..4de01df 100644 --- a/resources/lang/fr.json +++ b/resources/lang/fr.json @@ -349,6 +349,8 @@ "select_country": "Sélectionner un pays", "select_terms": "Sélectionner une version des conditions", "select_society": "Sélectionner une société nationale", + "search": "Rechercher", + "search_placeholder": "Nom ou e-mail (3 caractères min.)", "create": "Créer un nouvel utilisateur", "download_report": "Télécharger le rapport", "empty": "Aucun utilisateur trouvé", diff --git a/resources/lang/ht.json b/resources/lang/ht.json index d83355a..f09197f 100644 --- a/resources/lang/ht.json +++ b/resources/lang/ht.json @@ -343,6 +343,8 @@ "select_country": "Chwazi yon peyi", "select_terms": "Chwazi yon vèsyon tèm yo", "select_society": "Chwazi yon sosyete nasyonal", + "search": "Chèche", + "search_placeholder": "Non oswa imel (omwen 3 karaktè)", "create": "Kreye nouvo itilizatè", "download_report": "Telechaje rapò", "empty": "Yo pa jwenn itilizatè yo", diff --git a/resources/lang/id.json b/resources/lang/id.json index 1ee7201..b1743cc 100644 --- a/resources/lang/id.json +++ b/resources/lang/id.json @@ -343,6 +343,8 @@ "select_country": "Select a country", "select_terms": "Select a terms version", "select_society": "Select a nacional society", + "search": "Cari", + "search_placeholder": "Nama atau email (min. 3 karakter)", "create": "Membuat pengguna baru", "download_report": "Unduh laporan", "empty": "Tidak dapat menemukan pengguna", diff --git a/resources/lang/it.json b/resources/lang/it.json index da36375..9a49f68 100644 --- a/resources/lang/it.json +++ b/resources/lang/it.json @@ -343,6 +343,8 @@ "select_country": "Seleziona un paese", "select_terms": "Seleziona una versione dei termini", "select_society": "Seleziona una società nazionale", + "search": "Cerca", + "search_placeholder": "Nome o email (min. 3 caratteri)", "create": "Crea nuovo utente", "download_report": "Scarica il report", "empty": "Non siamo riusciti a trovare alcun utente", diff --git a/resources/lang/ja.json b/resources/lang/ja.json index 527dff9..b1cd646 100644 --- a/resources/lang/ja.json +++ b/resources/lang/ja.json @@ -343,6 +343,8 @@ "select_country": "国を選択", "select_terms": "利用規約のバージョンを選択", "select_society": "国内協会を選択", + "search": "検索", + "search_placeholder": "名前またはメール(3文字以上)", "create": "新規ユーザーの作成", "download_report": "レポートをダウンロード", "empty": "ユーザーが見つかりません", diff --git a/resources/lang/my.json b/resources/lang/my.json index 7bdf389..a655a06 100644 --- a/resources/lang/my.json +++ b/resources/lang/my.json @@ -343,6 +343,8 @@ "select_country": "နိုင်ငံကို ရွေးချယ်ပါ", "select_terms": "စည်းကမ်းချက်များ ဗားရှင်းကို ရွေးချယ်ပါ", "select_society": "အမျိုးသားအဖွဲ့အစည်းကို ရွေးချယ်ပါ", + "search": "ရှာဖွေရန်", + "search_placeholder": "အမည် သို့မဟုတ် အီးမေးလ် (အနည်းဆုံး စာလုံး 3 လုံး)", "create": "အသုံးပြုသူ အသစ် ဖန်တီးခြင်း", "download_report": "အစီရင်ခံစာကို ဒေါင်းလုဒ်ဆွဲပါ", "empty": "မည်သည့်အသုံးပြုသူကိုမှ မတွေ့ရှိရပါ", diff --git a/resources/lang/ne.json b/resources/lang/ne.json index 9adbd1b..969378e 100644 --- a/resources/lang/ne.json +++ b/resources/lang/ne.json @@ -343,6 +343,8 @@ "select_country": "देश चयन गर्नुहोस्", "select_terms": "सर्त संस्करण चयन गर्नुहोस्", "select_society": "राष्ट्रिय समाज चयन गर्नुहोस्", + "search": "खोज्नुहोस्", + "search_placeholder": "नाम वा इमेल (कम्तीमा ३ अक्षर)", "create": "नयाँ प्रयोगकर्ता बनाउनुहोस्", "download_report": "रिपोर्ट डाउनलोड गर्नुहोस", "empty": "कुनै प्रयोगकर्ता फेला परेन", diff --git a/resources/lang/pt.json b/resources/lang/pt.json index 5f05c31..dfcbeca 100644 --- a/resources/lang/pt.json +++ b/resources/lang/pt.json @@ -343,6 +343,8 @@ "select_country": "Selecione um país", "select_terms": "Selecione uma versão dos termos", "select_society": "Selecione uma sociedade nacional", + "search": "Pesquisar", + "search_placeholder": "Nome ou email (mín. 3 caracteres)", "create": "Criar novo utilizador", "download_report": "Descarregar relatório", "empty": "Não foi possível encontrar utilizadores", diff --git a/resources/lang/ru.json b/resources/lang/ru.json index eafba44..756d848 100644 --- a/resources/lang/ru.json +++ b/resources/lang/ru.json @@ -343,6 +343,8 @@ "select_country": "Выберите страну", "select_terms": "Выберите версию условий", "select_society": "Выберите национальное общество", + "search": "Поиск", + "search_placeholder": "Имя или email (мин. 3 символа)", "create": "Создать нового пользователя", "download_report": "Скачать отчет", "empty": "Пользователи не найдены", diff --git a/resources/lang/rw.json b/resources/lang/rw.json index 6aa55fa..669a38d 100644 --- a/resources/lang/rw.json +++ b/resources/lang/rw.json @@ -343,6 +343,8 @@ "select_country": "Hitamo igihugu", "select_terms": "Hitamo verisiyo y'amasezerano", "select_society": "Hitamo umuryango w'igihugu", + "search": "Shakisha", + "search_placeholder": "Izina cyangwa imeyili (nibura inyuguti 3)", "create": "Shyiraho ukoresha mushya", "download_report": "Kurura raporo", "empty": "Ntubasha kubona umukoresha numwe", diff --git a/resources/lang/sw.json b/resources/lang/sw.json index c498aff..4032940 100644 --- a/resources/lang/sw.json +++ b/resources/lang/sw.json @@ -343,6 +343,8 @@ "select_country": "Chagua nchi", "select_terms": "Chagua toleo la masharti", "select_society": "Chagua jamii ya kitaifa", + "search": "Tafuta", + "search_placeholder": "Jina au barua pepe (angalau herufi 3)", "create": "Unda mtumiaji mpya", "download_report": "Kupakua taarifa", "empty": "Imeshindwa kupata watumiaji wowote", diff --git a/resources/lang/th.json b/resources/lang/th.json index ffd2c6c..d8c0044 100644 --- a/resources/lang/th.json +++ b/resources/lang/th.json @@ -343,6 +343,8 @@ "select_country": "เลือกประเทศ", "select_terms": "เลือกเวอร์ชันข้อกำหนด", "select_society": "เลือกสมาคมระดับชาติ", + "search": "ค้นหา", + "search_placeholder": "ชื่อหรืออีเมล (อย่างน้อย 3 ตัวอักษร)", "create": "สร้างบัญชีผู้ใช้ใหม่", "download_report": "ดาวน์โหลดรายงาน", "empty": "ไม่พบบัญชีผู้ใช้ใดๆ", diff --git a/resources/lang/tr.json b/resources/lang/tr.json index 2be00a6..556df0e 100644 --- a/resources/lang/tr.json +++ b/resources/lang/tr.json @@ -343,6 +343,8 @@ "select_country": "Ülke seçin", "select_terms": "Şartlar sürümü seçin", "select_society": "Ulusal topluluk seçin", + "search": "Ara", + "search_placeholder": "Ad veya e-posta (en az 3 karakter)", "create": "Yeni kullanıcı oluştur", "download_report": "Raporu indir", "empty": "Kullanıcı bulunamadı", diff --git a/resources/lang/ur.json b/resources/lang/ur.json index eb56790..08a5cc7 100644 --- a/resources/lang/ur.json +++ b/resources/lang/ur.json @@ -343,6 +343,8 @@ "select_country": "ملک منتخب کریں", "select_terms": "شرائط کا ورژن منتخب کریں", "select_society": "قومی سوسائٹی منتخب کریں", + "search": "تلاش کریں", + "search_placeholder": "نام یا ای میل (کم از کم 3 حروف)", "create": "ایک نیا صارف بنائیں", "download_report": "رپورٹ ڈاؤن لوڈ کریں", "empty": "کوئی بھی صارف نہیں ڈھونڈ سکا", diff --git a/resources/lang/vi.json b/resources/lang/vi.json index 1f2d23a..269a71b 100644 --- a/resources/lang/vi.json +++ b/resources/lang/vi.json @@ -343,6 +343,8 @@ "select_country": "Chọn quốc gia", "select_terms": "Chọn phiên bản điều khoản", "select_society": "Chọn hội quốc gia", + "search": "Tìm kiếm", + "search_placeholder": "Tên hoặc email (tối thiểu 3 ký tự)", "create": "Tạo người dùng mới", "download_report": "Tải xuống báo cáo", "empty": "Không tìm thấy người dùng nào", diff --git a/resources/lang/zh.json b/resources/lang/zh.json index 92aec53..cd8ca87 100644 --- a/resources/lang/zh.json +++ b/resources/lang/zh.json @@ -343,6 +343,8 @@ "select_country": "选择国家/地区", "select_terms": "选择条款版本", "select_society": "选择国家协会", + "search": "搜索", + "search_placeholder": "姓名或电子邮件(至少 3 个字符)", "create": "创建新用户", "download_report": "下载报告", "empty": "找不到用户", diff --git a/resources/lang/zh_CN.json b/resources/lang/zh_CN.json index a435c58..5bacbd3 100644 --- a/resources/lang/zh_CN.json +++ b/resources/lang/zh_CN.json @@ -343,6 +343,8 @@ "select_country": "选择国家/地区", "select_terms": "选择条款版本", "select_society": "选择国家协会", + "search": "搜索", + "search_placeholder": "姓名或电子邮件(至少 3 个字符)", "create": "創建一個新用戶", "download_report": "下載報告", "empty": "無法找到任何用戶", diff --git a/tests/Feature/UserTest.php b/tests/Feature/UserTest.php index 09e0194..7d52e61 100644 --- a/tests/Feature/UserTest.php +++ b/tests/Feature/UserTest.php @@ -179,6 +179,55 @@ public function get_admin_user_list() ]); } + /** @test */ + public function get_admin_user_list_can_sort_by_profile_last_name() + { + $adminUser = factory(User::class)->create(); + $adminUser->userProfile()->save(factory(UserProfile::class)->make([ + 'first_name' => 'Able', + 'last_name' => 'Zed', + ])); + + $duplicateProfile = factory(UserProfile::class)->make([ + 'first_name' => 'Able', + 'last_name' => 'Aardvark', + ]); + $duplicateProfile->user_id = $adminUser->id; + $duplicateProfile->save(); + + $role = Role::create(['name' => 'Super Admin']); + $role->permissions() + ->sync([ + Permission::where('name', '=', 'users-list')->first()->id, + ]); + $adminUser->roles()->attach($role->id); + + $this->actingAs($adminUser) + ->getJson('/api/users/admins?page=1&orderBy=last_name&sort=asc') + ->assertSuccessful() + ->assertJsonCount(1, 'data') + ->assertJsonStructure([ + 'data' => [ + '*' => $this->getUserJsonStructure() + ], + 'links' => [ + 'first', + 'last', + 'prev', + 'next' + ], + 'meta' => [ + 'current_page', + 'from', + 'last_page', + 'path', + 'per_page', + 'to', + 'total' + ] + ]); + } + /** @test */ public function get_user_list_with_pagination() {