Skip to content
Merged
13 changes: 12 additions & 1 deletion apps/backend/src/resolvers/types/Proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,12 +312,23 @@ export class ProposalResolver {
}

@FieldResolver(() => ProposalAttachments, { nullable: true })
attachments(@Root() proposal: Proposal, @Ctx() ctx: ResolverContext) {
async attachments(@Root() proposal: Proposal, @Ctx() ctx: ResolverContext) {
return ctx.queries.questionary.getProposalAttachments(
ctx.user,
proposal.primaryKey
);
}

@FieldResolver(() => [BasicUserDetails], { nullable: true })
async dataAccessUsers(
@Root() proposal: Proposal,
@Ctx() context: ResolverContext
): Promise<BasicUserDetails[]> {
return await context.queries.dataAccessUsers.findByProposalPk(
context.user,
proposal.primaryKey
);
}
}

export async function resolveProposalReference(
Expand Down
3 changes: 3 additions & 0 deletions apps/e2e/cypress/e2e/proposals.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ context('Proposal tests', () => {
createdProposalId
);
cy.get('[data-cy="questionary-details-view"]').contains(newProposalTitle);
cy.get('[data-cy="data-access-users-list"]').contains(
initialDBData.users.user3.lastName
);
});

it('Copy to clipboard should work for Proposal ID', () => {
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/src/components/proposal/ProposalCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export function createProposalStub(
samples: [],
genericTemplates: [],
call: call,
dataAccessUsers: [],
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { TableProps } from '@mui/material';
import React, { FunctionComponent } from 'react';
import React, { FunctionComponent, useContext } from 'react';

import UOLoader from 'components/common/UOLoader';
import ProposalQuestionaryDetails from 'components/proposal/ProposalQuestionaryDetails';
import { TableRowData } from 'components/questionary/QuestionaryDetails';
import { BasicUserDetails } from 'generated/sdk';
import UserList from 'components/user/UserList';
import UserListItem from 'components/user/UserListItem';
import { FeatureContext } from 'context/FeatureContextProvider';
import { FeatureId } from 'generated/sdk';
import { ProposalWithQuestionary } from 'models/questionary/proposal/ProposalWithQuestionary';
import { getFullUserNameWithBasicDetails } from 'utils/user';

export default function ProposalQuestionaryReview(
props: {
data: ProposalWithQuestionary;
} & TableProps<FunctionComponent<unknown>>
) {
const { data, ...restProps } = props;
const featureContext = useContext(FeatureContext);
const isDataAccessUsersEnabled = featureContext.featuresMap.get(
FeatureId.DATA_ACCESS_USERS
)?.isEnabled;

if (!data.questionaryId) {
return <UOLoader style={{ marginLeft: '50%', marginTop: '100px' }} />;
Expand All @@ -34,14 +40,25 @@ export default function ProposalQuestionaryReview(
{ label: 'Abstract', value: data.abstract },
{
label: 'Principal Investigator',
value: getFullUserNameWithBasicDetails(data.proposer),
value: <UserListItem user={data.proposer} />,
},
{
label: 'Co-Proposers',
value: users
.map((user: BasicUserDetails) => getFullUserNameWithBasicDetails(user))
.join(', '),
value: <UserList users={users} />,
},
...(isDataAccessUsersEnabled && data.dataAccessUsers
? [
{
label: 'Data Access Users',
value: (
<UserList
users={data.dataAccessUsers}
data-cy="data-access-users-list"
/>
),
},
]
: []),
...(data.coProposerInvites?.length > 0
? [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export function createFapReviewStub(
experiments: null,
experimentSequence: null,
fileId: null,
dataAccessUsers: [],
},
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export function createTechnicalReviewStub(
experiments: null,
experimentSequence: null,
fileId: null,
dataAccessUsers: [],
},
};
}
Expand Down
72 changes: 72 additions & 0 deletions apps/frontend/src/components/user/UserList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Button, Collapse, List, ListItem, styled } from '@mui/material';
import React, { useState } from 'react';

import { BasicUserDetails } from 'generated/sdk';

import UserListItem from './UserListItem';

interface UserListProps {
users: BasicUserDetails[];
initVisibleItems?: number;
'data-cy'?: string;
}

const StyledList = styled(List)(() => ({
padding: 0,
'& .MuiListItem-root': {
paddingLeft: 0,
paddingRight: 0,
paddingTop: 0,
paddingBottom: 0,
},
}));

const UserList: React.FC<UserListProps> = ({
users,
initVisibleItems = 4,
'data-cy': dataCy = 'user-list',
}) => {
const [showAll, setShowAll] = useState(false);

if (!users || users.length === 0) {
return null;
}

const initialUsers = users.slice(0, initVisibleItems);
const hiddenUsers = users.slice(initVisibleItems);
const hasMore = users.length > initVisibleItems;

return (
<div data-cy={dataCy}>
<StyledList>
{initialUsers.map((user) => (
<ListItem key={user.id} disableGutters>
<UserListItem user={user} />
</ListItem>
))}
</StyledList>
<Collapse in={showAll}>
<StyledList>
{hiddenUsers.map((user) => (
<ListItem key={user.id} disableGutters>
<UserListItem user={user} />
</ListItem>
))}
</StyledList>
</Collapse>
{hasMore && (
<Button
onClick={() => setShowAll(!showAll)}
variant="text"
size="small"
data-cy={showAll ? 'show-less-users-btn' : 'show-more-users-btn'}
sx={{ paddingLeft: 0, textTransform: 'none' }}
>
{showAll ? 'Show less...' : 'Show more...'}
</Button>
)}
</div>
);
};

export default UserList;
22 changes: 22 additions & 0 deletions apps/frontend/src/components/user/UserListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ListItemText } from '@mui/material';
import React from 'react';

import { BasicUserDetails } from 'generated/sdk';
import { getFullUserNameWithEmail } from 'utils/user';

interface UserListItemProps {
user?: BasicUserDetails | null;
}

const UserListItem: React.FC<UserListItemProps> = ({ user }) => {
return (
<ListItemText
primary={getFullUserNameWithEmail(user)}
secondary={user?.institution || ''}
primaryTypographyProps={{ variant: 'body2' }}
secondaryTypographyProps={{ variant: 'caption' }}
/>
);
};

export default UserListItem;
3 changes: 3 additions & 0 deletions apps/frontend/src/graphql/proposal/getProposal.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ query getProposal($primaryKey: Int!) {
users {
...basicUserDetails
}
dataAccessUsers {
...basicUserDetails
}
questionary {
...questionary
isCompleted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type ProposalWithQuestionary = Pick<
| 'questionary'
| 'status'
| 'users'
| 'dataAccessUsers'
| 'coProposerInvites'
| 'title'
| 'proposalId'
Expand Down
Loading