-
Notifications
You must be signed in to change notification settings - Fork 62
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
View deleted Submissions and delete/undelete Submissions. #1043
Conversation
7809977
to
a6f86ac
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still reviewing things, but I wanted to share the comments that I have so far. There are a few of these comments, but they're mostly small things. Overall things are looking great so far.
6460d16
to
8a8f747
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More small comments! Mostly I just need to look at tests now.
8a8f747
to
27871b0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! ✨
props: { state: true, checkbox: true } | ||
})); | ||
|
||
describe('SubmissionRetore', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
describe('SubmissionRetore', () => { | |
describe('SubmissionRestore', () => { |
describe('deleted', () => { | ||
it('shows the deleted date', () => { | ||
const { deletedAt } = testData.extendedSubmissions.createPast(1, { deletedAt: new Date().toISOString() }).last(); | ||
mountComponent({ deleted: true }).findAllComponents(DateTime)[1].props().iso.should.equal(deletedAt); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you're going to use findAllComponents()
, could you make an assertion about the length
of the resulting array? If we add another <date-time>
at some point, then [1]
won't be the last <date-time>
. Alternatively, I think you could do something like:
mountComponent({ deleted: true }).findAllComponents(DateTime)[1].props().iso.should.equal(deletedAt); | |
mountComponent({ deleted: true }).get('.state-and-actions').getComponent(DateTime).props().iso.should.equal(deletedAt); |
|
||
it('does not show the delete button if user does not have submission delete permission', async () => { | ||
mockLogin({ role: 'none' }); | ||
testData.extendedSubmissions.createPast(1, { role: 'viewer' }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think testData.extendedSubmissions
will use { role: 'viewer' }
. It needs to be passed to testData.extendedProjects
:
testData.extendedProjects.createPast(1, { role: 'viewer' });
testData.extendedSubmissions.createPast(1);
|
||
it('does not show the restore button if user does not have submission restore permission', async () => { | ||
mockLogin({ role: 'none' }); | ||
testData.extendedSubmissions.createPast(1, { deletedAt: new Date().toISOString(), role: 'viewer' }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the comment above, { role: 'viewer' }
needs to be passed to testData.extendedProjects.createPast()
.
src/components/submission/list.vue
Outdated
.finally(() => { this.refreshing = false; }) | ||
.catch(noop); | ||
|
||
// emit event to parent component to re-fetch deleted Submissions count | ||
if (refresh && !this.deleted) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (refresh && !this.deleted) { | |
if (refresh && !this.deleted && !this.draft) { |
}); | ||
}); | ||
|
||
describe('deleting after checking the checkbox', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
describe('deleting after checking the checkbox', () => { | |
describe('undeleting after checking the checkbox', () => { |
src/components/submission/list.vue
Outdated
}) | ||
.then(() => { | ||
this.deleteModal.hide(); | ||
this.deletedSubmissionCount.value += 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the request for deletedSubmissionCount
failed, then there isn't a count to increment:
this.deletedSubmissionCount.value += 1; | |
if (this.deletedSubmissionCount.dataExists) this.deletedSubmissionCount.value += 1; |
src/components/submission/list.vue
Outdated
@@ -29,15 +30,24 @@ except according to the terms contained in the LICENSE file. | |||
</button> | |||
</form> | |||
<submission-download-button :form-version="formVersion" | |||
:aria-disabled="deleted" v-tooltip.aria-describedby="deleted ? $t('downloadDisabled') : null" | |||
:filtered="odataFilter != null" @download="downloadModal.show()"/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:filtered="odataFilter != null" @download="downloadModal.show()"/> | |
:filtered="odataFilter != null && !deleted" @download="downloadModal.show()"/> |
After the user clicks .toggle-deleted-submissions
, the download button should show the number of regular/non-deleted submissions, not the number of deleted submissions. Without this change, I think it will show the number of deleted submissions, since that's what odata.count
will be.
After the user clicks .toggle-deleted-submissions
, I think the download button also shouldn't say anything about "matching Submissions".
src/components/form/submissions.vue
Outdated
<template v-if="deletedSubmissionCount.dataExists"> | ||
<button v-if="canUpdate && (deletedSubmissionCount.value > 0 || deleted)" type="button" | ||
class="btn toggle-deleted-submissions" :class="{ 'btn-danger': deleted, 'btn-link': !deleted }" | ||
@click="deleted = !deleted"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I filter the submissions, then I click the "# deleted Submissions" button, what happens to the filters? They remain in place, right? So if I click the "# deleted Submissions" button again, the filters will start applying again? I feel like there's some potential for confusion in that interaction. Even though the user can't change the filters while viewing deleted submissions, I think they might still wonder whether the filters are being applied to what they're seeing. Maybe it'd be better to reset the filters whenever the "# deleted Submissions" button is clicked. Instead of setting deleted
on this line, @click
could call a method like this:
toggleDeleted() {
const { path } = this.$route;
this.$router.push(this.deleted ? path : `${path}?deleted=true`);
}
By changing the route like this, all the filters will be reset: fromQuery
will be called for each of them.
We could also wait to make this change and see what issa and the QA team think.
@@ -30,6 +30,11 @@ except according to the terms contained in the LICENSE file. | |||
<span class="icon-pencil"></span>{{ $t('action.edit') }} | |||
</button> | |||
</template> | |||
<button v-if="project.dataExists && project.permits('submission.delete')" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about adding a test that a project viewer cannot see this button?
27871b0
to
a028868
Compare
a028868
to
0b74e3c
Compare
closes getodk/central#709
What has been done to verify that this works as intended?
Wrote bunch of tests, also manually run different scenarios.
Why is this the best possible solution? Were any other approaches considered?
Showing deleted Submissions: I have used existing
odata
request data resource instead of creating a separate resource because that would have required lot of duplicate functionality and re/writing of associated tests.deletedSubmissionCount: resource is created in the
submissions.js
module to keep related stuff together, because of this I had to calluseSubmissions()
in the parent components of SubmissionList i.e. FormSubmissions and FormDraftTesting. Another approach is to create it in a separate module which wouldn't require moving ofuseSubmissions()
to parent component.deleted
prop: is added to multiple components, which seems more suitable in this case than the provide/inject approach because this prop is used by every intermediate component rather than being passed down transparently..Delete/Undeleted: I have followed the same pattern of deleting entity.
Spinner on delete/undelete button: This prevents double-clicking of the button, which would cause two requests to the backend—one of them would fail and error message would be shown. The awaiting response state is maintained in a
Set
, which causes prop re-evaluation for every row in the table, but DOM is not repainted for all of them, so it should not significantly impact performance. Additionally, we are soon going to implement pagination that will mitigate any performance hit if any.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?
It's a big change but it only affects the list of Submissions. So there shouldn't be any side effects in other parts of the application.
Does this change require updates to user documentation? If so, please file an issue here and include the link below.
getodk/docs#1873
Before submitting this PR, please make sure you have:
npm run test
andnpm run lint
and confirmed all checks still pass OR confirm CircleCI build passes