Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Preparation form links to Interactions #6110

Open
wants to merge 17 commits into
base: production
Choose a base branch
from

Conversation

grantfitzsimmons
Copy link
Member

@grantfitzsimmons grantfitzsimmons commented Jan 17, 2025

Fixes #6112

Checklist

  • Self-review the PR after opening it to make sure the changes look good and
    self-explanatory (or properly documented)
  • Add relevant documentation (Tester - Dev)

Before

You could not see any linked disposals or exchanges. Interactions that were empty still displayed with no results.

image

After

  • Interactions are only shown when they are linked to a Preparation
    • If none are linked, it shows "There are no interactions linked to this preparation"
  • When clicking on the "Show Interactions" button, the user should see all associated
    • Resolved Loans
    • Unresolved Loans
    • Gifts
    • Disposals
    • Exchange Out
    • Exchange In
Screen.Recording.2025-01-17.at.11.06.02.AM.mov

Testing instructions

You will need to make sure you have the "Show Interactions" / "Show Loans" button on the Preparation form to test this properly. If you do not already, here is the XML to add:

 <cell type="command" id="ShowLoansBtn" name="ShowLoansBtn" label="SHOW_INTERACTIONS" ignore="true"/>
  • Create a new CO with a memorable catalog number (you'll need it)
    No Interactions:
  • Create a new Preparation, give it a large count, and save
  • Click on the 'Show Interactions' button and see that "There are no interactions linked to this preparation." displays
    Gift:
  • Create a new Loan using the catalog number
  • Select some portion of your preparation's available count and create the loan
  • Go back to the Preparation and click on the 'Show Interactions' button– see it appears under "Open Loans"
  • Resolve the loan
  • Go back to the Preparation and click on the 'Show Interactions' button– see it appears under "Resolved Loans"
    Gift:
  • Create a new Gift using the catalog number
  • Select some portion of your preparation's available count and create the gift
  • Go back to the Gift and click on the 'Show Interactions' button– see it appears under "Gift"
    Exchange Out:
  • Create a new Exchange Out using the catalog number
  • Select some portion of your preparation's available count and create the Exchange Out
  • Go back to the Exchange Out and click on the 'Show Interactions' button– see it appears under "Exchange Out"
    Exchange In:
  • Create a new Exchange In using the catalog number
  • Select some portion of your preparation's available count and create the Exchange In
  • Go back to the Exchange In and click on the 'Show Interactions' button– see it appears under "Exchange In"
    Disposal:
  • Create a new Disposal using the catalog number
  • Select some portion of your preparation's available count and create the Disposal
  • Go back to the Disposal and click on the 'Show Interactions' button– see it appears under "Disposal"

Test this by using a combination of various interactions and preparations. Involve the preparation in multiple interactions of the same type. Verify that existing preparations linked to interactions appear as expected. Compare the behavior to production and let me know if this is undesirable.

  • OK I did it

If anyone is interested in updating the documentation for this, I would appreciate it. Thank you!

@grantfitzsimmons grantfitzsimmons marked this pull request as ready for review January 17, 2025 17:21
@grantfitzsimmons grantfitzsimmons marked this pull request as draft January 19, 2025 00:18
@grantfitzsimmons grantfitzsimmons marked this pull request as ready for review January 19, 2025 21:24
@melton-jason melton-jason self-requested a review January 19, 2025 21:31
Comment on lines +140 to +142
{interactionsText.preparations({
preparationTable: tables.Preparation.label,
})}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting that prettier did not reformat this file in 0371f02

part of #6051 I guess

melton-jason added a commit that referenced this pull request Jan 20, 2025
Comment on lines +211 to +219
disposals: {
comment: 'Example: Disposal records',
'en-us': '{disposalTable:string} records',
'es-es': '{disposalTable:string} registros',
'fr-fr': '{disposalTable:string} enregistrements',
'ru-ru': '{disposalTable:string} записи',
'uk-ua': '{disposalTable:string} записи',
'de-ch': '{disposalTable:string} Datensätze',
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there's multiple ${InteractionTable} records localization strings. If these are used in the same context, can they merged into a single localization string?

For example:

Suggested change
disposals: {
comment: 'Example: Disposal records',
'en-us': '{disposalTable:string} records',
'es-es': '{disposalTable:string} registros',
'fr-fr': '{disposalTable:string} enregistrements',
'ru-ru': '{disposalTable:string} записи',
'uk-ua': '{disposalTable:string} записи',
'de-ch': '{disposalTable:string} Datensätze',
},
interactionRecords: {
comment: 'Example: Loan records',
'en-us': '{interactionTableLabel:string} records',
},

Which can be used like:

// Loan records
interactionsText.interactionRecords({
  interactionTableLabel: tables.Loan.label,
});

// Preparation records
interactionsText.interactionRecords({
  interactionTableLabel: tables.Preparation.label,
});

// ...

@@ -17,6 +17,11 @@ export const interactionsText = createDictionary({
'uk-ua': 'Взаємодії',
'de-ch': 'Interaktionen',
},
noInteractions: {
comment: 'Example: There are no interactions linked to this {preparation}',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should explicit examples include a placeholder like this? Personally, I think examples should be one or more values the placeholder can take: there's hopefully less confusion for the localizer this way.

If you need to provide additional context for how this might be used or appear in the application, you can do so outside of the example; like in the following example:

comment: `
Example usage: "Choose collection:". Used only if there is nothing else on
this line after the colon heading
`,

</>
) : (
<>
{Array.isArray(data.openLoans) && data.openLoans.length > 0 && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data.openLoans.length > 0

With this assertion, there is actually a slight change in UX compared to the previous implementation, was this intentional?

An empty array is a valid prop to pass to the List component, and it used to show No Results under the table icon.

return resources.length === 0 ? (
<>{commonText.noResults()}</>
) : Array.isArray(entries) ? (

Now, if no results are returned, the Interaction is not shown at all.
I'm all for this change if it was intentional: it cuts down on potentially unneeded information and thus reduces visual clutter!

Comment on lines +110 to +116
exchangeIns: hasTablePermission('ExchangeInPrep', 'read')
? fetchCollection('ExchangeInPrep', {
limit: DEFAULT_FETCH_LIMIT,
preparation: preparation.get('id'),
domainFilter: false,
}).then(({ records }) => records.map(deserializeResource))
: undefined,
Copy link
Contributor

@melton-jason melton-jason Jan 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While probably not in the scope of the PR, there are a few things I noticed about the implementation of the ShowLoansCommand feature.

Conceptually, we are fetching the first 20 InteractionPreparation records associated with the Preparation (e.g., we're fetching al ExchangeInPrep records related to the Preparation in the highlighted section of code), and then we are iterating over each of the InteractionPreparation records and fetching their related Interactions:

const interactions: RA<SpecifyResource<AnySchema>> = await Promise.all(
resources.map(async (resource) => resource.rgetPromise(fieldName))
);
return interactions
.map((resource) => ({
label: resource.get(displayFieldName),
resource,
}))
.sort(sortFunction(({ label }) => label));

(With the highlighted code of this comment, this would be iterating over all of the ExchangeInPrep records and fetching their related ExchangeIn record).

The problem with approach becomes apparent because we do not omit "duplicate" Interaction records in the list of Interactions for a Preparation.
Put more precisely, the Show Transactions Dialog can repeat the same resource multiple times if there are multiple InteractionPreps

Here is a setup demonstrating the Issue: there are 20 Preparations included in ExchangeOut #1, and 1 Preparation included in ExchangeOut #2. Specify fetches the first 20 ExchangeOutPreps related to the Preparation (i.e., all of the ExchangeOutPreps in ExchangeOut #1), then fetches the ExchangeOut related to each of those ExchangeOutPreps and uses that resource to populate the Dialog

fetching_issues.mov

I would assume the purpose of this Dialog is to show related Interaction records? In reality it is showing the InteractionPreparations which all link to the Interaction record.

Besides not showing identical Interaction records, I also think the fetching logic here can be improved!

Currently, the method of fetching the Interaction after fetching the InteractionPreparations can be expensive if each InteractionPreparation exists for a different Interaction: we need a separate network request for each Interaction (this is after the static network request to fetch the InteractionPreparations):

network_requests.mov

We can actually directly use the API to fetch the related Interaction records for a table in a single network request.
Currently, we are constructing a query like /api/specify/interactionprep/?preparation=<PREP_ID>, then iterating over the returned records and constructing queries for /api/specify/interaction/<INTERACTION_ID>/ from the relationship name to the Interaction.

But we can use a query like /api/specify/interaction/?relationshipToInteractionPrep__preparation=<PREP_ID> (e.g., /api/specify/loan/?loanpreparations__preparation=<PREP_ID>) to directly return the collection of Interaction records given a specific preparation id.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have actually worked on a branch - issue-6108-b - to address this problem. It is functionally a complete reimplementation of the Show Transactions Dialog: take a look through the branch and see if there's any ideas you'd like to carry over to this implementation!

Here are some videos comparing production, this branch, and issue-6108-b:

production

production.mov

The error here is a bug in production which occurs whenever there are ExchangeOut records associated with a Preparation

issue-6108

issue-6108.mov

issue-6108-b

issue-6108-b.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: 📋Back Log
Development

Successfully merging this pull request may close these issues.

Extend "Show Interactions" dialog to Disposal, Exchange In, and Exchange Out
3 participants