Skip to content

Commit 8e305ea

Browse files
committed
MMT-4089: Fix inconsistencies with Collection Association
1 parent c9e4b80 commit 8e305ea

File tree

8 files changed

+841
-747
lines changed

8 files changed

+841
-747
lines changed

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

Lines changed: 162 additions & 162 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: 84 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
import {
2-
Alert,
3-
Col,
4-
Row
5-
} from 'react-bootstrap'
1+
import { Col, Row } from 'react-bootstrap'
62
import { camelCase } from 'lodash-es'
73
import { useMutation, useSuspenseQuery } from '@apollo/client'
84
import { useParams } from 'react-router'
@@ -12,9 +8,9 @@ import React, { useCallback, useState } from 'react'
128
import pluralize from 'pluralize'
139

1410
import Button from '@/js/components/Button/Button'
11+
import ControlledPaginatedContent from '@/js/components/ControlledPaginatedContent/ControlledPaginatedContent'
1512
import CustomModal from '@/js/components/CustomModal/CustomModal'
1613
import EllipsisText from '@/js/components/EllipsisText/EllipsisText'
17-
import Pagination from '@/js/components/Pagination/Pagination'
1814
import Table from '@/js/components/Table/Table'
1915

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

4238
const { addNotification } = useNotificationsContext()
4339

44-
const [searchParams, setSearchParams] = useSearchParams()
40+
const derivedConceptType = getConceptTypeByConceptId(conceptId)
41+
42+
// Variables for deletion mutation
4543
const [collectionConceptIds, setCollectionConceptIds] = useState([])
4644
const [showDeleteModal, setShowDeleteModal] = useState(false)
45+
const [isDeleting, setIsDeleting] = useState(false)
4746

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

