Skip to content

Remove snapshot filters from Entities and Submissions list#1459

Merged
sadiqkhoja merged 6 commits intogetodk:masterfrom
sadiqkhoja:fixes/remove-snapshot-filters
Feb 24, 2026
Merged

Remove snapshot filters from Entities and Submissions list#1459
sadiqkhoja merged 6 commits intogetodk:masterfrom
sadiqkhoja:fixes/remove-snapshot-filters

Conversation

@sadiqkhoja
Copy link
Copy Markdown
Contributor

@sadiqkhoja sadiqkhoja commented Jan 7, 2026

Closes getodk/central#1557

Changes:

  • Removed snapshot filters from the Entities and Submissions list.
  • If user end up on a page where there are no rows on the page then pagination component will show Rows 0 of N, N being the total number of records.
  • Since we don't need to keep the consistency between page navigation, I have removed deletedSubmissions and deletedEntities variable from the corresponding resource. The result of that is that when user deletes a row, then navigates to another page and then returns to the original page, they would see 250 (or whatever page size) records. Previously we used to show 250 - deleted records on the original page.
  • Since I am no longer maintaining state of deleted records, a generic message There are no Entities/Submissions to show will be displayed if all rows are deleted by the user. Previously we used to show All Entities/Submissions on the page have been deleted.

Known issues / questions

  • When user deletes a row, then total count on the top navigation tab is updated but total count in the pagination control is not updated. Additionally, the text "Rows 1-250 of N" is not updated. Second part is a bit complicated because we should drop with the row ranges when number of pages are less than 10, should we just subtract 1 from the range of the current page like 251-499 would be the second option if row is deleted from the second page? Should I fix this in the current PR or in a separate PR?
  • There was a suggest to show toast message if the count is changed between page navigation, I haven't implemented that. Wouldn't that be quite annoying if Submissions/Entities are being created continuously?

What has been done to verify that this works as intended?

All tests are passing.

Why is this the best possible solution? Were any other approaches considered?

It's mostly removal of code.

How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?

All functionality around filters, pagination, search, delete and bulk delete should be tested on Submissions and Entity list page.

Does this change require updates to user documentation? If so, please file an issue here and include the link below.

No

Before submitting this PR, please make sure you have:

  • run npm run test and npm run lint and confirmed all checks still pass OR confirm CircleCI build passes
  • verified that any code or assets from external sources are properly credited in comments or that everything is internally sourced

@sadiqkhoja sadiqkhoja force-pushed the fixes/remove-snapshot-filters branch from 3a23949 to f485773 Compare January 7, 2026 18:09
@sadiqkhoja sadiqkhoja force-pushed the fixes/remove-snapshot-filters branch from f485773 to 969065d Compare January 7, 2026 19:55
@sadiqkhoja
Copy link
Copy Markdown
Contributor Author

Met with @lognaturel and @nicoleorlowski, agreed upon following changes:

  • refresh the data when using bulk delete
  • Update total count on the pagination control
    • Subtract from the second number of the range on non-bulk delete
  • Remove page dropdown (shown when there are less than 10 pages) from the pagination control

Separate issue for:

  • Adding another pagination control at the top
  • Remove/reposition timestamp of last data refreshe

@sadiqkhoja sadiqkhoja force-pushed the fixes/remove-snapshot-filters branch 2 times, most recently from 60cf462 to c7b7474 Compare January 27, 2026 20:43
* removed page dropdown which was shown when number of pages were less than 10
* update count and range in the pagination control when a row is deleted
* refresh the data after bulk delete
@sadiqkhoja sadiqkhoja force-pushed the fixes/remove-snapshot-filters branch from c7b7474 to 937a6e4 Compare January 27, 2026 20:57
Copy link
Copy Markdown
Member

@matthew-white matthew-white left a comment

Choose a reason for hiding this comment

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

Still taking a look, but I thought I'd leave a few questions/comments so far.

this.dataset.entities -= uuids.length;


this.bulkDeletedEntities = [...this.selectedEntities];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For this.bulkDeletedEntities, I think we could now just store the entity UUIDs. That seems to be the only piece of data that's needed anymore. Just thinking to reduce the amount of entity data in memory.

