Skip to content

Commit fb04dcc

Browse files
committed
page working
1 parent 872ce47 commit fb04dcc

12 files changed

+111
-33
lines changed

src/api/routers/adminRouter.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import express from 'express';
22

3-
import { isUid2SuperuserCheck2 } from '../middleware/userRoleMiddleware';
3+
import { isSuperUserCheck } from '../middleware/userRoleMiddleware';
44
import { getAdminUserList } from '../services/usersService';
55
// import { getSiteList, getVisibleSiteList } from '../services/adminServiceClient';
66
// import { AdminSiteDTO, mapAdminSitesToSiteDTOs } from '../services/adminServiceHelpers';
@@ -9,7 +9,7 @@ import { getAdminUserList } from '../services/usersService';
99
export function createAdminRouter() {
1010
const adminRouter = express.Router();
1111

12-
adminRouter.get('/users', isUid2SuperuserCheck2, async (_req, res) => {
12+
adminRouter.get('/users', isSuperUserCheck, async (_req, res) => {
1313
// const allSitesPromise = getSiteList();
1414
// const attachedSitesPromise = getAttachedSiteIDs();
1515
// const [allSites, attachedSites] = await Promise.all([allSitesPromise, attachedSitesPromise]);

src/api/routers/businessContactsRouter.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import express, { Response } from 'express';
22

3-
import { BusinessContactSchema } from '../entities/BusinessContact';
3+
import { BusinessContact, BusinessContactSchema } from '../entities/BusinessContact';
44
import {
55
BusinessContactRequest,
66
hasBusinessContactAccess,
@@ -18,14 +18,16 @@ export function createBusinessContactsRouter() {
1818

1919
businessContactsRouter.get('/', async (req: ParticipantRequest, res: Response) => {
2020
const { participant } = req;
21-
const businessContacts = await participant!.$relatedQuery('businessContacts');
21+
const businessContacts = await BusinessContact.query().where('participantId', participant?.id!);
2222
return res.status(200).json(businessContacts);
2323
});
2424

2525
businessContactsRouter.post('/', async (req: ParticipantRequest, res: Response) => {
2626
const data = BusinessContactsDTO.parse(req.body);
2727
const { participant } = req;
28-
const newContact = await participant!.$relatedQuery('businessContacts').insert(data);
28+
const newContact = await BusinessContact.query()
29+
.where('participantId', participant?.id!)
30+
.insert({ ...data, participantId: participant?.id! });
2931
return res.status(201).json(newContact);
3032
});
3133

src/api/services/usersService.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ export const inviteUserToParticipant = async (
200200
};
201201

202202
export const getAdminUserList = async () => {
203-
const userList = User.query();
203+
// do we want any other filtering here?
204+
const userList = await User.query().where('deleted', 0).orderBy('email');
204205
return userList;
205206
};

src/database/migrations/20250228040900_CreateSuperUserRole.ts

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { Knex } from 'knex';
22

33
export async function up(knex: Knex): Promise<void> {
44
await knex('userRoles').insert({
5-
id: 4,
65
roleName: 'Super User',
76
});
87
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.user-management-item {
2+
line-height: 2;
3+
4+
.user-item-name-cell {
5+
display: flex;
6+
align-items: center;
7+
}
8+
9+
.approve-button {
10+
padding-right: 0;
11+
}
12+
13+
.approver-date {
14+
margin-right: 80px;
15+
}
16+
17+
.approver-name {
18+
margin-right: 40px;
19+
}
20+
}

src/web/components/UserManagement/UserManagementItem.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ type UserManagementItemProps = Readonly<{
99
export function UserManagementItem({ user }: UserManagementItemProps) {
1010
return (
1111
<tr className='user-management-item'>
12+
<td>{user.email}</td>
13+
<td>{user.firstName}</td>
1214
<td>{user.lastName}</td>
15+
<td>{user.jobFunction}</td>
16+
<td>{user.acceptedTerms}</td>
17+
<td />
18+
<td />
1319
</tr>
1420
);
1521
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
.users-table-container {
2+
.users-table {
3+
width: 100%;
4+
5+
tr th {
6+
font-size: 0.875rem;
7+
line-height: 1.5;
8+
}
9+
}
10+
11+
.users-table-header {
12+
display: flex;
13+
justify-content: end;
14+
align-items: baseline;
15+
padding-bottom: 10px;
16+
17+
&-right {
18+
display: flex;
19+
justify-content: right;
20+
}
21+
22+
.users-search-bar-container {
23+
display: flex;
24+
align-items: center;
25+
border-bottom: 2px solid var(--theme-action);
26+
padding: 6px 2px;
27+
::placeholder {
28+
color: var(--theme-search-text);
29+
opacity: 1;
30+
}
31+
}
32+
33+
.users-search-bar-icon {
34+
color: var(--theme-search-text);
35+
height: 16px;
36+
margin-left: 8px;
37+
}
38+
39+
.users-search-bar {
40+
width: 100%;
41+
border: none;
42+
outline: none;
43+
color: var(--theme-search-text);
44+
font-weight: 400;
45+
font-size: 0.75rem;
46+
background: none;
47+
}
48+
}
49+
}

src/web/components/UserManagement/UserManagementTable.tsx

+12-8
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ function UserManagementTableContent({ users }: UserManagementTableProps) {
5959
if (searchText.length > 1) {
6060
searchedUsers = users.filter((item) => {
6161
const search = searchText.toLowerCase();
62-
return item.lastName.toLowerCase().indexOf(search) >= 0;
62+
return (
63+
item.lastName.toLowerCase().indexOf(search) >= 0 ||
64+
item.firstName.toLowerCase().indexOf(search) >= 0 ||
65+
item.email.toLowerCase().indexOf(search) >= 0
66+
);
6367
});
6468
}
6569

@@ -87,13 +91,13 @@ function UserManagementTableContent({ users }: UserManagementTableProps) {
8791
<table className='users-table'>
8892
<thead>
8993
<tr>
90-
<SortableTableHeader<UserDTO> sortKey='lastName' header='Name' />
91-
<th>User Type</th>
92-
<SortableTableHeader<UserDTO> sortKey='lastName' header='Approver' />
93-
<SortableTableHeader<UserDTO> sortKey='lastName' header='Date Approved' />
94-
<th>API Permissions</th>
95-
<SortableTableHeader<UserDTO> sortKey='lastName' header='Salesforce Agreement Number' />
96-
<th className='action'>Action</th>
94+
<SortableTableHeader<UserDTO> sortKey='email' header='Email' />
95+
<SortableTableHeader<UserDTO> sortKey='firstName' header='First Name' />
96+
<SortableTableHeader<UserDTO> sortKey='lastName' header='Last Name' />
97+
<SortableTableHeader<UserDTO> sortKey='jobFunction' header='Job Function' />
98+
<th>Accepted Terms</th>
99+
<th>Delete Action Here</th>
100+
<th>Lock Action Here</th>
97101
</tr>
98102
</thead>
99103

src/web/contexts/SortableTableProvider.tsx

+7-2
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,13 @@ export function SortableProvider<TData extends {}>({ children }: { children: Rea
5858
(a: TData, b: TData): number => {
5959
if (!sortKey) return 0;
6060

61-
if (a[sortKey] > b[sortKey]) return sortOrder === 'asc' ? 1 : -1;
62-
if (a[sortKey] < b[sortKey]) return sortOrder === 'asc' ? -1 : 1;
61+
const aSort =
62+
typeof a[sortKey] === 'string' ? (a[sortKey] as string).toLowerCase() : a[sortKey];
63+
const bSort =
64+
typeof b[sortKey] === 'string' ? (b[sortKey] as string).toLowerCase() : b[sortKey];
65+
66+
if (aSort > bSort) return sortOrder === 'asc' ? 1 : -1;
67+
if (aSort < bSort) return sortOrder === 'asc' ? -1 : 1;
6368

6469
return 0;
6570
},

src/web/screens/dashboard.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { EmailContactsRoute } from './emailContacts';
1212
import { HomeRoute } from './home';
1313
import { LogoutRoute } from './logout';
1414
import { ManageParticipantsRoute } from './manageParticipants';
15+
import { ManageUsersRoute } from './manageUsers';
1516
import { ParticipantInformationRoute } from './participantInformation';
1617
import { PortalRoute } from './routeUtils';
1718
import { SharingPermissionsRoute } from './sharingPermissions';
@@ -34,7 +35,7 @@ export const StandardRoutes: PortalRoute[] = [
3435
export const Uid2SupportRoutes: PortalRoute[] = [ManageParticipantsRoute];
3536

3637
// TODO: add route for Users List once created
37-
export const SuperUserRoutes: PortalRoute[] = [];
38+
export const SuperUserRoutes: PortalRoute[] = [ManageUsersRoute];
3839

3940
export const DashboardRoutes: PortalRoute[] = [
4041
...StandardRoutes,

src/web/screens/manageUsers.tsx

+3-13
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,9 @@ import { ScreenContentContainer } from '../components/Core/ScreenContentContaine
66
import UserManagementTable from '../components/UserManagement/UserManagementTable';
77
import { GetAllUsersAdmin } from '../services/userAccount';
88
import { AwaitTypesafe } from '../utils/AwaitTypesafe';
9-
import { makeParticipantLoader } from '../utils/loaderHelpers';
109
import { RouteErrorBoundary } from '../utils/RouteErrorBoundary';
1110
import { PortalRoute } from './routeUtils';
1211

13-
// const oldloader = makeParticipantLoader((participantId) => {
14-
// const auditTrail = GetAllUsersAdmin(participantId);
15-
// return defer({ auditTrail });
16-
// });
17-
1812
const loader = () => {
1913
const userList = GetAllUsersAdmin();
2014
return defer({ userList });
@@ -25,11 +19,8 @@ function ManageUsers() {
2519

2620
return (
2721
<>
28-
<h1>Audit Trail</h1>
29-
<p className='heading-details'>
30-
View a detailed log of all past actions performed by, or on behalf of, the current
31-
participant.
32-
</p>
22+
<h1>Users</h1>
23+
<p className='heading-details'>Manage portal users</p>
3324
<ScreenContentContainer>
3425
<Suspense fallback={<Loading message='Loading users...' />}>
3526
<AwaitTypesafe resolve={data.userList}>
@@ -46,7 +37,6 @@ export const ManageUsersRoute: PortalRoute = {
4637
element: <ManageUsers />,
4738
errorElement: <RouteErrorBoundary />,
4839
// ****** should we change the route here?
49-
path: '/participant/:participantId/ManageUsers',
40+
path: '/participant/:participantId/manageUsers',
5041
loader,
51-
isHidden: true,
5242
};

src/web/services/userAccount.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { KeycloakProfile } from 'keycloak-js';
33
import log from 'loglevel';
44
import { z } from 'zod';
55

6-
import { UserCreationPartial } from '../../api/entities/User';
6+
import { UserCreationPartial, UserDTO } from '../../api/entities/User';
77
import { UserWithParticipantRoles } from '../../api/services/usersService';
88
import { backendError } from '../utils/apiError';
99

@@ -107,7 +107,8 @@ export async function SetTermsAccepted() {
107107

108108
export async function GetAllUsersAdmin() {
109109
try {
110-
return await axios.get('/admin/users');
110+
const userResponse = await axios.get<UserDTO[]>('/admin/users');
111+
return userResponse.data;
111112
} catch (e: unknown) {
112113
throw backendError(e, 'Unable to get admin user list.');
113114
}

0 commit comments

Comments
 (0)