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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import ProposalReviewContent, {
import ProposalReviewModal from 'components/review/ProposalReviewModal';
import { Review, SettingsId, Fap } from 'generated/sdk';
import { useFormattedDateTime } from 'hooks/admin/useFormattedDateTime';
import { useExpandCollapseAll } from 'hooks/fap/useExpandCollapseAll';
import {
FapProposalType,
FapProposalAssignmentType,
Expand Down Expand Up @@ -159,6 +160,10 @@ const FapLegacyProposalsTable = ({
settingsFormatToUse: SettingsId.DATE_FORMAT,
});
const { t } = useTranslation();
const { tableRef, expandCollapseAllButton } = useExpandCollapseAll(
'[data-cy="fap-assignments-table"]',
[loadingFapProposals]
);

const translatedColumns = FapProposalColumns.map((column) =>
column.title === 'Instrument'
Expand Down Expand Up @@ -360,6 +365,7 @@ const FapLegacyProposalsTable = ({
</ProposalReviewModal>
<div data-cy="fap-assignments-table">
<MaterialTable
tableRef={tableRef}
icons={tableIcons}
columns={translatedColumns}
title={
Expand Down Expand Up @@ -424,6 +430,7 @@ const FapLegacyProposalsTable = ({
}}
/>
</div>
{expandCollapseAllButton}
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import ProposalReviewModal from 'components/review/ProposalReviewModal';
import { UserRole, Review, SettingsId, Fap } from 'generated/sdk';
import { useFormattedDateTime } from 'hooks/admin/useFormattedDateTime';
import { useCheckAccess } from 'hooks/common/useCheckAccess';
import { useExpandCollapseAll } from 'hooks/fap/useExpandCollapseAll';
import {
FapProposalType,
FapProposalAssignmentType,
Expand Down Expand Up @@ -182,6 +183,10 @@ const FapProposalsAndAssignmentsTable = ({
UserRole.USER_OFFICER,
]);
const { t } = useTranslation();
const { tableRef, expandCollapseAllButton } = useExpandCollapseAll(
'[data-cy="fap-assignments-table"]',
[loadingFapProposals]
);

const translatedColumns = FapProposalColumns.map((column) =>
column.title === 'Instrument'
Expand Down Expand Up @@ -431,6 +436,7 @@ const FapProposalsAndAssignmentsTable = ({
/>
<div data-cy="fap-assignments-table">
<MaterialTable
tableRef={tableRef}
icons={tableIcons}
columns={translatedColumns}
title={
Expand Down Expand Up @@ -495,6 +501,7 @@ const FapProposalsAndAssignmentsTable = ({
}}
/>
</div>
{expandCollapseAllButton}
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from 'react';
import { useSearchParams } from 'react-router-dom';

import { Fap } from 'generated/sdk';
import { useExpandCollapseAll } from 'hooks/fap/useExpandCollapseAll';
import { FapMember, useFapMembersData } from 'hooks/fap/useFapMembersData';
import {
FapProposalAssignmentType,
Expand Down Expand Up @@ -122,6 +123,11 @@ const FapReviewersAndAssignmentsTable = ({
const page = searchParams.get('page');
const pageSize = searchParams.get('pageSize');

const { tableRef, expandCollapseAllButton } = useExpandCollapseAll(
'[data-cy="fap-reviewers-assignments-table"]',
[loadingMembers, loadingFapProposals]
);

const reviewersAndProposals: ReviewerAndProposals[] = FapMembersData.map(
(member) => {
const assignedProposals = fapProposals.FapProposalsData.filter(
Expand Down Expand Up @@ -253,46 +259,50 @@ const FapReviewersAndAssignmentsTable = ({
fapProposalsData={FapProposalsData}
assignProposalsToReviewer={handleMemberAssignmentToFapProposals}
/>
<MaterialTable
title="Reviewers and Assignments"
columns={FapReviewersAndAssignmentsTableColumns}
actions={tableActions}
detailPanel={(rowData) => (
<FapAssignedProposalsTable
assignedProposals={rowData.rowData.assignedProposals}
fapSecs={fap.fapSecretaries.map((s) => s.id)}
removeAssignedReviewer={removeAssignedReviewer}
updateView={updateFapProposalAssignmentsView}
/>
)}
data={reviewersAndProposals}
isLoading={loadingMembers || loadingFapProposals}
options={{
pageSize: pageSize ? +pageSize : Math.min(10, maxPageLength),
selection: true,
initialPage: page ? +page : 0,
pageSizeOptions: pageSizeOptions,
}}
localization={{
toolbar: {
nRowsSelected: (rowCount) => `${rowCount} reviewer(s) selected`,
},
}}
onSelectionChange={(selectedItems) => {
const selectedReviewers = selectedItems.map(
(item) => item.user.userId
);

setSearchParams((searchParams) => {
searchParams.delete('selection');
selectedReviewers.forEach((pk) =>
searchParams.append('selection', pk.toString())
<div data-cy="fap-reviewers-assignments-table">
<MaterialTable
tableRef={tableRef}
title="Reviewers and Assignments"
columns={FapReviewersAndAssignmentsTableColumns}
actions={tableActions}
detailPanel={(rowData) => (
<FapAssignedProposalsTable
assignedProposals={rowData.rowData.assignedProposals}
fapSecs={fap.fapSecretaries.map((s) => s.id)}
removeAssignedReviewer={removeAssignedReviewer}
updateView={updateFapProposalAssignmentsView}
/>
)}
data={reviewersAndProposals}
isLoading={loadingMembers || loadingFapProposals}
options={{
pageSize: pageSize ? +pageSize : Math.min(10, maxPageLength),
selection: true,
initialPage: page ? +page : 0,
pageSizeOptions: pageSizeOptions,
}}
localization={{
toolbar: {
nRowsSelected: (rowCount) => `${rowCount} reviewer(s) selected`,
},
}}
onSelectionChange={(selectedItems) => {
const selectedReviewers = selectedItems.map(
(item) => item.user.userId
);

return searchParams;
});
}}
/>
setSearchParams((searchParams) => {
searchParams.delete('selection');
selectedReviewers.forEach((pk) =>
searchParams.append('selection', pk.toString())
);

return searchParams;
});
}}
/>
</div>
{expandCollapseAllButton}
</>
);
};
Expand Down
88 changes: 88 additions & 0 deletions apps/frontend/src/hooks/fap/useExpandCollapseAll.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import KeyboardDoubleArrowDown from '@mui/icons-material/KeyboardDoubleArrowDown';
import KeyboardDoubleArrowRight from '@mui/icons-material/KeyboardDoubleArrowRight';
import { IconButton, Tooltip } from '@mui/material';
import {
DependencyList,
useCallback,
useEffect,
useRef,
useState,
} from 'react';
import React from 'react';
import { createPortal } from 'react-dom';

export function useExpandCollapseAll(
tableSelector: string,
dependencies: DependencyList
) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const tableRef = useRef<any>(null);
const [isAllExpanded, setIsAllExpanded] = useState(false);
const [portalTarget, setPortalTarget] = useState<HTMLElement | null>(null);

//find position to render button
useEffect(() => {
const frame = requestAnimationFrame(() => {
const container = document.querySelector(`${tableSelector} thead tr`);
if (!container) return;

const cells = container.querySelectorAll('th, td');
const cellArray = Array.from(cells);
const targetCell = cellArray.find(
(cell) =>
cell.hasAttribute('data-expand-all-target') ||
(cell.childElementCount === 0 && cell.textContent?.trim() === '')
);

if (targetCell) {
if (!targetCell.hasAttribute('data-expand-all-target')) {
targetCell.setAttribute('data-expand-all-target', 'true');
}
setPortalTarget(targetCell as HTMLElement);
}
});

return () => cancelAnimationFrame(frame);
}, dependencies);

//index each row and expand/collapse them
const toggleExpandAll = useCallback(() => {
const nextExpandedState = !isAllExpanded;

const sortedData = tableRef.current?.dataManager?.sortedData || [];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sortedData.forEach((row: any, index: number) => {
const isCurrentlyExpanded = !!row.tableData.showDetailPanel;
if (isCurrentlyExpanded !== nextExpandedState) {
const detailPanelProp = tableRef.current?.props.detailPanel;
const detailPanelRenderFunction = Array.isArray(detailPanelProp)
? detailPanelProp[0].render
: detailPanelProp;

tableRef.current?.onToggleDetailPanel(
[index],
detailPanelRenderFunction
);
}
});

setIsAllExpanded(nextExpandedState);
}, [isAllExpanded]);

const expandCollapseAllButton = portalTarget
? createPortal(
<Tooltip title={isAllExpanded ? 'Collapse all' : 'Expand all'}>
<IconButton onClick={toggleExpandAll} data-cy="expand-collapse-all">
{isAllExpanded ? (
<KeyboardDoubleArrowDown />
) : (
<KeyboardDoubleArrowRight />
)}
</IconButton>
</Tooltip>,
portalTarget
)
: null;

return { tableRef, expandCollapseAllButton };
}
Loading