Skip to content

Commit e5f1f44

Browse files
authored
MMT-4089: Fix inconsistencies with Collection Association (#1418)
* MMT-4089: Fix inconsistencies with Collection Association * MMT-4089: Editing based on CMR feedback * MMT-4089: Addressing PR Feedback
1 parent c9e4b80 commit e5f1f44

File tree

9 files changed

+868
-740
lines changed

9 files changed

+868
-740
lines changed

static/src/js/components/CollectionAssociationForm/CollectionAssociationForm.jsx

Lines changed: 164 additions & 163 deletions
Large diffs are not rendered by default.

static/src/js/components/CollectionAssociationForm/__tests__/CollectionAssociationForm.test.jsx

Lines changed: 235 additions & 144 deletions
Large diffs are not rendered by default.

static/src/js/components/CollectionAssociationForm/__tests__/__mocks__/CollectionAssociationResults.js

Lines changed: 205 additions & 235 deletions
Large diffs are not rendered by default.

static/src/js/components/ManageCollectionAssociation/ManageCollectionAssociation.jsx

Lines changed: 94 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import React, { useCallback, useState } from 'react'
1212
import pluralize from 'pluralize'
1313

1414
import Button from '@/js/components/Button/Button'
15+
import ControlledPaginatedContent from '@/js/components/ControlledPaginatedContent/ControlledPaginatedContent'
1516
import CustomModal from '@/js/components/CustomModal/CustomModal'
1617
import EllipsisText from '@/js/components/EllipsisText/EllipsisText'
17-
import Pagination from '@/js/components/Pagination/Pagination'
1818
import Table from '@/js/components/Table/Table'
1919

2020
import conceptTypeQueries from '@/js/constants/conceptTypeQueries'
@@ -41,14 +41,17 @@ const ManageCollectionAssociation = () => {
4141

4242
const { addNotification } = useNotificationsContext()
4343

44-
const [searchParams, setSearchParams] = useSearchParams()
44+
const derivedConceptType = getConceptTypeByConceptId(conceptId)
45+
46+
// Variables for deletion mutation
4547
const [collectionConceptIds, setCollectionConceptIds] = useState([])
4648
const [showDeleteModal, setShowDeleteModal] = useState(false)
49+
const [isDeleting, setIsDeleting] = useState(false)
4750

48-
const derivedConceptType = getConceptTypeByConceptId(conceptId)
49-
51+
// Variables for pagination
52+
const [searchParams, setSearchParams] = useSearchParams()
53+
const [activePage, setActivePage] = useState(1)
5054
const limit = 20
51-
const activePage = parseInt(searchParams.get('page'), 10) || 1
5255
const offset = (activePage - 1) * limit
5356

5457
let params = {
@@ -79,10 +82,7 @@ const ManageCollectionAssociation = () => {
7982
})
8083

8184
const [deleteAssociationMutation] = useMutation(DELETE_ASSOCIATION, {
82-
refetchQueries: [{
83-
query: conceptTypeQueries[derivedConceptType],
84-
variables: params
85-
}],
85+
8686
onCompleted: () => {
8787
setShowDeleteModal(false)
8888

@@ -93,8 +93,14 @@ const ManageCollectionAssociation = () => {
9393
})
9494

9595
setCollectionConceptIds([])
96+
97+
// Gives time for CMR to update data
98+
setTimeout(() => {
99+
refetch()
100+
}, 250)
96101
},
97102
onError: () => {
103+
setShowDeleteModal(false)
98104
addNotification({
99105
message: 'Error disassociating collection',
100106
variant: 'danger'
@@ -105,13 +111,15 @@ const ManageCollectionAssociation = () => {
105111
})
106112

107113
// Handles deleting selected collection
108-
// if no collections selected, returns an error notification
109114
const handleDeleteAssociation = () => {
115+
setIsDeleting(true)
110116
deleteAssociationMutation({
111117
variables: {
112118
conceptId,
113119
associatedConceptIds: collectionConceptIds
114120
}
121+
}).finally(() => {
122+
setIsDeleting(false)
115123
})
116124
}
117125

@@ -124,14 +132,13 @@ const ManageCollectionAssociation = () => {
124132
if (order === 'ascending') nextSortKey = `-${key}`
125133
if (order === 'descending') nextSortKey = key
126134

127-
// Reset the page parameter
128-
currentParams.delete('page')
129-
130135
// Set the sort key
131136
currentParams.set('sortKey', nextSortKey)
132137

133138
return Object.fromEntries(currentParams)
134139
})
140+
141+
setActivePage(1) // Reset to first page when sorting
135142
}, [])
136143

137144
const buildEllipsisTextCell = useCallback((cellData) => (
@@ -140,8 +147,8 @@ const ManageCollectionAssociation = () => {
140147
</EllipsisText>
141148
), [])
142149

143-
// Handles checkbox selections, if checked add the conceptId to the state variable
144-
// and pops the added conceptId from the array.
150+
// Adds or removes checked collections from collectionConceptIds array
151+
// which is provided to the deleteMutation
145152
const handleCheckbox = (event) => {
146153
const { target } = event
147154
const { value } = target
@@ -200,11 +207,7 @@ const ManageCollectionAssociation = () => {
200207
]
201208

202209
const setPage = (nextPage) => {
203-
setSearchParams((currentParams) => {
204-
currentParams.set('page', nextPage)
205-
206-
return Object.fromEntries(currentParams)
207-
})
210+
setActivePage(nextPage)
208211
}
209212

210213
const toggleShowDeleteModal = (nextState) => {
@@ -216,91 +219,101 @@ const ManageCollectionAssociation = () => {
216219
toggleShowDeleteModal(true)
217220
})
218221

219-
// Handle refresh, calls getMetadata to get the list of association
220-
// TODO: MMT-4089 See if we can get rid of this refresh button.
221-
const handleRefreshPage = () => {
222-
refetch()
223-
}
224-
225-
const refreshAccessibleEventProps = useAccessibleEvent(() => {
226-
handleRefreshPage()
227-
})
228-
229222
const { [camelCase(derivedConceptType)]: concept } = data
230223

231224
const { collections: associatedCollections } = concept
232225

233-
const { items = [], count } = associatedCollections
234-
235-
const totalPages = Math.ceil(count / limit)
236-
237-
const currentPageIndex = Math.floor(offset / limit)
238-
const firstResultIndex = currentPageIndex * limit
239-
const isLastPage = totalPages === activePage
240-
const lastResultIndex = firstResultIndex + (isLastPage ? count % limit : limit)
241-
242-
const paginationMessage = count > 0
243-
? `Showing ${totalPages > 1 ? `Collection Associations ${firstResultIndex + 1}-${lastResultIndex} of ${count}` : `${count} ${pluralize('Collection Association', count)}`}`
244-
: 'No Collection Associations found'
226+
const { items, count } = associatedCollections
245227

246228
return (
247-
<div className="mt-4">
229+
<div>
248230
<Alert className="fst-italic fs-6" variant="warning">
249231
<i className="eui-icon eui-fa-info-circle" />
250232
{' '}
251233
Association operations may take some time. If you are not seeing what you expect below,
252234
please
253235
{' '}
254-
<span
255-
className="text-decoration-underline"
236+
<button
237+
className="btn btn-link p-0 text-decoration-underline"
256238
style={
257239
{
258240
color: 'blue',
259241
cursor: 'pointer'
260242
}
261243
}
262-
// eslint-disable-next-line react/jsx-props-no-spreading
263-
{...refreshAccessibleEventProps}
244+
onClick={() => refetch()}
245+
aria-label="Refresh the page"
246+
type="button"
264247
>
265-
refresh the page
266-
</span>
248+
<i>refresh the page</i>
249+
</button>
267250
</Alert>
268-
<Row className="d-flex justify-content-between align-items-center mb-4 mt-5">
269-
<Col className="mb-4 flex-grow-1" xs="auto">
270-
{
271-
(!!count) && (
272-
<span className="text-secondary fw-bolder">{paginationMessage}</span>
251+
<ControlledPaginatedContent
252+
activePage={activePage}
253+
count={count}
254+
limit={limit}
255+
setPage={setPage}
256+
>
257+
{
258+
({
259+
totalPages,
260+
pagination,
261+
firstResultPosition,
262+
lastResultPosition
263+
}) => {
264+
const paginationMessage = count > 0
265+
? `Showing ${totalPages > 1 ? `${firstResultPosition}-${lastResultPosition} of` : ''} ${count} Collection ${pluralize('Association', count)}`
266+
: 'No collection associations found'
267+
268+
return (
269+
<>
270+
<Row className="d-flex justify-content-between align-items-center mb-4">
271+
<Col className="mb-4 flex-grow-1" xs="auto">
272+
{
273+
(!!count) && (
274+
<span className="text-secondary fw-bolder">{paginationMessage}</span>
275+
)
276+
}
277+
</Col>
278+
<Col className="mb-4 flex-grow-1" xs="auto" />
279+
{
280+
totalPages > 1 && (
281+
<Col xs="auto">
282+
{pagination}
283+
</Col>
284+
)
285+
}
286+
</Row>
287+
<Table
288+
className="m-5"
289+
columns={columns}
290+
data={items}
291+
generateCellKey={({ conceptId: conceptIdCell }, dataKey) => `column_${dataKey}_${conceptIdCell}`}
292+
generateRowKey={({ conceptId: conceptIdRow }) => `row_${conceptIdRow}`}
293+
id="associated-collections"
294+
limit={count}
295+
noDataMessage="No collection associations found."
296+
/>
297+
{
298+
totalPages > 1 && (
299+
<Row>
300+
<Col xs="12" className="pt-4 d-flex align-items-center justify-content-center">
301+
<div>
302+
{pagination}
303+
</div>
304+
</Col>
305+
</Row>
306+
)
307+
}
308+
</>
273309
)
274310
}
275-
</Col>
276-
{
277-
totalPages > 1 && (
278-
<Col xs="auto">
279-
<Pagination
280-
setPage={setPage}
281-
activePage={activePage}
282-
totalPages={totalPages}
283-
/>
284-
</Col>
285-
)
286311
}
287-
</Row>
288-
<Table
289-
className="m-5"
290-
columns={columns}
291-
data={items}
292-
generateCellKey={({ conceptId: conceptIdCell }, dataKey) => `column_${dataKey}_${conceptIdCell}`}
293-
generateRowKey={({ conceptId: conceptIdRow }) => `row_${conceptIdRow}`}
294-
id="associated-collections"
295-
limit={count}
296-
noDataMessage="No collection associations found."
297-
offset={offset}
298-
/>
312+
</ControlledPaginatedContent>
299313
<Button
300314
className="mt-4"
301315
variant="danger"
302-
disabled={collectionConceptIds.length === 0}
303-
// eslint-disable-next-line react/jsx-props-no-spreading
316+
disabled={collectionConceptIds.length === 0 || isDeleting}
304317
{...accessibleEventProps}
305318
>
306319
Delete Selected Associations
@@ -319,7 +332,8 @@ const ManageCollectionAssociation = () => {
319332
{
320333
label: 'Yes',
321334
variant: 'primary',
322-
onClick: handleDeleteAssociation
335+
onClick: handleDeleteAssociation,
336+
disabled: isDeleting
323337
}
324338
]
325339
}

0 commit comments

Comments
 (0)