feat: Add assistant filter support to documents page (#10697)
This PR introduces support for an assistant filter on the documents page. - Moved the existing assistant filter functionality to a standalone, reusable component. - Updated the documents page and responses page to use the component
This commit is contained in:
@@ -6,11 +6,12 @@ class CaptainDocument extends ApiClient {
|
|||||||
super('captain/documents', { accountScoped: true });
|
super('captain/documents', { accountScoped: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
get({ page = 1, searchKey } = {}) {
|
get({ page = 1, searchKey, assistantId } = {}) {
|
||||||
return axios.get(this.url, {
|
return axios.get(this.url, {
|
||||||
params: {
|
params: {
|
||||||
page,
|
page,
|
||||||
searchKey,
|
searchKey,
|
||||||
|
assistant_id: assistantId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { useMapGetter } from 'dashboard/composables/store';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { OnClickOutside } from '@vueuse/components';
|
||||||
|
import Button from 'dashboard/components-next/button/Button.vue';
|
||||||
|
import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
assistantId: {
|
||||||
|
type: [String, Number],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['update']);
|
||||||
|
const { t } = useI18n();
|
||||||
|
const isFilterOpen = ref(false);
|
||||||
|
|
||||||
|
const assistants = useMapGetter('captainAssistants/getRecords');
|
||||||
|
const assistantOptions = computed(() => [
|
||||||
|
{
|
||||||
|
label: t(`CAPTAIN.RESPONSES.FILTER.ALL_ASSISTANTS`),
|
||||||
|
value: 'all',
|
||||||
|
action: 'filter',
|
||||||
|
},
|
||||||
|
...assistants.value.map(assistant => ({
|
||||||
|
value: assistant.id,
|
||||||
|
label: assistant.name,
|
||||||
|
action: 'filter',
|
||||||
|
})),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const selectedAssistantLabel = computed(() => {
|
||||||
|
const assistant = assistantOptions.value.find(
|
||||||
|
option => option.value === props.assistantId
|
||||||
|
);
|
||||||
|
return t('CAPTAIN.RESPONSES.FILTER.ASSISTANT', {
|
||||||
|
selected: assistant ? assistant.label : '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleAssistantFilterChange = ({ value }) => {
|
||||||
|
isFilterOpen.value = false;
|
||||||
|
emit('update', value);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<OnClickOutside @trigger="isFilterOpen = false">
|
||||||
|
<Button
|
||||||
|
:label="selectedAssistantLabel"
|
||||||
|
icon="i-lucide-chevron-down"
|
||||||
|
size="sm"
|
||||||
|
color="slate"
|
||||||
|
trailing-icon
|
||||||
|
class="max-w-48"
|
||||||
|
@click="isFilterOpen = !isFilterOpen"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DropdownMenu
|
||||||
|
v-if="isFilterOpen"
|
||||||
|
:menu-items="assistantOptions"
|
||||||
|
class="mt-2"
|
||||||
|
@action="handleAssistantFilterChange"
|
||||||
|
/>
|
||||||
|
</OnClickOutside>
|
||||||
|
</template>
|
||||||
@@ -7,7 +7,8 @@ import DocumentCard from 'dashboard/components-next/captain/assistant/DocumentCa
|
|||||||
import PageLayout from 'dashboard/components-next/captain/PageLayout.vue';
|
import PageLayout from 'dashboard/components-next/captain/PageLayout.vue';
|
||||||
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
|
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
|
||||||
import RelatedResponses from 'dashboard/components-next/captain/pageComponents/document/RelatedResponses.vue';
|
import RelatedResponses from 'dashboard/components-next/captain/pageComponents/document/RelatedResponses.vue';
|
||||||
import CreateDocumentDialog from '../../../../components-next/captain/pageComponents/document/CreateDocumentDialog.vue';
|
import CreateDocumentDialog from 'dashboard/components-next/captain/pageComponents/document/CreateDocumentDialog.vue';
|
||||||
|
import AssistantSelector from 'dashboard/components-next/captain/pageComponents/AssistantSelector.vue';
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
|
||||||
const uiFlags = useMapGetter('captainDocuments/getUIFlags');
|
const uiFlags = useMapGetter('captainDocuments/getUIFlags');
|
||||||
@@ -15,6 +16,7 @@ const documents = useMapGetter('captainDocuments/getRecords');
|
|||||||
const assistants = useMapGetter('captainAssistants/getRecords');
|
const assistants = useMapGetter('captainAssistants/getRecords');
|
||||||
const isFetching = computed(() => uiFlags.value.fetchingList);
|
const isFetching = computed(() => uiFlags.value.fetchingList);
|
||||||
const documentsMeta = useMapGetter('captainDocuments/getMeta');
|
const documentsMeta = useMapGetter('captainDocuments/getMeta');
|
||||||
|
const selectedAssistant = ref('all');
|
||||||
|
|
||||||
const selectedDocument = ref(null);
|
const selectedDocument = ref(null);
|
||||||
const deleteDocumentDialog = ref(null);
|
const deleteDocumentDialog = ref(null);
|
||||||
@@ -60,7 +62,17 @@ const handleAction = ({ action, id }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fetchDocuments = (page = 1) => {
|
const fetchDocuments = (page = 1) => {
|
||||||
store.dispatch('captainDocuments/get', { page });
|
const filterParams = { page };
|
||||||
|
|
||||||
|
if (selectedAssistant.value !== 'all') {
|
||||||
|
filterParams.assistantId = selectedAssistant.value;
|
||||||
|
}
|
||||||
|
store.dispatch('captainDocuments/get', filterParams);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAssistantFilterChange = assistant => {
|
||||||
|
selectedAssistant.value = assistant;
|
||||||
|
fetchDocuments();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPageChange = page => fetchDocuments(page);
|
const onPageChange = page => fetchDocuments(page);
|
||||||
@@ -83,6 +95,12 @@ onMounted(() => {
|
|||||||
@update:current-page="onPageChange"
|
@update:current-page="onPageChange"
|
||||||
@click="handleCreateDocument"
|
@click="handleCreateDocument"
|
||||||
>
|
>
|
||||||
|
<div v-if="!isFetching" class="mb-4 -mt-3 flex gap-3">
|
||||||
|
<AssistantSelector
|
||||||
|
:assistant-id="selectedAssistant"
|
||||||
|
@update="handleAssistantFilterChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="isFetching"
|
v-if="isFetching"
|
||||||
class="flex items-center justify-center py-10 text-n-slate-11"
|
class="flex items-center justify-center py-10 text-n-slate-11"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.v
|
|||||||
|
|
||||||
import DeleteDialog from 'dashboard/components-next/captain/pageComponents/DeleteDialog.vue';
|
import DeleteDialog from 'dashboard/components-next/captain/pageComponents/DeleteDialog.vue';
|
||||||
import PageLayout from 'dashboard/components-next/captain/PageLayout.vue';
|
import PageLayout from 'dashboard/components-next/captain/PageLayout.vue';
|
||||||
|
import AssistantSelector from 'dashboard/components-next/captain/pageComponents/AssistantSelector.vue';
|
||||||
import ResponseCard from 'dashboard/components-next/captain/assistant/ResponseCard.vue';
|
import ResponseCard from 'dashboard/components-next/captain/assistant/ResponseCard.vue';
|
||||||
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
|
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
|
||||||
import CreateResponseDialog from 'dashboard/components-next/captain/pageComponents/response/CreateResponseDialog.vue';
|
import CreateResponseDialog from 'dashboard/components-next/captain/pageComponents/response/CreateResponseDialog.vue';
|
||||||
@@ -30,7 +31,6 @@ const { t } = useI18n();
|
|||||||
const createDialog = ref(null);
|
const createDialog = ref(null);
|
||||||
|
|
||||||
const isStatusFilterOpen = ref(false);
|
const isStatusFilterOpen = ref(false);
|
||||||
const isAssistantFilterOpen = ref(false);
|
|
||||||
|
|
||||||
const statusOptions = computed(() =>
|
const statusOptions = computed(() =>
|
||||||
['all', 'pending', 'approved'].map(key => ({
|
['all', 'pending', 'approved'].map(key => ({
|
||||||
@@ -49,29 +49,6 @@ const selectedStatusLabel = computed(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const assistants = useMapGetter('captainAssistants/getRecords');
|
|
||||||
const assistantOptions = computed(() => [
|
|
||||||
{
|
|
||||||
label: t(`CAPTAIN.RESPONSES.FILTER.ALL_ASSISTANTS`),
|
|
||||||
value: 'all',
|
|
||||||
action: 'filter',
|
|
||||||
},
|
|
||||||
...assistants.value.map(assistant => ({
|
|
||||||
value: assistant.id,
|
|
||||||
label: assistant.name,
|
|
||||||
action: 'filter',
|
|
||||||
})),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const selectedAssistantLabel = computed(() => {
|
|
||||||
const assistant = assistantOptions.value.find(
|
|
||||||
option => option.value === selectedAssistant.value
|
|
||||||
);
|
|
||||||
return t('CAPTAIN.RESPONSES.FILTER.ASSISTANT', {
|
|
||||||
selected: assistant ? assistant.label : '',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
deleteDialog.value.dialogRef.open();
|
deleteDialog.value.dialogRef.open();
|
||||||
};
|
};
|
||||||
@@ -122,14 +99,14 @@ const handleCreateClose = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fetchResponses = (page = 1) => {
|
const fetchResponses = (page = 1) => {
|
||||||
const filterParams = {};
|
const filterParams = { page };
|
||||||
if (selectedStatus.value !== 'all') {
|
if (selectedStatus.value !== 'all') {
|
||||||
filterParams.status = selectedStatus.value;
|
filterParams.status = selectedStatus.value;
|
||||||
}
|
}
|
||||||
if (selectedAssistant.value !== 'all') {
|
if (selectedAssistant.value !== 'all') {
|
||||||
filterParams.assistantId = selectedAssistant.value;
|
filterParams.assistantId = selectedAssistant.value;
|
||||||
}
|
}
|
||||||
store.dispatch('captainResponses/get', { page, ...filterParams });
|
store.dispatch('captainResponses/get', page);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPageChange = page => fetchResponses(page);
|
const onPageChange = page => fetchResponses(page);
|
||||||
@@ -140,9 +117,8 @@ const handleStatusFilterChange = ({ value }) => {
|
|||||||
fetchResponses();
|
fetchResponses();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAssistantFilterChange = ({ value }) => {
|
const handleAssistantFilterChange = assistant => {
|
||||||
selectedAssistant.value = value;
|
selectedAssistant.value = assistant;
|
||||||
isAssistantFilterOpen.value = false;
|
|
||||||
fetchResponses();
|
fetchResponses();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -181,25 +157,10 @@ onMounted(() => {
|
|||||||
@action="handleStatusFilterChange"
|
@action="handleStatusFilterChange"
|
||||||
/>
|
/>
|
||||||
</OnClickOutside>
|
</OnClickOutside>
|
||||||
|
<AssistantSelector
|
||||||
<OnClickOutside @trigger="isAssistantFilterOpen = false">
|
:assistant-id="selectedAssistant"
|
||||||
<Button
|
@update="handleAssistantFilterChange"
|
||||||
:label="selectedAssistantLabel"
|
/>
|
||||||
icon="i-lucide-chevron-down"
|
|
||||||
size="sm"
|
|
||||||
color="slate"
|
|
||||||
trailing-icon
|
|
||||||
class="max-w-48"
|
|
||||||
@click="isAssistantFilterOpen = !isAssistantFilterOpen"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DropdownMenu
|
|
||||||
v-if="isAssistantFilterOpen"
|
|
||||||
:menu-items="assistantOptions"
|
|
||||||
class="mt-2"
|
|
||||||
@action="handleAssistantFilterChange"
|
|
||||||
/>
|
|
||||||
</OnClickOutside>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="isFetching"
|
v-if="isFetching"
|
||||||
|
|||||||
Reference in New Issue
Block a user