From 6416e8fb6c4f12e384299681742ce6cabc7ea4c6 Mon Sep 17 00:00:00 2001 From: Grant Fitzsimmons <37256050+grantfitzsimmons@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:11:19 -0500 Subject: [PATCH] feat(forms): use ordinal for attachment ordering --- .../lib/hooks/__tests__/useCollection.test.ts | 62 +++++++++++++++++++ .../js_src/lib/hooks/useCollection.tsx | 17 ++++- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/hooks/__tests__/useCollection.test.ts b/specifyweb/frontend/js_src/lib/hooks/__tests__/useCollection.test.ts index b2b3d6eca29..018aa393384 100644 --- a/specifyweb/frontend/js_src/lib/hooks/__tests__/useCollection.test.ts +++ b/specifyweb/frontend/js_src/lib/hooks/__tests__/useCollection.test.ts @@ -121,6 +121,44 @@ describe('useCollection', () => { ) ); + const collectionObjectAttachmentUrl = + `/api/specify/collectionobjectattachment/?domainfilter=false&collectionobject=${ceID}&offset=0`; + const collectionObjectAttachmentObjects = [ + { + id: 1, + resource_uri: getResourceApiUrl('CollectionObjectAttachment', 1), + ordinal: 3, + }, + { + id: 2, + resource_uri: getResourceApiUrl('CollectionObjectAttachment', 2), + ordinal: 1, + }, + { + id: 3, + resource_uri: getResourceApiUrl('CollectionObjectAttachment', 3), + ordinal: 2, + }, + ]; + + overrideAjax( + `/api/specify/collectionobject/${ceID}/`, + { + id: ceID, + resource_uri: getResourceApiUrl('CollectionObject', ceID), + } + ); + + overrideAjax( + collectionObjectAttachmentUrl, + makeBackendResponse(collectionObjectAttachmentObjects, 0, 3) + ); + + overrideAjax( + `${collectionObjectAttachmentUrl}&limit=0`, + makeBackendResponse(collectionObjectAttachmentObjects, 0, 3, 0) + ); + test('to-many independent collection gets fetched and set correctly', async () => { const collectingEvent = new tables.CollectingEvent.Resource({ id: ceID }); const collectionObject = @@ -148,6 +186,30 @@ describe('useCollection', () => { }); }); + test('to-many independent collection defaults to attachment ordinal sorting', async () => { + const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + const collectionObject = new tables.CollectionObject.Resource({ id: ceID }); + const collectionObjectAttachments = + tables.CollectionObject.strictGetRelationship('collectionObjectAttachments'); + + const { result } = renderHook(() => + useCollection({ + parentResource: collectionObject, + relationship: collectionObjectAttachments, + }) + ); + + await waitFor(() => { + expect(result.current[0]).toBeDefined(); + }); + + expect(castAsCollection(result.current[0]).models.map((resource) => + resource.get('ordinal') + )).toEqual([1, 2, 3]); + + warnSpy.mockRestore(); + }); + test('to-many dependent collection sorts', async () => { let sortBy: SubViewSortField = { direction: 'asc', diff --git a/specifyweb/frontend/js_src/lib/hooks/useCollection.tsx b/specifyweb/frontend/js_src/lib/hooks/useCollection.tsx index be9d7b3c7a0..d7d38979d77 100644 --- a/specifyweb/frontend/js_src/lib/hooks/useCollection.tsx +++ b/specifyweb/frontend/js_src/lib/hooks/useCollection.tsx @@ -98,7 +98,22 @@ const fetchToManyCollection = async ({ related: parentResource, field: relationship.getReverse(), }) as Collection; - if (sortBy === undefined) return collection; + + if (sortBy === undefined) { + if ( + relationship.relatedTable.name.endsWith('Attachment') && + relationship.relatedTable.getField('ordinal') !== undefined + ) { + overwriteReadOnly( + collection, + 'models', + Array.from(collection.models).sort( + sortFunction((resource) => resource.get('ordinal'), false) + ) + ); + } + return collection; + } // BUG: this does not look into related tables const field = sortBy.fieldNames[0];