5453
let params = {
@@ -75,14 +74,11 @@ const ManageCollectionAssociation = () => {
7574
}
7675

7776
const { data, refetch } = useSuspenseQuery(conceptTypeQueries[derivedConceptType], {
78-
variables: params
77+
variables: params,
78+
fetchPolicy: 'network-only'
7979
})
8080

8181
const [deleteAssociationMutation] = useMutation(DELETE_ASSOCIATION, {
82-
refetchQueries: [{
83-
query: conceptTypeQueries[derivedConceptType],
84-
variables: params
85-
}],
8682
onCompleted: () => {
8783
setShowDeleteModal(false)
8884

@@ -93,8 +89,10 @@ const ManageCollectionAssociation = () => {
9389
})
9490

9591
setCollectionConceptIds([])
92+
refetch() // Refetch the data to update the associated collections list
9693
},
9794
onError: () => {
95+
setShowDeleteModal(false)
9896
addNotification({
9997
message: 'Error disassociating collection',
10098
variant: 'danger'
@@ -105,13 +103,15 @@ const ManageCollectionAssociation = () => {
105103
})
106104

107105
// Handles deleting selected collection
108-
// if no collections selected, returns an error notification
109106
const handleDeleteAssociation = () => {
107+
setIsDeleting(true)
110108
deleteAssociationMutation({
111109
variables: {
112110
conceptId,
113111
associatedConceptIds: collectionConceptIds
114112
}
113+
}).finally(() => {
114+
setIsDeleting(false)
115115
})
116116
}
117117

@@ -124,14 +124,13 @@ const ManageCollectionAssociation = () => {
124124
if (order === 'ascending') nextSortKey = `-${key}`
125125
if (order === 'descending') nextSortKey = key
126126

127-
// Reset the page parameter
128-
currentParams.delete('page')
129-
130127
// Set the sort key
131128
currentParams.set('sortKey', nextSortKey)
132129

133130
return Object.fromEntries(currentParams)
134131
})
132+
133+
setActivePage(1) // Reset to first page when sorting
135134
}, [])
136135

137136
const buildEllipsisTextCell = useCallback((cellData) => (
@@ -140,8 +139,8 @@ const ManageCollectionAssociation = () => {
140139
</EllipsisText>
141140
), [])
142141

143-
// Handles checkbox selections, if checked add the conceptId to the state variable
144-
// and pops the added conceptId from the array.
142+
// Adds or removes checked collections from collectionConceptIds array
143+
// which is provided to the deleteMutation
145144
const handleCheckbox = (event) => {
146145
const { target } = event
147146
const { value } = target
@@ -200,11 +199,7 @@ const ManageCollectionAssociation = () => {
200199
]
201200

202201
const setPage = (nextPage) => {
203-
setSearchParams((currentParams) => {
204-
currentParams.set('page', nextPage)
205-
206-
return Object.fromEntries(currentParams)
207-
})
202+
setActivePage(nextPage)
208203
}
209204

210205
const toggleShowDeleteModal = (nextState) => {
@@ -216,91 +211,80 @@ const ManageCollectionAssociation = () => {
216211
toggleShowDeleteModal(true)
217212
})
218213

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-
229214
const { [camelCase(derivedConceptType)]: concept } = data
230215

231216
const { collections: associatedCollections } = concept
232217

233218
const { items = [], count } = associatedCollections
234219

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'
245-
246220
return (
247-
<div className="mt-4">
248-
<Alert className="fst-italic fs-6" variant="warning">
249-
<i className="eui-icon eui-fa-info-circle" />
250-
{' '}
251-
Association operations may take some time. If you are not seeing what you expect below,
252-
please
253-
{' '}
254-
<span
255-
className="text-decoration-underline"
256-
style={
257-
{
258-
color: 'blue',
259-
cursor: 'pointer'
260-
}
261-
}
262-
// eslint-disable-next-line react/jsx-props-no-spreading
263-
{...refreshAccessibleEventProps}
264-
>
265-
refresh the page
266-
</span>
267-
</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>
221+
<div>
222+
<ControlledPaginatedContent
223+
activePage={activePage}
224+
count={count}
225+
limit={limit}
226+
setPage={setPage}
227+
>
228+
{
229+
({
230+
totalPages,
231+
pagination,
232+
firstResultPosition,
233+
lastResultPosition
234+
}) => {
235+
const paginationMessage = count > 0
236+
? `Showing ${totalPages > 1 ? `${firstResultPosition}-${lastResultPosition} of` : ''} ${count} Collection ${pluralize('Association', count)}`
237+
: 'No collection associations found'
238+
239+
return (
240+
<>
241+
<Row className="d-flex justify-content-between align-items-center mb-4">
242+
<Col className="mb-4 flex-grow-1" xs="auto">
243+
{
244+
(!!count) && (
245+
<span className="text-secondary fw-bolder">{paginationMessage}</span>
246+
)
247+
}
248+
</Col>
249+
<Col className="mb-4 flex-grow-1" xs="auto" />
250+
{
251+
totalPages > 1 && (
252+
<Col xs="auto">
253+
{pagination}
254+
</Col>
255+
)
256+
}
257+
</Row>
258+
<Table
259+
className="m-5"
260+
columns={columns}
261+
data={items}
262+
generateCellKey={({ conceptId: conceptIdCell }, dataKey) => `column_${dataKey}_${conceptIdCell}`}
263+
generateRowKey={({ conceptId: conceptIdRow }) => `row_${conceptIdRow}`}
264+
id="associated-collections"
265+
limit={count}
266+
noDataMessage="No collection associations found."
267+
/>
268+
{
269+
totalPages > 1 && (
270+
<Row>
271+
<Col xs="12" className="pt-4 d-flex align-items-center justify-content-center">
272+
<div>
273+
{pagination}
274+
</div>
275+
</Col>
276+
</Row>
277+
)
278+
}
279+
</>
273280
)
274281
}
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-
)
286282
}
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-
/>
283+
</ControlledPaginatedContent>
299284
<Button
300285
className="mt-4"
301286
variant="danger"
302-
disabled={collectionConceptIds.length === 0}
303-
// eslint-disable-next-line react/jsx-props-no-spreading
287+
disabled={collectionConceptIds.length === 0 || isDeleting}
304288
{...accessibleEventProps}
305289
>
306290
Delete Selected Associations
@@ -319,7 +303,8 @@ const ManageCollectionAssociation = () => {
319303
{
320304
label: 'Yes',
321305
variant: 'primary',
322-
onClick: handleDeleteAssociation
306+
onClick: handleDeleteAssociation,
307+
disabled: isDeleting
323308
}
324309
]
325310
}

0 commit comments

Comments
 (0)