diff --git a/app/javascript/dashboard/components-next/EmptyStateLayout.vue b/app/javascript/dashboard/components-next/EmptyStateLayout.vue
index f2744dde5..39eda2d85 100644
--- a/app/javascript/dashboard/components-next/EmptyStateLayout.vue
+++ b/app/javascript/dashboard/components-next/EmptyStateLayout.vue
@@ -14,6 +14,10 @@ defineProps({
type: Array,
default: () => [],
},
+ showBackdrop: {
+ type: Boolean,
+ default: true,
+ },
});
@@ -25,14 +29,24 @@ defineProps({
class="relative w-full max-w-[60rem] mx-auto overflow-hidden h-full max-h-[28rem]"
>
-
+
{{ subtitle }}
diff --git a/app/javascript/dashboard/components-next/captain/PageLayout.vue b/app/javascript/dashboard/components-next/captain/PageLayout.vue
index 495db1838..c394f1b6b 100644
--- a/app/javascript/dashboard/components-next/captain/PageLayout.vue
+++ b/app/javascript/dashboard/components-next/captain/PageLayout.vue
@@ -114,6 +114,7 @@ const handlePageChange = event => {
+
diff --git a/app/javascript/dashboard/components-next/captain/pageComponents/emptyStates/ResponsePageEmptyState.vue b/app/javascript/dashboard/components-next/captain/pageComponents/emptyStates/ResponsePageEmptyState.vue
index a1c91b2a3..d2ed12b0a 100644
--- a/app/javascript/dashboard/components-next/captain/pageComponents/emptyStates/ResponsePageEmptyState.vue
+++ b/app/javascript/dashboard/components-next/captain/pageComponents/emptyStates/ResponsePageEmptyState.vue
@@ -6,16 +6,39 @@ import ResponseCard from 'dashboard/components-next/captain/assistant/ResponseCa
import FeatureSpotlight from 'dashboard/components-next/feature-spotlight/FeatureSpotlight.vue';
import { responsesList } from 'dashboard/components-next/captain/pageComponents/emptyStates/captainEmptyStateContent.js';
-const emit = defineEmits(['click']);
+import { computed } from 'vue';
+
+const props = defineProps({
+ variant: {
+ type: String,
+ default: 'approved',
+ validator: value => ['approved', 'pending'].includes(value),
+ },
+ hasActiveFilters: {
+ type: Boolean,
+ default: false,
+ },
+});
+
+const emit = defineEmits(['click', 'clearFilters']);
+
+const isApproved = computed(() => props.variant === 'approved');
+const isPending = computed(() => props.variant === 'pending');
+
const { isOnChatwootCloud } = useAccount();
const onClick = () => {
emit('click');
};
+
+const onClearFilters = () => {
+ emit('clearFilters');
+};
{
class="mb-8"
/>
-
+
{
-
+
+
+
+
diff --git a/app/javascript/dashboard/i18n/locale/en/integrations.json b/app/javascript/dashboard/i18n/locale/en/integrations.json
index e0c25e39d..e465b40c1 100644
--- a/app/javascript/dashboard/i18n/locale/en/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/en/integrations.json
@@ -942,7 +942,9 @@
},
"EMPTY_STATE": {
"TITLE": "No FAQs Found",
+ "NO_PENDING_TITLE": "There are no more pending FAQs to review",
"SUBTITLE": "FAQs help your assistant provide quick and accurate answers to questions from your customers. They can be generated automatically from your content or can be added manually.",
+ "CLEAR_SEARCH": "Clear active filters",
"FEATURE_SPOTLIGHT": {
"TITLE": "Captain FAQ",
"NOTE": "Captain FAQs detects common customer questions—whether missing from your knowledge base or frequently asked—and generates relevant FAQs to improve support. You can review each suggestion and decide whether to approve or reject it."
diff --git a/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue b/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue
index 4ed117806..7a5d97f0b 100644
--- a/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue
+++ b/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue
@@ -2,7 +2,7 @@
import { computed, onMounted, ref, nextTick } from 'vue';
import { useMapGetter, useStore } from 'dashboard/composables/store';
import { useI18n } from 'vue-i18n';
-import { useRouter } from 'vue-router';
+import { useRouter, useRoute } from 'vue-router';
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
import { debounce } from '@chatwoot/utils';
import { useAccount } from 'dashboard/composables/useAccount';
@@ -23,6 +23,7 @@ import FeatureSpotlightPopover from 'dashboard/components-next/feature-spotlight
import LimitBanner from 'dashboard/components-next/captain/pageComponents/response/LimitBanner.vue';
const router = useRouter();
+const route = useRoute();
const store = useStore();
const { isOnChatwootCloud } = useAccount();
const uiFlags = useMapGetter('captainResponses/getUIFlags');
@@ -90,6 +91,18 @@ const handleCreateClose = () => {
selectedResponse.value = null;
};
+const updateURLWithFilters = (page, search) => {
+ const query = {
+ page: page || 1,
+ };
+
+ if (search) {
+ query.search = search;
+ }
+
+ router.replace({ query });
+};
+
const fetchResponses = (page = 1) => {
const filterParams = { page, status: 'approved' };
@@ -99,6 +112,10 @@ const fetchResponses = (page = 1) => {
if (searchQuery.value) {
filterParams.search = searchQuery.value;
}
+
+ // Update URL with current filters
+ updateURLWithFilters(page, searchQuery.value);
+
store.dispatch('captainResponses/get', filterParams);
};
@@ -186,20 +203,28 @@ const onBulkDeleteSuccess = () => {
const handleAssistantFilterChange = assistant => {
selectedAssistant.value = assistant;
- fetchResponses();
+ fetchResponses(1);
};
const debouncedSearch = debounce(async () => {
- fetchResponses();
+ fetchResponses(1);
}, 500);
+const initializeFromURL = () => {
+ if (route.query.search) {
+ searchQuery.value = route.query.search;
+ }
+ const pageFromURL = parseInt(route.query.page, 10) || 1;
+ fetchResponses(pageFromURL);
+};
+
const navigateToPendingFAQs = () => {
router.push({ name: 'captain_responses_pending' });
};
onMounted(() => {
store.dispatch('captainAssistants/get');
- fetchResponses();
+ initializeFromURL();
store.dispatch('captainResponses/fetchPendingCount');
});
@@ -230,18 +255,10 @@ onMounted(() => {
/>
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/captain/responses/Pending.vue b/app/javascript/dashboard/routes/dashboard/captain/responses/Pending.vue
index 21846ba94..236bb7b23 100644
--- a/app/javascript/dashboard/routes/dashboard/captain/responses/Pending.vue
+++ b/app/javascript/dashboard/routes/dashboard/captain/responses/Pending.vue
@@ -119,6 +119,18 @@ const handleCreateClose = () => {
selectedResponse.value = null;
};
+const updateURLWithFilters = (page, search) => {
+ const query = {
+ page: page || 1,
+ };
+
+ if (search) {
+ query.search = search;
+ }
+
+ router.replace({ query });
+};
+
const fetchResponses = (page = 1) => {
const filterParams = { page, status: 'pending' };
@@ -128,6 +140,10 @@ const fetchResponses = (page = 1) => {
if (searchQuery.value) {
filterParams.search = searchQuery.value;
}
+
+ // Update URL with current filters
+ updateURLWithFilters(page, searchQuery.value);
+
store.dispatch('captainResponses/get', filterParams);
};
@@ -225,16 +241,34 @@ const onBulkDeleteSuccess = () => {
const handleAssistantFilterChange = assistant => {
selectedAssistant.value = assistant;
- fetchResponses();
+ fetchResponses(1);
};
const debouncedSearch = debounce(async () => {
- fetchResponses();
+ fetchResponses(1);
}, 500);
+const hasActiveFilters = computed(() => {
+ return Boolean(searchQuery.value || selectedAssistant.value !== 'all');
+});
+
+const clearFilters = () => {
+ searchQuery.value = '';
+ selectedAssistant.value = 'all';
+ fetchResponses(1);
+};
+
+const initializeFromURL = () => {
+ if (route.query.search) {
+ searchQuery.value = route.query.search;
+ }
+ const pageFromURL = parseInt(route.query.page, 10) || 1;
+ fetchResponses(pageFromURL);
+};
+
onMounted(() => {
store.dispatch('captainAssistants/get');
- fetchResponses();
+ initializeFromURL();
});
@@ -265,18 +299,10 @@ onMounted(() => {
/>
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+