Skip to content

Commit 1a65d08

Browse files
authored
Merge pull request #24 from fleetbase/dev-v0.1.7
Added tabbed user type sections to users management
2 parents 5b12288 + 0fb1678 commit 1a65d08

File tree

26 files changed

+321
-86
lines changed

26 files changed

+321
-86
lines changed

addon/controllers/users.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import Controller from '@ember/controller';
2+
import { inject as service } from '@ember/service';
3+
import { getOwner } from '@ember/application';
4+
5+
export default class UsersController extends Controller {
6+
@service hostRouter;
7+
8+
get tabs() {
9+
return [
10+
{
11+
route: 'users.index',
12+
label: 'Users',
13+
},
14+
{
15+
route: 'users.drivers',
16+
label: 'Drivers',
17+
},
18+
{
19+
route: 'users.customers',
20+
label: 'Customers',
21+
},
22+
];
23+
}
24+
25+
get childController() {
26+
const owner = getOwner(this);
27+
const fullRouteName = this.hostRouter.currentRouteName;
28+
29+
// strip engine mount prefix once
30+
const mount = owner.mountPoint;
31+
let local = fullRouteName;
32+
if (mount && local.startsWith(mount + '.')) {
33+
local = local.slice(mount.length + 1);
34+
}
35+
36+
const childController = owner.lookup(`controller:${local}`);
37+
return childController;
38+
}
39+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import UsersIndexController from './index';
2+
3+
export default class UsersCustomersController extends UsersIndexController {}

addon/controllers/users/drivers.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import UsersIndexController from './index';
2+
3+
export default class UsersDriversController extends UsersIndexController {}

addon/controllers/users/index.js

Lines changed: 71 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,40 +16,55 @@ export default class UsersIndexController extends Controller {
1616
@service fetch;
1717
@service abilities;
1818
@service filters;
19+
@service tableContext;
1920

20-
/**
21-
* Queryable parameters for this controller's model
22-
*
23-
* @var {Array}
24-
*/
25-
queryParams = ['page', 'limit', 'sort', 'query', 'type', 'created_by', 'updated_by', 'status', 'role', 'name'];
21+
/** action buttons */
22+
get actionButtons() {
23+
return [
24+
{
25+
icon: 'refresh',
26+
onClick: () => this.hostRouter.refresh(),
27+
helpText: this.intl.t('common.refresh'),
28+
},
29+
{
30+
text: this.intl.t('common.new'),
31+
type: 'primary',
32+
icon: 'plus',
33+
permission: 'iam create user',
34+
onClick: this.createUser,
35+
},
36+
{
37+
text: this.intl.t('common.export'),
38+
icon: 'long-arrow-up',
39+
iconClass: 'rotate-icon-45',
40+
wrapperClass: 'hidden md:flex',
41+
permission: 'iam export user',
42+
onClick: this.exportUsers,
43+
},
44+
];
45+
}
2646

27-
/**
28-
* The current page of data being viewed
29-
*
30-
* @var {Integer}
31-
*/
32-
@tracked page = 1;
47+
/** bulk actions */
48+
get bulkActions() {
49+
const selected = this.tableContext.getSelectedRows();
3350

34-
/**
35-
* The maximum number of items to show per page
36-
*
37-
* @var {Integer}
38-
*/
39-
@tracked limit;
51+
return [
52+
{
53+
label: this.intl.t('common.delete-selected-count', { count: selected.length }),
54+
class: 'text-red-500',
55+
fn: this.bulkDeleteUsers,
56+
},
57+
];
58+
}
4059

41-
/**
42-
* The search query param
43-
*
44-
* @var {Integer}
45-
*/
60+
queryParams = ['page', 'limit', 'sort', 'query', 'type', 'created_by', 'updated_by', 'status', 'role', 'name', 'phone', 'email'];
61+
@tracked page = 1;
62+
@tracked limit;
4663
@tracked query;
47-
48-
/**
49-
* The param to sort the data on, the param with prepended `-` is descending
50-
*
51-
* @var {String}
52-
*/
64+
@tracked name;
65+
@tracked phone;
66+
@tracked email;
67+
@tracked role;
5368
@tracked sort = '-created_at';
5469

5570
/**
@@ -59,9 +74,9 @@ export default class UsersIndexController extends Controller {
5974
*/
6075
@tracked columns = [
6176
{
77+
sticky: true,
6278
label: this.intl.t('iam.common.name'),
6379
valuePath: 'name',
64-
width: '160px',
6580
cellComponent: 'table/cell/user-name',
6681
permission: 'iam view user',
6782
mediaPath: 'avatar_url',
@@ -72,24 +87,28 @@ export default class UsersIndexController extends Controller {
7287
filterComponent: 'filter/string',
7388
},
7489
{
90+
sticky: true,
7591
label: this.intl.t('iam.common.email'),
7692
valuePath: 'email',
7793
cellComponent: 'click-to-copy',
78-
sortable: false,
79-
width: '12%',
94+
resizable: true,
95+
sortable: true,
96+
filterable: true,
97+
filterComponent: 'filter/string',
8098
},
8199
{
82100
label: this.intl.t('iam.common.phone'),
83101
valuePath: 'phone',
84102
cellComponent: 'click-to-copy',
85-
sortable: false,
86-
width: '12%',
103+
resizable: true,
104+
sortable: true,
105+
filterable: true,
106+
filterComponent: 'filter/string',
87107
},
88108
{
89109
label: this.intl.t('iam.common.role'),
90110
valuePath: 'role.name',
91111
sortable: false,
92-
width: '10%',
93112
filterable: true,
94113
filterComponent: 'filter/model',
95114
filterComponentPlaceholder: 'Select role',
@@ -100,17 +119,15 @@ export default class UsersIndexController extends Controller {
100119
label: this.intl.t('iam.common.status'),
101120
valuePath: 'session_status',
102121
sortable: false,
103-
width: '12%',
104122
cellComponent: 'table/cell/status',
105123
filterable: true,
106124
filterComponent: 'filter/select',
107125
filterParam: 'status',
108-
filterOptions: ['pending', 'active'],
126+
filterOptions: ['pending', 'active', 'inactive'],
109127
},
110128
{
111129
label: this.intl.t('iam.users.index.last-login'),
112130
valuePath: 'lastLogin',
113-
width: '130px',
114131
resizable: true,
115132
sortable: false,
116133
filterable: false,
@@ -120,7 +137,6 @@ export default class UsersIndexController extends Controller {
120137
label: this.intl.t('iam.users.index.created-at'),
121138
valuePath: 'createdAt',
122139
sortParam: 'created_at',
123-
width: '140px',
124140
resizable: true,
125141
sortable: false,
126142
filterable: false,
@@ -130,7 +146,6 @@ export default class UsersIndexController extends Controller {
130146
label: this.intl.t('iam.users.index.updated-at'),
131147
valuePath: 'updatedAt',
132148
sortParam: 'updated_at',
133-
width: '130px',
134149
resizable: true,
135150
hidden: true,
136151
sortable: false,
@@ -146,7 +161,8 @@ export default class UsersIndexController extends Controller {
146161
ddMenuLabel: this.intl.t('iam.users.index.user-actions'),
147162
cellClassNames: 'overflow-visible',
148163
wrapperClass: 'flex items-center justify-end mx-2',
149-
width: '10%',
164+
sticky: 'right',
165+
width: 60,
150166
actions: [
151167
{
152168
label: this.intl.t('iam.users.index.edit-user'),
@@ -324,6 +340,7 @@ export default class UsersIndexController extends Controller {
324340
acceptButtonIcon: 'save',
325341
acceptButtonDisabled: this.abilities.cannot(formPermission),
326342
acceptButtonHelpText: this.abilities.cannot(formPermission) ? this.intl.t('common.unauthorized') : null,
343+
keepOpen: true,
327344
formPermission,
328345
user,
329346
uploadNewPhoto: (file) => {
@@ -354,9 +371,16 @@ export default class UsersIndexController extends Controller {
354371
try {
355372
await user.save();
356373
this.notifications.success(this.intl.t('iam.users.index.user-changes-saved-success'));
357-
return this.hostRouter.refresh();
374+
this.hostRouter.refresh();
375+
modal.done();
358376
} catch (error) {
359377
this.notifications.serverError(error);
378+
379+
// If error is because email address was made empty rollback changes
380+
if (error && typeof error.message === 'string' && error.message.includes('Email address cannot be empty')) {
381+
user.rollbackAttributes();
382+
}
383+
360384
modal.stopLoading();
361385
}
362386
},
@@ -379,6 +403,7 @@ export default class UsersIndexController extends Controller {
379403
body: this.intl.t('iam.users.index.data-assosciated-user-delete'),
380404
confirm: async (modal) => {
381405
modal.startLoading();
406+
382407
try {
383408
await user.removeFromCurrentCompany();
384409
this.notifications.success(this.intl.t('iam.users.index.delete-user-success-message', { userName: user.get('name') }));
@@ -402,6 +427,7 @@ export default class UsersIndexController extends Controller {
402427
body: this.intl.t('iam.users.index.access-account-or-resources-unless-re-activated'),
403428
confirm: async (modal) => {
404429
modal.startLoading();
430+
405431
try {
406432
await user.deactivate();
407433
this.notifications.success(this.intl.t('iam.users.index.deactivate-user-success-message', { userName: user.get('name') }));
@@ -425,6 +451,7 @@ export default class UsersIndexController extends Controller {
425451
body: this.intl.t('iam.users.index.this-user-will-regain-access-to-your-organization'),
426452
confirm: async (modal) => {
427453
modal.startLoading();
454+
428455
try {
429456
await user.activate();
430457
this.notifications.success(this.intl.t('iam.users.index.re-activate-user-success-message', { userName: user.get('name') }));
@@ -448,6 +475,7 @@ export default class UsersIndexController extends Controller {
448475
body: this.intl.t('iam.users.index.verify-user-manually-prompt'),
449476
confirm: async (modal) => {
450477
modal.startLoading();
478+
451479
try {
452480
await user.verify();
453481
this.notifications.success(this.intl.t('iam.users.index.user-verified-success-message', { userName: user.get('name') }));
@@ -483,6 +511,7 @@ export default class UsersIndexController extends Controller {
483511
body: this.intl.t('iam.users.index.confirming-fleetbase-will-re-send-invitation-for-user-to-join-your-organization'),
484512
confirm: async (modal) => {
485513
modal.startLoading();
514+
486515
try {
487516
await user.resendInvite();
488517
this.notifications.success(this.intl.t('iam.users.index.invitation-resent'));

addon/routes.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import buildRoutes from 'ember-engines/routes';
22

33
export default buildRoutes(function () {
44
this.route('home', { path: '/' }, function () {});
5-
this.route('users', function () {});
5+
this.route('users', function () {
6+
this.route('index', { path: '/' });
7+
this.route('drivers');
8+
this.route('customers');
9+
});
610
this.route('groups', function () {});
711
this.route('roles', function () {});
812
this.route('policies', function () {});

addon/routes/users/customers.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import Route from '@ember/routing/route';
2+
import { inject as service } from '@ember/service';
3+
4+
export default class UsersCustomersRoute extends Route {
5+
@service store;
6+
7+
queryParams = {
8+
page: { refreshModel: true },
9+
limit: { refreshModel: true },
10+
sort: { refreshModel: true },
11+
query: { refreshModel: true },
12+
status: { refreshModel: true },
13+
role: { refreshModel: true },
14+
name: { refreshModel: true },
15+
phone: { refreshModel: true },
16+
email: { refreshModel: true },
17+
};
18+
19+
model(params) {
20+
return this.store.query('user', { ...params, is_customer: 1 });
21+
}
22+
}

addon/routes/users/drivers.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import Route from '@ember/routing/route';
2+
import { inject as service } from '@ember/service';
3+
4+
export default class UsersDriversRoute extends Route {
5+
@service store;
6+
7+
queryParams = {
8+
page: { refreshModel: true },
9+
limit: { refreshModel: true },
10+
sort: { refreshModel: true },
11+
query: { refreshModel: true },
12+
status: { refreshModel: true },
13+
role: { refreshModel: true },
14+
name: { refreshModel: true },
15+
phone: { refreshModel: true },
16+
email: { refreshModel: true },
17+
};
18+
19+
model(params) {
20+
return this.store.query('user', { ...params, is_driver: 1 });
21+
}
22+
}

addon/routes/users/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ export default class UsersIndexRoute extends Route {
1212
status: { refreshModel: true },
1313
role: { refreshModel: true },
1414
name: { refreshModel: true },
15+
phone: { refreshModel: true },
16+
email: { refreshModel: true },
1517
};
1618

1719
model(params) {
18-
return this.store.query('user', params);
20+
return this.store.query('user', { ...params, is_user: 1 });
1921
}
2022
}

addon/templates/users.hbs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,19 @@
1-
{{outlet}}
1+
<TabNavigation @tabs={{this.tabs}} @size="lg" @tablistClass="next-view-section-subheader-fixed-height pl-2">
2+
<:actions>
3+
<div class="flex flex-row items-center space-x-1 pr-4">
4+
<Layout::Resource::TabularActions
5+
@columns={{this.childController.columns}}
6+
@actionButtons={{this.childController.actionButtons}}
7+
@bulkActions={{this.childController.bulkActions}}
8+
@searchQuery={{this.childController.query}}
9+
@onSearch={{perform this.childController.search}}
10+
@table={{this.childController.table}}
11+
@bulkActionIconOnly={{true}}
12+
@controller={{this.childController}}
13+
/>
14+
</div>
15+
</:actions>
16+
<:default>
17+
{{outlet}}
18+
</:default>
19+
</TabNavigation>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Layout::Section::Body>
2+
<Table
3+
@rows={{@model}}
4+
@columns={{this.columns}}
5+
@selectable={{true}}
6+
@canSelectAll={{true}}
7+
@onSetup={{fn (mut this.table)}}
8+
@pagination={{true}}
9+
@paginationMeta={{@model.meta}}
10+
@page={{this.page}}
11+
@onPageChange={{fn (mut this.page)}}
12+
/>
13+
</Layout::Section::Body>
14+
{{outlet}}

0 commit comments

Comments
 (0)