fix: Approved FAQ not disappearing from pending list after filtering (#11909)

# Pull Request Template

## Description

This PR fixes an issue where approved FAQs were not removed from the
list when filtered by `pending` status.

**Fix:** Implemented real-time client-side filtering using a
`filteredResponses` computed property in `Index.vue`, updated all list
and selection logic to use it, and also hide the card toggle dropdown
button when the bulk action checkbox is selected.


## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

### Loom video

https://www.loom.com/share/216221a4910c44ebb1d49ab9e34c820c?sid=98c9c239-54eb-4bd9-b04d-aeccc55bfb3a

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
Sivin Varghese
2025-07-12 10:06:30 +05:30
committed by GitHub
parent 5b9f997fa0
commit 1b02ebec68
2 changed files with 42 additions and 22 deletions

View File

@@ -55,6 +55,10 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
showMenu: {
type: Boolean,
default: true,
},
}); });
const emit = defineEmits(['action', 'navigate', 'select', 'hover']); const emit = defineEmits(['action', 'navigate', 'select', 'hover']);
@@ -130,7 +134,7 @@ const handleDocumentableClick = () => {
<span class="text-base text-n-slate-12 line-clamp-1"> <span class="text-base text-n-slate-12 line-clamp-1">
{{ question }} {{ question }}
</span> </span>
<div v-if="!compact" class="flex items-center gap-2"> <div v-if="!compact && showMenu" class="flex items-center gap-2">
<Policy <Policy
v-on-clickaway="() => toggleDropdown(false)" v-on-clickaway="() => toggleDropdown(false)"
:permissions="['administrator']" :permissions="['administrator']"

View File

@@ -55,6 +55,12 @@ const statusOptions = computed(() =>
})) }))
); );
const filteredResponses = computed(() => {
return selectedStatus.value === 'pending'
? responses.value.filter(r => r.status === 'pending')
: responses.value;
});
const selectedStatusLabel = computed(() => { const selectedStatusLabel = computed(() => {
const status = statusOptions.value.find( const status = statusOptions.value.find(
option => option.value === selectedStatus.value option => option.value === selectedStatus.value
@@ -94,7 +100,9 @@ const handleEdit = () => {
}; };
const handleAction = ({ action, id }) => { const handleAction = ({ action, id }) => {
selectedResponse.value = responses.value.find(response => id === response.id); selectedResponse.value = filteredResponses.value.find(
response => id === response.id
);
nextTick(() => { nextTick(() => {
if (action === 'delete') { if (action === 'delete') {
handleDelete(); handleDelete();
@@ -139,7 +147,7 @@ const hoveredCard = ref(null);
const bulkSelectionState = computed(() => { const bulkSelectionState = computed(() => {
const selectedCount = bulkSelectedIds.value.size; const selectedCount = bulkSelectedIds.value.size;
const totalCount = responses.value?.length || 0; const totalCount = filteredResponses.value?.length || 0;
return { return {
hasSelected: selectedCount > 0, hasSelected: selectedCount > 0,
@@ -152,13 +160,13 @@ const bulkCheckbox = computed({
get: () => bulkSelectionState.value.allSelected, get: () => bulkSelectionState.value.allSelected,
set: value => { set: value => {
bulkSelectedIds.value = value bulkSelectedIds.value = value
? new Set(responses.value.map(r => r.id)) ? new Set(filteredResponses.value.map(r => r.id))
: new Set(); : new Set();
}, },
}); });
const buildSelectedCountLabel = computed(() => { const buildSelectedCountLabel = computed(() => {
const count = responses.value?.length || 0; const count = filteredResponses.value?.length || 0;
return bulkSelectionState.value.allSelected return bulkSelectionState.value.allSelected
? t('CAPTAIN.RESPONSES.UNSELECT_ALL', { count }) ? t('CAPTAIN.RESPONSES.UNSELECT_ALL', { count })
: t('CAPTAIN.RESPONSES.SELECT_ALL', { count }); : t('CAPTAIN.RESPONSES.SELECT_ALL', { count });
@@ -174,6 +182,24 @@ const handleCardSelect = id => {
bulkSelectedIds.value = selected; bulkSelectedIds.value = selected;
}; };
const fetchResponseAfterBulkAction = () => {
const hasNoResponsesLeft = filteredResponses.value?.length === 0;
const currentPage = responseMeta.value?.page;
if (hasNoResponsesLeft) {
// Page is now empty after bulk action.
// Fetch the previous page if not already on the first page.
const pageToFetch = currentPage > 1 ? currentPage - 1 : currentPage;
fetchResponses(pageToFetch);
} else {
// Page still has responses left, re-fetch the same page.
fetchResponses(currentPage);
}
// Clear selection
bulkSelectedIds.value = new Set();
};
const handleBulkApprove = async () => { const handleBulkApprove = async () => {
try { try {
await store.dispatch( await store.dispatch(
@@ -181,8 +207,7 @@ const handleBulkApprove = async () => {
Array.from(bulkSelectedIds.value) Array.from(bulkSelectedIds.value)
); );
// Clear selection fetchResponseAfterBulkAction();
bulkSelectedIds.value = new Set();
useAlert(t('CAPTAIN.RESPONSES.BULK_APPROVE.SUCCESS_MESSAGE')); useAlert(t('CAPTAIN.RESPONSES.BULK_APPROVE.SUCCESS_MESSAGE'));
} catch (error) { } catch (error) {
useAlert( useAlert(
@@ -205,23 +230,13 @@ const onPageChange = page => {
}; };
const onDeleteSuccess = () => { const onDeleteSuccess = () => {
if (responses.value?.length === 0 && responseMeta.value?.page > 1) { if (filteredResponses.value?.length === 0 && responseMeta.value?.page > 1) {
onPageChange(responseMeta.value.page - 1); onPageChange(responseMeta.value.page - 1);
} }
}; };
const onBulkDeleteSuccess = () => { const onBulkDeleteSuccess = () => {
// Only fetch if no records left fetchResponseAfterBulkAction();
if (responses.value?.length === 0) {
const page =
responseMeta.value?.page > 1
? responseMeta.value.page - 1
: responseMeta.value.page;
fetchResponses(page);
}
// Clear selection
bulkSelectedIds.value = new Set();
}; };
const handleStatusFilterChange = ({ value }) => { const handleStatusFilterChange = ({ value }) => {
@@ -249,8 +264,8 @@ onMounted(() => {
:header-title="$t('CAPTAIN.RESPONSES.HEADER')" :header-title="$t('CAPTAIN.RESPONSES.HEADER')"
:button-label="$t('CAPTAIN.RESPONSES.ADD_NEW')" :button-label="$t('CAPTAIN.RESPONSES.ADD_NEW')"
:is-fetching="isFetching" :is-fetching="isFetching"
:is-empty="!responses.length" :is-empty="!filteredResponses.length"
:show-pagination-footer="!isFetching && !!responses.length" :show-pagination-footer="!isFetching && !!filteredResponses.length"
:feature-flag="FEATURE_FLAGS.CAPTAIN" :feature-flag="FEATURE_FLAGS.CAPTAIN"
@update:current-page="onPageChange" @update:current-page="onPageChange"
@click="handleCreate" @click="handleCreate"
@@ -368,7 +383,7 @@ onMounted(() => {
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<ResponseCard <ResponseCard
v-for="response in responses" v-for="response in filteredResponses"
:id="response.id" :id="response.id"
:key="response.id" :key="response.id"
:question="response.question" :question="response.question"
@@ -380,6 +395,7 @@ onMounted(() => {
:updated-at="response.updated_at" :updated-at="response.updated_at"
:is-selected="bulkSelectedIds.has(response.id)" :is-selected="bulkSelectedIds.has(response.id)"
:selectable="hoveredCard === response.id || bulkSelectedIds.size > 0" :selectable="hoveredCard === response.id || bulkSelectedIds.size > 0"
:show-menu="!bulkSelectedIds.has(response.id)"
@action="handleAction" @action="handleAction"
@navigate="handleNavigationAction" @navigate="handleNavigationAction"
@select="handleCardSelect" @select="handleCardSelect"