diff --git a/frontend/src/metabase/admin/people/containers/PeopleListingApp.jsx b/frontend/src/metabase/admin/people/containers/PeopleListingApp.jsx index a022aaca4e46d52e327ce2d86117c390489806ab..a4f7123a1b55490a319f2f63052a81de11050412 100644 --- a/frontend/src/metabase/admin/people/containers/PeopleListingApp.jsx +++ b/frontend/src/metabase/admin/people/containers/PeopleListingApp.jsx @@ -2,7 +2,6 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; import { Link } from "react-router"; -import _ from "underscore"; import { connect } from "react-redux"; import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper.jsx"; @@ -30,7 +29,7 @@ export const MODAL_RESET_PASSWORD_EMAIL = "MODAL_RESET_PASSWORD_EMAIL"; export const MODAL_USER_ADDED_WITH_INVITE = "MODAL_USER_ADDED_WITH_INVITE"; export const MODAL_USER_ADDED_WITH_PASSWORD = "MODAL_USER_ADDED_WITH_PASSWORD"; -import { getUsers, getModal, getGroups } from "../selectors"; +import { getSortedUsers, getModal, getGroups } from "../selectors"; import { createUser, deleteUser, @@ -48,7 +47,7 @@ import { const mapStateToProps = (state, props) => { return { - users: getUsers(state, props), + users: getSortedUsers(state, props), modal: getModal(state, props), user: state.currentUser, groups: getGroups(state, props), @@ -402,8 +401,6 @@ export default class PeopleListingApp extends Component { let { modal, users, groups } = this.props; let { error } = this.state; - users = _.values(users).sort((a, b) => b.date_joined - a.date_joined); - return ( <LoadingAndErrorWrapper loading={!users} error={error}> {() => ( diff --git a/frontend/src/metabase/admin/people/selectors.js b/frontend/src/metabase/admin/people/selectors.js index 358d1aad1698b253aa0501ff8810c7ed09756248..577178f90162f4f09118909c5d42882b207f6038 100644 --- a/frontend/src/metabase/admin/people/selectors.js +++ b/frontend/src/metabase/admin/people/selectors.js @@ -23,3 +23,19 @@ export const getUsers = createSelector( .value(), })), ); + +// sort the users list by last_name, ignore case or diacritical marks. If last names are the same then compare by first +// name +const compareNames = (a, b) => + a.localeCompare(b, undefined, { sensitivty: "base" }); + +export const getSortedUsers = createSelector( + [getUsers], + users => + users && + _.values(users).sort( + (a, b) => + compareNames(a.last_name, b.last_name) || + compareNames(a.first_name, b.first_name), + ), +); diff --git a/src/metabase/api/user.clj b/src/metabase/api/user.clj index fdb7a6b7191c02fdf13da5a6bcf85fa60549c9c2..342e59eb7a586eb2c79620fc78daf83c00653b92 100644 --- a/src/metabase/api/user.clj +++ b/src/metabase/api/user.clj @@ -23,7 +23,9 @@ "Fetch a list of all active `Users` for the admin People page." [] (db/select [User :id :first_name :last_name :email :is_superuser :google_auth :ldap_auth :last_login] - :is_active true)) + :is_active true + {:order-by [[:%lower.last_name :asc] + [:%lower.first_name :asc]]})) (defn- reactivate-user! [existing-user first-name last-name] (when-not (:is_active existing-user)