Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions addon/controllers/users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { getOwner } from '@ember/application';

export default class UsersController extends Controller {
@service hostRouter;

get tabs() {
return [
{
route: 'users.index',
label: 'Users',
},
{
route: 'users.drivers',
label: 'Drivers',
},
{
route: 'users.customers',
label: 'Customers',
},
];
}

get childController() {
const owner = getOwner(this);
const fullRouteName = this.hostRouter.currentRouteName;

// strip engine mount prefix once
const mount = owner.mountPoint;
let local = fullRouteName;
if (mount && local.startsWith(mount + '.')) {
local = local.slice(mount.length + 1);
}

const childController = owner.lookup(`controller:${local}`);
return childController;
}
}
3 changes: 3 additions & 0 deletions addon/controllers/users/customers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import UsersIndexController from './index';

export default class UsersCustomersController extends UsersIndexController {}
3 changes: 3 additions & 0 deletions addon/controllers/users/drivers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import UsersIndexController from './index';

export default class UsersDriversController extends UsersIndexController {}
113 changes: 71 additions & 42 deletions addon/controllers/users/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,55 @@ export default class UsersIndexController extends Controller {
@service fetch;
@service abilities;
@service filters;
@service tableContext;

/**
* Queryable parameters for this controller's model
*
* @var {Array}
*/
queryParams = ['page', 'limit', 'sort', 'query', 'type', 'created_by', 'updated_by', 'status', 'role', 'name'];
/** action buttons */
get actionButtons() {
return [
{
icon: 'refresh',
onClick: () => this.hostRouter.refresh(),
helpText: this.intl.t('common.refresh'),
},
{
text: this.intl.t('common.new'),
type: 'primary',
icon: 'plus',
permission: 'iam create user',
onClick: this.createUser,
},
{
text: this.intl.t('common.export'),
icon: 'long-arrow-up',
iconClass: 'rotate-icon-45',
wrapperClass: 'hidden md:flex',
permission: 'iam export user',
onClick: this.exportUsers,
},
];
}

/**
* The current page of data being viewed
*
* @var {Integer}
*/
@tracked page = 1;
/** bulk actions */
get bulkActions() {
const selected = this.tableContext.getSelectedRows();

/**
* The maximum number of items to show per page
*
* @var {Integer}
*/
@tracked limit;
return [
{
label: this.intl.t('common.delete-selected-count', { count: selected.length }),
class: 'text-red-500',
fn: this.bulkDeleteUsers,
},
];
}

/**
* The search query param
*
* @var {Integer}
*/
queryParams = ['page', 'limit', 'sort', 'query', 'type', 'created_by', 'updated_by', 'status', 'role', 'name', 'phone', 'email'];
@tracked page = 1;
@tracked limit;
@tracked query;

/**
* The param to sort the data on, the param with prepended `-` is descending
*
* @var {String}
*/
@tracked name;
@tracked phone;
@tracked email;
@tracked role;
@tracked sort = '-created_at';

/**
Expand All @@ -59,9 +74,9 @@ export default class UsersIndexController extends Controller {
*/
@tracked columns = [
{
sticky: true,
label: this.intl.t('iam.common.name'),
valuePath: 'name',
width: '160px',
cellComponent: 'table/cell/user-name',
permission: 'iam view user',
mediaPath: 'avatar_url',
Expand All @@ -72,24 +87,28 @@ export default class UsersIndexController extends Controller {
filterComponent: 'filter/string',
},
{
sticky: true,
label: this.intl.t('iam.common.email'),
valuePath: 'email',
cellComponent: 'click-to-copy',
sortable: false,
width: '12%',
resizable: true,
sortable: true,
filterable: true,
filterComponent: 'filter/string',
},
{
label: this.intl.t('iam.common.phone'),
valuePath: 'phone',
cellComponent: 'click-to-copy',
sortable: false,
width: '12%',
resizable: true,
sortable: true,
filterable: true,
filterComponent: 'filter/string',
},
{
label: this.intl.t('iam.common.role'),
valuePath: 'role.name',
sortable: false,
width: '10%',
filterable: true,
filterComponent: 'filter/model',
filterComponentPlaceholder: 'Select role',
Expand All @@ -100,17 +119,15 @@ export default class UsersIndexController extends Controller {
label: this.intl.t('iam.common.status'),
valuePath: 'session_status',
sortable: false,
width: '12%',
cellComponent: 'table/cell/status',
filterable: true,
filterComponent: 'filter/select',
filterParam: 'status',
filterOptions: ['pending', 'active'],
filterOptions: ['pending', 'active', 'inactive'],
},
{
label: this.intl.t('iam.users.index.last-login'),
valuePath: 'lastLogin',
width: '130px',
resizable: true,
sortable: false,
filterable: false,
Expand All @@ -120,7 +137,6 @@ export default class UsersIndexController extends Controller {
label: this.intl.t('iam.users.index.created-at'),
valuePath: 'createdAt',
sortParam: 'created_at',
width: '140px',
resizable: true,
sortable: false,
filterable: false,
Expand All @@ -130,7 +146,6 @@ export default class UsersIndexController extends Controller {
label: this.intl.t('iam.users.index.updated-at'),
valuePath: 'updatedAt',
sortParam: 'updated_at',
width: '130px',
resizable: true,
hidden: true,
sortable: false,
Expand All @@ -146,7 +161,8 @@ export default class UsersIndexController extends Controller {
ddMenuLabel: this.intl.t('iam.users.index.user-actions'),
cellClassNames: 'overflow-visible',
wrapperClass: 'flex items-center justify-end mx-2',
width: '10%',
sticky: 'right',
width: 60,
actions: [
{
label: this.intl.t('iam.users.index.edit-user'),
Expand Down Expand Up @@ -324,6 +340,7 @@ export default class UsersIndexController extends Controller {
acceptButtonIcon: 'save',
acceptButtonDisabled: this.abilities.cannot(formPermission),
acceptButtonHelpText: this.abilities.cannot(formPermission) ? this.intl.t('common.unauthorized') : null,
keepOpen: true,
formPermission,
user,
uploadNewPhoto: (file) => {
Expand Down Expand Up @@ -354,9 +371,16 @@ export default class UsersIndexController extends Controller {
try {
await user.save();
this.notifications.success(this.intl.t('iam.users.index.user-changes-saved-success'));
return this.hostRouter.refresh();
this.hostRouter.refresh();
modal.done();
} catch (error) {
this.notifications.serverError(error);

// If error is because email address was made empty rollback changes
if (error && typeof error.message === 'string' && error.message.includes('Email address cannot be empty')) {
user.rollbackAttributes();
}

modal.stopLoading();
}
},
Expand All @@ -379,6 +403,7 @@ export default class UsersIndexController extends Controller {
body: this.intl.t('iam.users.index.data-assosciated-user-delete'),
confirm: async (modal) => {
modal.startLoading();

try {
await user.removeFromCurrentCompany();
this.notifications.success(this.intl.t('iam.users.index.delete-user-success-message', { userName: user.get('name') }));
Expand All @@ -402,6 +427,7 @@ export default class UsersIndexController extends Controller {
body: this.intl.t('iam.users.index.access-account-or-resources-unless-re-activated'),
confirm: async (modal) => {
modal.startLoading();

try {
await user.deactivate();
this.notifications.success(this.intl.t('iam.users.index.deactivate-user-success-message', { userName: user.get('name') }));
Expand All @@ -425,6 +451,7 @@ export default class UsersIndexController extends Controller {
body: this.intl.t('iam.users.index.this-user-will-regain-access-to-your-organization'),
confirm: async (modal) => {
modal.startLoading();

try {
await user.activate();
this.notifications.success(this.intl.t('iam.users.index.re-activate-user-success-message', { userName: user.get('name') }));
Expand All @@ -448,6 +475,7 @@ export default class UsersIndexController extends Controller {
body: this.intl.t('iam.users.index.verify-user-manually-prompt'),
confirm: async (modal) => {
modal.startLoading();

try {
await user.verify();
this.notifications.success(this.intl.t('iam.users.index.user-verified-success-message', { userName: user.get('name') }));
Expand Down Expand Up @@ -483,6 +511,7 @@ export default class UsersIndexController extends Controller {
body: this.intl.t('iam.users.index.confirming-fleetbase-will-re-send-invitation-for-user-to-join-your-organization'),
confirm: async (modal) => {
modal.startLoading();

try {
await user.resendInvite();
this.notifications.success(this.intl.t('iam.users.index.invitation-resent'));
Expand Down
6 changes: 5 additions & 1 deletion addon/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import buildRoutes from 'ember-engines/routes';

export default buildRoutes(function () {
this.route('home', { path: '/' }, function () {});
this.route('users', function () {});
this.route('users', function () {
this.route('index', { path: '/' });
this.route('drivers');
this.route('customers');
});
this.route('groups', function () {});
this.route('roles', function () {});
this.route('policies', function () {});
Expand Down
22 changes: 22 additions & 0 deletions addon/routes/users/customers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class UsersCustomersRoute extends Route {
@service store;

queryParams = {
page: { refreshModel: true },
limit: { refreshModel: true },
sort: { refreshModel: true },
query: { refreshModel: true },
status: { refreshModel: true },
role: { refreshModel: true },
name: { refreshModel: true },
phone: { refreshModel: true },
email: { refreshModel: true },
};

model(params) {
return this.store.query('user', { ...params, is_customer: 1 });
}
}
22 changes: 22 additions & 0 deletions addon/routes/users/drivers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class UsersDriversRoute extends Route {
@service store;

queryParams = {
page: { refreshModel: true },
limit: { refreshModel: true },
sort: { refreshModel: true },
query: { refreshModel: true },
status: { refreshModel: true },
role: { refreshModel: true },
name: { refreshModel: true },
phone: { refreshModel: true },
email: { refreshModel: true },
};

model(params) {
return this.store.query('user', { ...params, is_driver: 1 });
}
}
4 changes: 3 additions & 1 deletion addon/routes/users/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ export default class UsersIndexRoute extends Route {
status: { refreshModel: true },
role: { refreshModel: true },
name: { refreshModel: true },
phone: { refreshModel: true },
email: { refreshModel: true },
};

model(params) {
return this.store.query('user', params);
return this.store.query('user', { ...params, is_user: 1 });
}
}
20 changes: 19 additions & 1 deletion addon/templates/users.hbs
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
{{outlet}}
<TabNavigation @tabs={{this.tabs}} @size="lg" @tablistClass="next-view-section-subheader-fixed-height pl-2">
<:actions>
<div class="flex flex-row items-center space-x-1 pr-4">
<Layout::Resource::TabularActions
@columns={{this.childController.columns}}
@actionButtons={{this.childController.actionButtons}}
@bulkActions={{this.childController.bulkActions}}
@searchQuery={{this.childController.query}}
@onSearch={{perform this.childController.search}}
@table={{this.childController.table}}
@bulkActionIconOnly={{true}}
@controller={{this.childController}}
/>
</div>
</:actions>
<:default>
{{outlet}}
</:default>
</TabNavigation>
14 changes: 14 additions & 0 deletions addon/templates/users/customers.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Layout::Section::Body>
<Table
@rows={{@model}}
@columns={{this.columns}}
@selectable={{true}}
@canSelectAll={{true}}
@onSetup={{fn (mut this.table)}}
@pagination={{true}}
@paginationMeta={{@model.meta}}
@page={{this.page}}
@onPageChange={{fn (mut this.page)}}
/>
</Layout::Section::Body>
{{outlet}}
Loading
Loading