From aa7ccb132e989876011283c373c6d485e914059d Mon Sep 17 00:00:00 2001 From: Ashley Smith Date: Thu, 5 Dec 2024 16:12:39 -0700 Subject: [PATCH] removed all uses of participant status --- src/api/controllers/userController.ts | 8 +- src/api/entities/Participant.ts | 8 -- .../participants/participantsApproval.ts | 17 +-- .../participants/participantsCreation.ts | 3 +- .../participants/participantsKeyPairs.spec.ts | 4 +- .../participants/participantsRouter.ts | 11 +- src/api/services/participantsService.ts | 24 +--- ...DeleteParticipantStatusFromParticipants.ts | 13 ++ src/database/seeds/Participants.ts | 8 +- src/database/seeds/Users.ts | 2 - src/testHelpers/apiTestHelpers.ts | 5 +- src/testHelpers/dataMocks.ts | 3 +- src/web/App.tsx | 4 +- .../ApprovedParticipantsTable.stories.tsx | 5 - .../ApprovedParticipantsTable.tsx | 80 +++++++++++- .../EditParticipantDialog.stories.tsx | 2 - .../ParticipantManagementTable.scss | 50 ++++--- .../ParticipantRequestForm.stories.tsx | 122 ------------------ .../BulkAddPermissions.stories.tsx | 5 - src/web/contexts/ParticipantProvider.tsx | 7 +- src/web/screens/homeRedirector.tsx | 6 +- src/web/screens/manageParticipants.tsx | 19 +-- src/web/services/participant.ts | 11 -- 23 files changed, 141 insertions(+), 276 deletions(-) create mode 100644 src/database/migrations/20241205040900_DeleteParticipantStatusFromParticipants.ts delete mode 100644 src/web/components/ParticipantManagement/ParticipantRequestForm.stories.tsx diff --git a/src/api/controllers/userController.ts b/src/api/controllers/userController.ts index 06446276e..3fc3c8f20 100644 --- a/src/api/controllers/userController.ts +++ b/src/api/controllers/userController.ts @@ -12,7 +12,6 @@ import { } from 'inversify-express-utils'; import { TYPES } from '../constant/types'; -import { ParticipantStatus } from '../entities/Participant'; import { UserRoleId } from '../entities/UserRole'; import { getTraceId } from '../helpers/loggingHelpers'; import { getKcAdminClient } from '../keycloakAdminClient'; @@ -56,12 +55,9 @@ export class UserController { @httpPut('/current/acceptTerms') public async acceptTerms(@request() req: UserRequest, @response() res: Response): Promise { - const doesUserHaveAnApprovedParticipant = - req.user?.participants?.some( - (participant) => participant.status === ParticipantStatus.Approved - ) ?? false; + const doesUserHaveAParticipant = (req.user?.participants?.length ?? 0) >= 1 ?? false; - if (!doesUserHaveAnApprovedParticipant) { + if (!doesUserHaveAParticipant) { res.status(403).json({ message: 'Unauthorized. You do not have the necessary permissions.', errorHash: req.headers.traceId, diff --git a/src/api/entities/Participant.ts b/src/api/entities/Participant.ts index d2b34ed29..a79d3ee10 100644 --- a/src/api/entities/Participant.ts +++ b/src/api/entities/Participant.ts @@ -8,12 +8,6 @@ import { ParticipantType, ParticipantTypeDTO, ParticipantTypeSchema } from './Pa import { type User, UserDTO, UserSchema } from './User'; import { UserToParticipantRole } from './UserToParticipantRole'; -export enum ParticipantStatus { - AwaitingSigning = 'awaitingSigning', - AwaitingApproval = 'awaitingApproval', - Approved = 'approved', -} - export class Participant extends BaseModel { static get tableName() { return 'participants'; @@ -82,7 +76,6 @@ export class Participant extends BaseModel { }; declare id: number; declare name: string; - declare status: ParticipantStatus; declare allowSharing: boolean; declare completedRecommendations: boolean; declare siteId?: number; @@ -107,7 +100,6 @@ export type ParticipantDTO = Omit, 'types' | 'users' export const ParticipantSchema = z.object({ id: z.number(), name: z.string(), - status: z.nativeEnum(ParticipantStatus), types: z.array(ParticipantTypeSchema).optional(), apiRoles: z.array(ApiRoleSchema).optional(), users: z.array(UserSchema).optional(), diff --git a/src/api/routers/participants/participantsApproval.ts b/src/api/routers/participants/participantsApproval.ts index d0818aef7..4c1a9b0c0 100644 --- a/src/api/routers/participants/participantsApproval.ts +++ b/src/api/routers/participants/participantsApproval.ts @@ -2,7 +2,7 @@ import { Response } from 'express'; import { getRoleNamesByIds } from '../../../web/utils/apiRoles'; import { AuditTrailEvents } from '../../entities/AuditTrail'; -import { ParticipantApprovalPartial, ParticipantStatus } from '../../entities/Participant'; +import { ParticipantApprovalPartial } from '../../entities/Participant'; import { getTraceId } from '../../helpers/loggingHelpers'; import { getKcAdminClient } from '../../keycloakAdminClient'; import { setSiteClientTypes } from '../../services/adminServiceClient'; @@ -17,27 +17,13 @@ import { import { assignApiParticipantMemberRole } from '../../services/kcUsersService'; import { getParticipantsApproved, - getParticipantsAwaitingApproval, - mapParticipantToApprovalRequest, ParticipantRequest, - ParticipantRequestDTO, sendParticipantApprovedEmail, updateParticipantAndTypesAndApiRoles, UserParticipantRequest, } from '../../services/participantsService'; import { getAllUsersFromParticipant } from '../../services/usersService'; -export const handleGetParticipantsAwaitingApproval = async ( - req: ParticipantRequest, - res: Response -) => { - const participantsAwaitingApproval = await getParticipantsAwaitingApproval(); - const result: ParticipantRequestDTO[] = participantsAwaitingApproval.map( - mapParticipantToApprovalRequest - ); - return res.status(200).json(result); -}; - export const handleGetApprovedParticipants = async (_req: ParticipantRequest, res: Response) => { const participants = await getParticipantsApproved(); const result = participants.sort((a, b) => a.name.localeCompare(b.name)); @@ -49,7 +35,6 @@ export const handleApproveParticipant = async (req: UserParticipantRequest, res: const traceId = getTraceId(req); const data = { ...ParticipantApprovalPartial.parse(req.body), - status: ParticipantStatus.Approved, approverId: user?.id, dateApproved: new Date(), }; diff --git a/src/api/routers/participants/participantsCreation.ts b/src/api/routers/participants/participantsCreation.ts index 6eae1fbb4..f94ed358e 100644 --- a/src/api/routers/participants/participantsCreation.ts +++ b/src/api/routers/participants/participantsCreation.ts @@ -4,7 +4,7 @@ import { z } from 'zod'; import { getRoleNamesByIds } from '../../../web/utils/apiRoles'; import { ApiRole } from '../../entities/ApiRole'; import { AuditAction, AuditTrailEvents } from '../../entities/AuditTrail'; -import { Participant, ParticipantStatus } from '../../entities/Participant'; +import { Participant } from '../../entities/Participant'; import { User, UserCreationPartial } from '../../entities/User'; import { UserRoleId } from '../../entities/UserRole'; import { UserToParticipantRole } from '../../entities/UserToParticipantRole'; @@ -136,7 +136,6 @@ async function createParticipant( await performAsyncOperationWithAuditTrail(auditTrailInsertObject, traceId, async () => { const participantData = { ...parsedParticipantRequest, - status: ParticipantStatus.Approved, approverId: requestingUser?.id, dateApproved: new Date(), }; diff --git a/src/api/routers/participants/participantsKeyPairs.spec.ts b/src/api/routers/participants/participantsKeyPairs.spec.ts index d1dd879cb..ec138a1d0 100644 --- a/src/api/routers/participants/participantsKeyPairs.spec.ts +++ b/src/api/routers/participants/participantsKeyPairs.spec.ts @@ -4,7 +4,7 @@ import { http, HttpResponse } from 'msw'; import { setupServer } from 'msw/node'; import { createResponseObject } from '../../../testHelpers/apiTestHelpers'; -import { Participant, ParticipantStatus } from '../../entities/Participant'; +import { Participant } from '../../entities/Participant'; import { SSP_ADMIN_SERVICE_BASE_URL } from '../../envars'; import { KeyPairDTO } from '../../services/adminServiceHelpers'; import { ParticipantRequest } from '../../services/participantsService'; @@ -96,7 +96,6 @@ describe('Get participant key pairs', () => { id: 5, allowSharing: true, completedRecommendations: true, - status: ParticipantStatus.Approved, apiRoles: [], }); @@ -122,7 +121,6 @@ describe('Get participant key pairs', () => { id: 5, allowSharing: true, completedRecommendations: true, - status: ParticipantStatus.Approved, apiRoles: [], siteId, }); diff --git a/src/api/routers/participants/participantsRouter.ts b/src/api/routers/participants/participantsRouter.ts index d9f519644..c0bfac12b 100644 --- a/src/api/routers/participants/participantsRouter.ts +++ b/src/api/routers/participants/participantsRouter.ts @@ -19,11 +19,7 @@ import { } from './participantsApiKeys'; import { handleGetParticipantApiRoles } from './participantsApiRoles'; import { handleGetParticipantAppNames, handleSetParticipantAppNames } from './participantsAppIds'; -import { - handleApproveParticipant, - handleGetApprovedParticipants, - handleGetParticipantsAwaitingApproval, -} from './participantsApproval'; +import { handleApproveParticipant, handleGetApprovedParticipants } from './participantsApproval'; import { handleGetAuditTrail } from './participantsAuditTrail'; import { handleCreateParticipant } from './participantsCreation'; import { @@ -50,11 +46,6 @@ export function createParticipantsRouter() { participantsRouter.get('/signed', handleGetSignedParticipants); - participantsRouter.get( - '/awaitingApproval', - isUid2SupportCheck, - handleGetParticipantsAwaitingApproval - ); participantsRouter.get('/approved', isUid2SupportCheck, handleGetApprovedParticipants); participantsRouter.put('/', handleCreateParticipant); diff --git a/src/api/services/participantsService.ts b/src/api/services/participantsService.ts index fee98f021..3380cb57b 100644 --- a/src/api/services/participantsService.ts +++ b/src/api/services/participantsService.ts @@ -5,12 +5,7 @@ import { z } from 'zod'; import { getRoleNamesByIds } from '../../web/utils/apiRoles'; import { ApiRole } from '../entities/ApiRole'; import { AuditAction, AuditTrailEvents } from '../entities/AuditTrail'; -import { - Participant, - ParticipantApprovalPartial, - ParticipantDTO, - ParticipantStatus, -} from '../entities/Participant'; +import { Participant, ParticipantApprovalPartial, ParticipantDTO } from '../entities/Participant'; import { ParticipantType } from '../entities/ParticipantType'; import { User, UserDTO } from '../entities/User'; import { SSP_WEB_BASE_URL } from '../envars'; @@ -27,7 +22,6 @@ import { } from './auditTrailService'; import { createEmailService } from './emailService'; import { EmailArgs } from './emailTypes'; -import { getAllUid2SupportUsers } from './uid2SupportService'; export interface ParticipantRequest extends Request { participant?: Participant; @@ -39,7 +33,7 @@ export interface UserParticipantRequest extends ParticipantRequest { export type ParticipantRequestDTO = Pick< ParticipantDTO, - 'id' | 'name' | 'siteId' | 'types' | 'status' | 'apiRoles' + 'id' | 'name' | 'siteId' | 'types' | 'apiRoles' > & { requestingUser: Pick & Partial> & { fullName: string }; @@ -62,7 +56,6 @@ export const mapParticipantToApprovalRequest = ( siteId: participant.siteId, types: participant.types, apiRoles: participant.apiRoles, - status: participant.status, requestingUser: { email: firstUser ? firstUser.email : '', jobFunction: firstUser?.jobFunction, @@ -73,13 +66,6 @@ export const mapParticipantToApprovalRequest = ( }; }; -export const getParticipantsAwaitingApproval = async (): Promise => { - const participantsAwaitingApproval = await Participant.query() - .withGraphFetched('[types, users]') - .where('status', ParticipantStatus.AwaitingApproval); - return participantsAwaitingApproval; -}; - type SiteIdType = NonNullable; export const getAttachedSiteIDs = async (): Promise => { const sites = await Participant.query() @@ -90,9 +76,7 @@ export const getAttachedSiteIDs = async (): Promise => { }; export const getParticipantsApproved = async (): Promise => { - return Participant.query() - .where('status', ParticipantStatus.Approved) - .withGraphFetched('[apiRoles, approver, types, users]'); + return Participant.query().withGraphFetched('[apiRoles, approver, types, users]'); }; export const getParticipantsBySiteIds = async (siteIds: number[]) => { @@ -167,7 +151,6 @@ export const updateParticipantApiRolesWithTransaction = async ( export const updateParticipantAndTypesAndApiRoles = async ( participant: Participant, participantApprovalPartial: z.infer & { - status: ParticipantStatus; approverId: number | undefined; dateApproved: Date; } @@ -176,7 +159,6 @@ export const updateParticipantAndTypesAndApiRoles = async ( await participant.$query(trx).patch({ name: participantApprovalPartial.name, siteId: participantApprovalPartial.siteId, - status: participantApprovalPartial.status, approverId: participantApprovalPartial.approverId, dateApproved: participantApprovalPartial.dateApproved, }); diff --git a/src/database/migrations/20241205040900_DeleteParticipantStatusFromParticipants.ts b/src/database/migrations/20241205040900_DeleteParticipantStatusFromParticipants.ts new file mode 100644 index 000000000..9d65708bc --- /dev/null +++ b/src/database/migrations/20241205040900_DeleteParticipantStatusFromParticipants.ts @@ -0,0 +1,13 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex.schema.alterTable('participants', (table) => { + table.dropColumn('status'); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.alterTable('participants', (table) => { + table.enu('status', ['initialize', 'awaitingApproval', 'approved']).notNullable(); + }); +} diff --git a/src/database/seeds/Participants.ts b/src/database/seeds/Participants.ts index 1a46a2633..9a3a0964a 100644 --- a/src/database/seeds/Participants.ts +++ b/src/database/seeds/Participants.ts @@ -2,7 +2,7 @@ import { Knex } from 'knex'; import { ModelObject } from 'objection'; import { Optional } from 'utility-types'; -import { Participant, ParticipantStatus } from '../../api/entities/Participant'; +import { Participant } from '../../api/entities/Participant'; type ParticipantsType = ModelObject; const sampleData: Optional< @@ -12,7 +12,6 @@ const sampleData: Optional< { name: 'Publisher example', allowSharing: true, - status: ParticipantStatus.Approved, type: 'Publisher', siteId: 124, apiRoleNames: ['GENERATOR', 'SHARER'], @@ -21,7 +20,6 @@ const sampleData: Optional< }, { name: 'DSP example', - status: ParticipantStatus.Approved, type: 'DSP', allowSharing: true, siteId: 123, @@ -31,7 +29,6 @@ const sampleData: Optional< }, { name: 'DP example', - status: ParticipantStatus.Approved, allowSharing: true, type: 'Data Provider', siteId: 125, @@ -42,7 +39,6 @@ const sampleData: Optional< { name: 'Advertiser example', allowSharing: true, - status: ParticipantStatus.Approved, type: 'Advertiser', siteId: 126, apiRoleNames: ['MAPPER', 'SHARER'], @@ -52,7 +48,6 @@ const sampleData: Optional< { name: 'AwaitingSigning example', allowSharing: true, - status: ParticipantStatus.AwaitingSigning, type: 'Publisher', apiRoleNames: ['GENERATOR', 'SHARER'], completedRecommendations: false, @@ -72,7 +67,6 @@ export async function CreateParticipant( const participant = await knex('participants') .insert({ name: details.name, - status: details.status, siteId: details.siteId, }) .returning('id'); diff --git a/src/database/seeds/Users.ts b/src/database/seeds/Users.ts index 33d01030b..4a349c71d 100644 --- a/src/database/seeds/Users.ts +++ b/src/database/seeds/Users.ts @@ -2,7 +2,6 @@ import { Knex } from 'knex'; import { ModelObject } from 'objection'; import { Optional } from 'utility-types'; -import { ParticipantStatus } from '../../api/entities/Participant'; import { User, UserJobFunction } from '../../api/entities/User'; import { UserRoleId } from '../../api/entities/UserRole'; import { CreateParticipant } from './Participants'; @@ -11,7 +10,6 @@ type UserType = ModelObject; const sampleParticipant = { name: 'Awaiting Approval', - status: ParticipantStatus.AwaitingApproval, allowSharing: true, completedRecommendations: false, crmAgreementNumber: '12345678', diff --git a/src/testHelpers/apiTestHelpers.ts b/src/testHelpers/apiTestHelpers.ts index 2e6e10a62..7d0df6d22 100644 --- a/src/testHelpers/apiTestHelpers.ts +++ b/src/testHelpers/apiTestHelpers.ts @@ -3,7 +3,7 @@ import { Response } from 'express'; import { Knex } from 'knex'; import { ModelObjectOpt } from '../api/entities/ModelObjectOpt'; -import { Participant, ParticipantStatus } from '../api/entities/Participant'; +import { Participant } from '../api/entities/Participant'; import { User, UserJobFunction } from '../api/entities/User'; import { UserRoleId } from '../api/entities/UserRole'; import { UserToParticipantRole } from '../api/entities/UserToParticipantRole'; @@ -68,7 +68,6 @@ export async function createParticipant( { name = faker.company.name(), allowSharing = true, - status = ParticipantStatus.Approved, type = 'Publisher', apiRoleNames = [], completedRecommendations = false, @@ -76,7 +75,6 @@ export async function createParticipant( }: { name?: string; allowSharing?: boolean; - status?: ParticipantStatus; type?: string; completedRecommendations?: boolean; apiRoleNames?: string[]; @@ -86,7 +84,6 @@ export async function createParticipant( const data = { name, allowSharing, - status, completedRecommendations, crmAgreementNumber, }; diff --git a/src/testHelpers/dataMocks.ts b/src/testHelpers/dataMocks.ts index aab967161..ce07e7b99 100644 --- a/src/testHelpers/dataMocks.ts +++ b/src/testHelpers/dataMocks.ts @@ -1,6 +1,6 @@ import { faker } from '@faker-js/faker'; -import { Participant, ParticipantDTO, ParticipantStatus } from '../api/entities/Participant'; +import { Participant, ParticipantDTO } from '../api/entities/Participant'; import { UserJobFunction } from '../api/entities/User'; import { UserWithParticipantRoles } from '../api/services/usersService'; @@ -29,7 +29,6 @@ export const createMockUser = (participants: Participant[]): UserWithParticipant export const createMockParticipant = (): ParticipantDTO => ({ id: faker.number.int(), name: faker.company.name(), - status: ParticipantStatus.Approved, allowSharing: true, completedRecommendations: faker.datatype.boolean(), crmAgreementNumber: '12345678', diff --git a/src/web/App.tsx b/src/web/App.tsx index 44b9b58ac..b5beeb59a 100644 --- a/src/web/App.tsx +++ b/src/web/App.tsx @@ -2,7 +2,6 @@ import { useKeycloak } from '@react-keycloak/web'; import { StrictMode, useCallback, useContext } from 'react'; import { Outlet, useLocation } from 'react-router-dom'; -import { ParticipantStatus } from '../api/entities/Participant'; import { EnvironmentBanner } from './components/Core/Banner/EnvironmentBanner'; import { ErrorView } from './components/Core/ErrorView/ErrorView'; import { Loading } from './components/Core/Loading/Loading'; @@ -34,8 +33,7 @@ function AppContent() { return ; } - const showUpdatesTour = - participant?.status === ParticipantStatus.Approved && !!LoggedInUser?.user?.acceptedTerms; + const showUpdatesTour = !!LoggedInUser?.user?.acceptedTerms; return ( <> diff --git a/src/web/components/ParticipantManagement/ApprovedParticipantsTable.stories.tsx b/src/web/components/ParticipantManagement/ApprovedParticipantsTable.stories.tsx index e88d3af72..6a8f0477d 100644 --- a/src/web/components/ParticipantManagement/ApprovedParticipantsTable.stories.tsx +++ b/src/web/components/ParticipantManagement/ApprovedParticipantsTable.stories.tsx @@ -1,6 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { ParticipantStatus } from '../../../api/entities/Participant'; import { UserJobFunction } from '../../../api/entities/User'; import ApprovedParticipantsTable from './ApprovedParticipantsTable'; @@ -20,7 +19,6 @@ export const AllParticipants: Story = { name: 'Participant 2', types: [{ id: 1, typeName: 'Type 1' }], apiRoles: [], - status: ParticipantStatus.Approved, allowSharing: true, completedRecommendations: false, approver: { @@ -52,7 +50,6 @@ export const AllParticipants: Story = { { id: 2, typeName: 'Type 2' }, ], apiRoles: [{ id: 1, roleName: 'Role1', externalName: 'Role 1', order: 1 }], - status: ParticipantStatus.Approved, allowSharing: true, completedRecommendations: false, approver: { @@ -88,7 +85,6 @@ export const AllParticipants: Story = { { id: 1, roleName: 'Role1', externalName: 'Role 1', order: 1 }, { id: 2, roleName: 'Role2', externalName: 'Role 2', order: 2 }, ], - status: ParticipantStatus.Approved, allowSharing: true, completedRecommendations: false, approver: { @@ -126,7 +122,6 @@ export const AllParticipants: Story = { { id: 2, roleName: 'Role2', externalName: 'Role 2', order: 2 }, { id: 3, roleName: 'Role3', externalName: 'Role 3', order: 3 }, ], - status: ParticipantStatus.Approved, allowSharing: true, completedRecommendations: false, crmAgreementNumber: '45678901', diff --git a/src/web/components/ParticipantManagement/ApprovedParticipantsTable.tsx b/src/web/components/ParticipantManagement/ApprovedParticipantsTable.tsx index 11afaf5f0..f71129d4c 100644 --- a/src/web/components/ParticipantManagement/ApprovedParticipantsTable.tsx +++ b/src/web/components/ParticipantManagement/ApprovedParticipantsTable.tsx @@ -1,8 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { useState } from 'react'; + import { ApiRoleDTO } from '../../../api/entities/ApiRole'; import { ParticipantDTO } from '../../../api/entities/Participant'; import { ParticipantTypeDTO } from '../../../api/entities/ParticipantType'; import { SortableProvider, useSortable } from '../../contexts/SortableTableProvider'; import { UpdateParticipantForm } from '../../services/participant'; +import { PagingTool } from '../Core/Paging/PagingTool'; +import { RowsPerPageValues } from '../Core/Paging/PagingToolHelper'; import { SortableTableHeader } from '../Core/Tables/SortableTableHeader'; import { TableNoDataPlaceholder } from '../Core/Tables/TableNoDataPlaceholder'; import { ApprovedParticipantItem } from './ApprovedParticipantItem'; @@ -33,12 +38,66 @@ function ApprovedParticipantsTableContent({ participantTypes, onUpdateParticipant, }: ApprovedParticipantsTableProps) { + const initialRowsPerPage = 10; + const initialPageNumber = 1; + + const [rowsPerPage, setRowsPerPage] = useState(initialRowsPerPage); + const [pageNumber, setPageNumber] = useState(initialPageNumber); + const [searchText, setSearchText] = useState(''); + + const getPagedParticipants = (values: ParticipantDTO[]) => { + const pagedRows = values.filter((_, index) => { + return ( + index >= (pageNumber - 1) * rowsPerPage && + index < (pageNumber - 1) * rowsPerPage + rowsPerPage + ); + }); + return pagedRows; + }; + + const onChangeDisplayedParticipants = ( + currentPageNumber: number, + currentRowsPerPage: RowsPerPageValues + ) => { + setPageNumber(currentPageNumber); + setRowsPerPage(currentRowsPerPage); + }; + + const handleSearch = (event: React.ChangeEvent) => { + setSearchText(event.target.value); + setPageNumber(initialPageNumber); + setRowsPerPage(initialRowsPerPage); + }; + + let searchedParticipants = participants; + if (searchText.length > 1) { + searchedParticipants = participants.filter((item) => { + const search = searchText.toLowerCase(); + return item.name.toLowerCase().indexOf(search) >= 0; + }); + } + const { sortData } = useSortable(); - const sortedParticipants = sortData(participants); + const sortedParticipants = sortData(searchedParticipants); + + const pagedRows = getPagedParticipants(sortedParticipants); return (
-

All Participants

+
+
+
+ + +
+
+
@@ -56,7 +115,7 @@ function ApprovedParticipantsTableContent({ - {sortedParticipants.map((participant) => ( + {pagedRows.map((participant) => (
+ {participants.length > 0 && searchText && searchedParticipants.length === 0 && ( + } + title='No Participants' + > + There are no participants that match this search. + + )} {!participants.length && } + {!!searchedParticipants.length && ( + + )}
); } diff --git a/src/web/components/ParticipantManagement/EditParticipantDialog.stories.tsx b/src/web/components/ParticipantManagement/EditParticipantDialog.stories.tsx index 2f1dca682..680d41e64 100644 --- a/src/web/components/ParticipantManagement/EditParticipantDialog.stories.tsx +++ b/src/web/components/ParticipantManagement/EditParticipantDialog.stories.tsx @@ -1,7 +1,6 @@ import type { Meta } from '@storybook/react'; import { useState } from 'react'; -import { ParticipantStatus } from '../../../api/entities/Participant'; import { UserJobFunction } from '../../../api/entities/User'; import EditParticipantDialog from './EditParticipantDialog'; @@ -35,7 +34,6 @@ const participant = { { id: 4, typeName: 'Type 4' }, ], apiRoles: apiRoles.slice(0, 3), - status: ParticipantStatus.Approved, allowSharing: true, completedRecommendations: false, crmAgreementNumber: '12345678', diff --git a/src/web/components/ParticipantManagement/ParticipantManagementTable.scss b/src/web/components/ParticipantManagement/ParticipantManagementTable.scss index 310840b48..46614478d 100644 --- a/src/web/components/ParticipantManagement/ParticipantManagementTable.scss +++ b/src/web/components/ParticipantManagement/ParticipantManagementTable.scss @@ -1,12 +1,4 @@ -.participant-requests-container { - h2 { - margin-top: 0; - } -} - -.participant-requests-container, .approved-participant-container { - .participant-requests-table, .approved-participants-table { width: 100%; @@ -16,20 +8,42 @@ } } - .no-participant-requests-container { - margin-top: 15px; - border: 1px solid var(--theme-border); - border-radius: 5px; - padding: 33px 24px; + .approved-participants-table-header { display: flex; - align-items: center; + justify-content: end; + align-items: baseline; + padding-bottom: 10px; - .no-participant-requests-text { - margin-left: 33px; + &-right { + display: flex; + justify-content: right; + } - h2 { - margin-top: 0; + .approved-participants-search-bar-container { + display: flex; + align-items: center; + border-bottom: 2px solid var(--theme-action); + padding: 6px 2px; + ::placeholder { + color: var(--theme-search-text); + opacity: 1; } } + + .approved-participants-search-bar-icon { + color: var(--theme-search-text); + height: 16px; + margin-left: 8px; + } + + .approved-participants-search-bar { + width: 100%; + border: none; + outline: none; + color: var(--theme-search-text); + font-weight: 400; + font-size: 0.75rem; + background: none; + } } } diff --git a/src/web/components/ParticipantManagement/ParticipantRequestForm.stories.tsx b/src/web/components/ParticipantManagement/ParticipantRequestForm.stories.tsx deleted file mode 100644 index c02636eff..000000000 --- a/src/web/components/ParticipantManagement/ParticipantRequestForm.stories.tsx +++ /dev/null @@ -1,122 +0,0 @@ -/* eslint-disable camelcase */ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; - -import { ParticipantStatus } from '../../../api/entities/Participant'; -import { UserJobFunction } from '../../../api/entities/User'; -import { SiteDTO } from '../../../api/services/adminServiceHelpers'; -import { TestSiteListProvider } from '../../services/site'; -import ParticipantApprovalForm from './ParticipantApprovalForm'; - -const meta: Meta = { - title: 'Manage Participants/Participant Approval Form', - component: ParticipantApprovalForm, -}; - -export default meta; -type Story = StoryObj; - -const response: SiteDTO[] = [ - { - id: 2, - name: 'Test Site', - enabled: true, - apiRoles: [ - { id: 1, roleName: 'Role1', externalName: 'Role 1', order: 1 }, - { id: 2, roleName: 'Role2', externalName: 'Role 2', order: 2 }, - ], - clientTypes: ['PUBLISHER'], - client_count: 1, - visible: true, - domain_names: [], - app_names: [], - }, - { - id: 4, - name: 'Test Four', - enabled: true, - apiRoles: [{ id: 1, roleName: 'Role1', externalName: 'Role 1', order: 1 }], - clientTypes: ['PUBLISHER'], - client_count: 1, - visible: false, - domain_names: [], - app_names: [], - }, -]; - -const Template: StoryFn = (args) => { - return ( - - - - ); -}; - -export const ParticipantApprovalMatchingSite: Story = { - render: Template, - - args: { - participant: { - id: 1, - name: 'Test Participant', - types: [ - { id: 1, typeName: 'Type 1' }, - { id: 2, typeName: 'Type 2' }, - ], - status: ParticipantStatus.AwaitingApproval, - requestingUser: { - email: 'test@example.com', - fullName: 'Test User', - jobFunction: UserJobFunction.MediaBuyer, - }, - }, - participantTypes: [ - { id: 1, typeName: 'Type 1' }, - { id: 2, typeName: 'Type 2' }, - { id: 3, typeName: 'Type 3' }, - ], - apiRoles: [ - { id: 1, roleName: 'MAPPER', externalName: 'Mapper', order: 1 }, - { id: 2, roleName: 'GENERATOR', externalName: 'Generator', order: 2 }, - { id: 3, roleName: 'ID_READER', externalName: 'Bidder', order: 4 }, - { id: 4, roleName: 'SHARER', externalName: 'Sharer', order: 3 }, - ], - onApprove: async (form) => { - console.log(JSON.stringify(form)); - }, - }, -}; - -export const ParticipantApprovalSiteSearch: Story = { - render: Template, - - args: { - participant: { - id: 1, - name: 'Participant 1', - types: [ - { id: 1, typeName: 'Type 1' }, - { id: 2, typeName: 'Type 2' }, - ], - status: ParticipantStatus.AwaitingApproval, - requestingUser: { - email: 'test@example.com', - fullName: 'Test User', - jobFunction: UserJobFunction.MediaBuyer, - }, - }, - participantTypes: [ - { id: 1, typeName: 'Type 1' }, - { id: 2, typeName: 'Type 2' }, - { id: 3, typeName: 'Type 3' }, - ], - apiRoles: [ - { id: 1, roleName: 'MAPPER', externalName: 'Mapper', order: 1 }, - { id: 2, roleName: 'GENERATOR', externalName: 'Generator', order: 2 }, - { id: 3, roleName: 'ID_READER', externalName: 'Bidder', order: 4 }, - { id: 4, roleName: 'SHARER', externalName: 'Sharer', order: 3 }, - ], - onApprove: async (form) => { - console.log(JSON.stringify(form)); - }, - }, -}; diff --git a/src/web/components/SharingPermission/BulkAddPermissions.stories.tsx b/src/web/components/SharingPermission/BulkAddPermissions.stories.tsx index 1a5a1ee86..d6a394346 100644 --- a/src/web/components/SharingPermission/BulkAddPermissions.stories.tsx +++ b/src/web/components/SharingPermission/BulkAddPermissions.stories.tsx @@ -1,6 +1,5 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react'; -import { ParticipantStatus } from '../../../api/entities/Participant'; import { SharingSiteDTO } from '../../../api/helpers/siteConvertingHelpers'; import { TestAvailableSiteListProvider } from '../../services/site'; import { BulkAddPermissions } from './BulkAddPermissions'; @@ -64,7 +63,6 @@ export const Publisher: Story = { name: 'Participant 1', types: [{ id: 2, typeName: 'Publisher' }], allowSharing: true, - status: ParticipantStatus.Approved, completedRecommendations: false, crmAgreementNumber: '12345678', }, @@ -85,7 +83,6 @@ export const AdvertiserAndDSP: Story = { { id: 1, typeName: 'DSP' }, ], allowSharing: true, - status: ParticipantStatus.Approved, completedRecommendations: false, crmAgreementNumber: '23456789', }, @@ -108,7 +105,6 @@ export const AllTypes: Story = { { id: 4, typeName: 'Data Provider' }, ], allowSharing: true, - status: ParticipantStatus.Approved, completedRecommendations: false, crmAgreementNumber: '34567890', }, @@ -130,7 +126,6 @@ export const HasSharedWithPublisher: Story = { { id: 1, typeName: 'DSP' }, ], allowSharing: true, - status: ParticipantStatus.Approved, completedRecommendations: true, crmAgreementNumber: '45678901', }, diff --git a/src/web/contexts/ParticipantProvider.tsx b/src/web/contexts/ParticipantProvider.tsx index 8c3bde21c..014eb145e 100644 --- a/src/web/contexts/ParticipantProvider.tsx +++ b/src/web/contexts/ParticipantProvider.tsx @@ -1,7 +1,7 @@ import { createContext, ReactNode, useContext, useEffect, useMemo, useState } from 'react'; -import { useLocation, useNavigate, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; -import { ParticipantDTO, ParticipantStatus } from '../../api/entities/Participant'; +import { ParticipantDTO } from '../../api/entities/Participant'; import { Loading } from '../components/Core/Loading/Loading'; import { GetSelectedParticipant, GetUsersDefaultParticipant } from '../services/participant'; import { ApiError } from '../utils/apiError'; @@ -22,8 +22,7 @@ function ParticipantProvider({ children }: Readonly<{ children: ReactNode }>) { const [participant, setParticipant] = useState(null); const [isLoading, setIsLoading] = useState(true); const { LoggedInUser } = useContext(CurrentUserContext); - const location = useLocation(); - const navigate = useNavigate(); + const throwError = useAsyncThrowError(); const user = LoggedInUser?.user ?? null; const { participantId } = useParams(); diff --git a/src/web/screens/homeRedirector.tsx b/src/web/screens/homeRedirector.tsx index 1f175eab3..47abbd92b 100644 --- a/src/web/screens/homeRedirector.tsx +++ b/src/web/screens/homeRedirector.tsx @@ -1,7 +1,6 @@ import { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; -import { ParticipantStatus } from '../../api/entities/Participant'; import { GetSelectedParticipant, GetUsersDefaultParticipant } from '../services/participant'; import { parseParticipantId } from '../utils/urlHelpers'; @@ -17,9 +16,8 @@ export function HomeRedirector() { const currentParticipant = lastSelectedParticipantId ? await GetSelectedParticipant(lastSelectedParticipantId) : await GetUsersDefaultParticipant(); - if (currentParticipant.status === ParticipantStatus.Approved) { - navigate(`/participant/${currentParticipant.id}/home`); - } + + navigate(`/participant/${currentParticipant.id}/home`); }; if (!participantId) { loadParticipant(); diff --git a/src/web/screens/manageParticipants.tsx b/src/web/screens/manageParticipants.tsx index d0ef44662..0c77ea147 100644 --- a/src/web/screens/manageParticipants.tsx +++ b/src/web/screens/manageParticipants.tsx @@ -16,7 +16,6 @@ import { AddParticipantForm, ApproveParticipantRequest, GetApprovedParticipants, - GetParticipantsAwaitingApproval, GetUsersDefaultParticipant, ParticipantApprovalFormDetails, UpdateParticipant, @@ -31,7 +30,6 @@ import './manageParticipants.scss'; const loader = makeLoader(() => { return defer({ - participantsAwaitingApproval: GetParticipantsAwaitingApproval(), participantsApproved: GetApprovedParticipants(), participantTypes: GetAllParticipantTypes(), apiRoles: GetAllEnabledApiRoles(), @@ -55,19 +53,6 @@ function ManageParticipants() { const reloader = useRevalidator(); - const handleApproveParticipantRequest = async ( - participantId: number, - formData: ParticipantApprovalFormDetails - ) => { - const approvalResponse = await ApproveParticipantRequest(participantId, formData); - if (approvalResponse?.users?.length === 0) { - WarningToast( - 'Participant approved. Since no users are attached to participant, email confirmation sent to approver.' - ); - } - reloader.revalidate(); - }; - const onUpdateParticipant = async ( form: UpdateParticipantForm, updatedParticipant: ParticipantDTO @@ -97,9 +82,7 @@ function ManageParticipants() {

Manage Participants

-

- View and manage UID2 Portal participant requests and information. -

+

View and manage UID2 Portal participants.