this.bulkDeletedEntities = [...this.selectedEntities];
this.odataEntities.value = this.odataEntities.value.filter(e => !this.selectedEntities.has(e));
this.alert.success(this.$tcn('alert.bulkDelete', this.selectedEntities.size))
.cta(this.$t('action.undo'), () => this.requestBulkRestore(uuids));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is maybe outside the scope of this PR, but this.requestBulkRestore() doesn't seem to take an argument.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I understand a lot of the code removal here, but not 100% why this line was removed. Is it because the watcher on alert.state does this already?

{{ text }}
</option>
</select>
<template v-if="empty">0</template>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
<template v-if="empty">0</template>
<template v-if="empty">{{ $n(0, 'default') }}</template>

Wondering whether an Arabic locale would display a different numeral for 0?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Introduce a new key to handle 0 case. We need to say Row 0 of N, here default pluralization scheme fails; we can't say "Rows 0"

type: Number,
required: true
},
removed: {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It helped me once I understood that this is the number removed specifically from the current page. How about adding a code comment along those lines?

Comment on lines -242 to -252
.beforeEachResponse((_, { url }) => {
const filters = new URL(url, window.location.origin).searchParams.get('$filter').split(' and ');

const start = filters[2].split(' ge ')[1];
start.should.equal('1970-01-01T00:00:00.000Z');
DateTime.fromISO(start).zoneName.should.equal(Settings.defaultZoneName);

const end = filters[3].split(' le ')[1];
end.should.equal('1970-01-02T23:59:59.999Z');
DateTime.fromISO(end).zoneName.should.equal(Settings.defaultZoneName);
})
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I feel like these are still useful assertions and aren't just about the snapshot filter. Maybe the indices on filters here just need to be changed for the test to pass? Similar to the changes for entity/filters.spec.js.

type: Array,
required: true
},
empty: Boolean,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can this be derived from count, removed, page, and size? Something like count.value === 0 || props.page > lastPage.value? If it can't be, I think a code comment explaining the case that it's capturing would be helpful.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

let's compute empty using count, removed, sizeOfCurrentPage

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

also add a unit test

return result;
});

const sizeOfCurrentPage = computed(() => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this use props.empty somehow? Something like:

if (props.empty) return 0;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have removed props.empty. I was thinking to if (props.count === 0) return 0; but we don't show the pagination component if the count is 0

{{ text }}
</option>
</select>
<template v-if="empty">0</template>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there a test of the case where props.empty is true? I did a quick search for "Row 0", but I didn't find any matches. I might have missed the relevant tests though.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see that there isn't any pagination.spec.js. Instead, the component is tested implicitly via tests of SubmissionList and EntityList. I was probably the one who decided not to add a pagination.spec.js, but with the introduction of new props, part of me is wondering whether it'd improve testing or make the component's contract clearer to add such a file. I'm not necessarily advocating for that, but I'm wondering what you think of that idea.

Comment on lines 262 to 268
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

From the PR description:

Since I am no longer maintaining state of deleted records, a generic message There are no Entities/Submissions to show will be displayed if all rows are deleted by the user. Previously we used to show All Entities/Submissions on the page have been deleted.

Mostly this makes sense to me, but I'm wondering about the case where the table is filtered. In that case, it won't say, "There are no Entities to show.", it'll say "There are no matching Entities." That feels a little less accurate/clear to me. I guess you could argue that "matching" includes the page number, but I wanted to ask about it.

Also, I know that removedEntities is now just a number, but a set, but the only way it was used here before was removedEntities.size. Could the number be used in lieu of removedEntities.size? I know they're not exactly the same, since the number now just refers to the current page. For that reason, the first if no longer works, but doesn't the second if still work?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agree. Also add a test that message is correct if table is filtered and all rows are deleted.

if (confirm != null) this.confirmDelete = confirm;

this.odata.removedSubmissions.add(instanceId);
this.odata.removedSubmissions += 1;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I feel like there's a slight race condition here, though it probably doesn't matter.

  • Delete a submission, checking "Delete immediately before confirmation".
  • Delete a second submission.
  • While the delete request is in progress, navigate to another page.
  • If the new page data is received before the success response from the delete, then removedSubmissions will be incremented for the current page when it shouldn't be.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

My initial thought is that nothing needs to change, since removedSubmissions isn't used in any critical way. Mostly I just wanted to mention it as food for thought.

{{ text }}
</option>
</select>
<template v-if="empty">0</template>
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Introduce a new key to handle 0 case. We need to say Row 0 of N, here default pluralization scheme fails; we can't say "Rows 0"

@@ -35,13 +35,7 @@ except according to the terms contained in the LICENSE file.
<i18n-t tag="div" keypath="rows" :plural="sizeOfCurrentPage"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this can be replaced with computed ref. We no longer need i18n-t as select element is removed.

type: Array,
required: true
},
empty: Boolean,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

let's compute empty using count, removed, sizeOfCurrentPage

type: Array,
required: true
},
empty: Boolean,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

also add a unit test

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Create corresponding spec for unit testing this component.

Comment on lines 262 to 268
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agree. Also add a test that message is correct if table is filtered and all rows are deleted.

Copy link
Copy Markdown
Member

@matthew-white matthew-white left a comment

Choose a reason for hiding this comment

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

I still need to review pagination.spec.js, but that's mostly it. It's looking great! I'm dropping a few comments now, but I'm planning to finish review tomorrow.

Comment on lines 297 to 299
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Similar to EntityList, how about adding this if back?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Not sure why this is being shown without any diff context. I'm talking about the second if removed from emptyMessage.

extendedEntities.delete = (index) => {
const entity = entities.get(index);
if (entity == null) throw new Error('entity not found');
entities.update(index, { deletedAt: new Date() });
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
entities.update(index, { deletedAt: new Date() });
entities.update(index, { deletedAt: new Date().toISOString() });

Since it's supposed to match the API response, I think it should be a string, not a Date.

<i18n-t tag="div" keypath="rows" :plural="sizeOfCurrentPage"
class="form-group">
<div class="form-group">{{ pageRange }}</div>
<!-- <i18n-t tag="div" keypath="rows" :plural="sizeOfCurrentPage"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see that this is commented out, but it'd be great to remove the <i18n-t> block entirely if possible.

Copy link
Copy Markdown
Member

@matthew-white matthew-white left a comment

Choose a reason for hiding this comment

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

Looks great! 💯 I love the new tests in pagination.spec.js.

-->
<template>
<div class="pagination">
<div v-if="count > 0" class="pagination">
Copy link
Copy Markdown
Member

@matthew-white matthew-white Feb 22, 2026

Choose a reason for hiding this comment

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

Within the template, is count kind of ambiguous? It's not clear to me whether it will be evaluated as the prop or as the computed ref. It might not be a bad idea to rename or alias the computed ref so that there's no ambiguity.

Comment on lines 188 to 191
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think these .form-control styles can be removed now that the <select> element has been removed.



this.bulkDeletedEntities = [...this.selectedEntities];
this.bulkDeletedEntities = [...this.selectedEntities].map(e => e.__id);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
this.bulkDeletedEntities = [...this.selectedEntities].map(e => e.__id);
this.bulkDeletedEntities = uuids;

I think uuids can be reused so we don't need to .map() again.


this.bulkDeletedEntities = [...this.selectedEntities];
this.bulkDeletedEntities = [...this.selectedEntities].map(e => e.__id);
this.odataEntities.value = this.odataEntities.value.filter(e => !this.selectedEntities.has(e));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need this line anymore? Since we're calling this.refresh(), I'm thinking that this.odataEntities will be cleared. For comparison, onSuccess in requestBulkRestore() no longer modifies this.odataEntities.value.

@sadiqkhoja sadiqkhoja merged commit 3ecfbc7 into getodk:master Feb 24, 2026
8 of 9 checks passed
sadiqkhoja added a commit to sadiqkhoja/central-frontend that referenced this pull request Mar 11, 2026
* Fixes getodk/central#1557: Remove snapshot filters from Entities and Submissions list

* Simplified pagination control

* removed page dropdown which was shown when number of pages were less than 10
* update count and range in the pagination control when a row is deleted
* refresh the data after bulk delete

* PR Feedbacks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace filtering based on client timestamps

2 participants