feat: Update Captain pages UI (#13184)

This commit is contained in:
Sivin Varghese
2026-01-07 00:44:07 +05:30
committed by GitHub
parent 68fc2e36aa
commit d6d7aba465
25 changed files with 398 additions and 316 deletions
@@ -212,7 +212,7 @@ const addAllExample = () => {
</template>
</SuggestedRules>
</div>
<div class="flex mt-7 flex-col gap-4">
<div class="flex mt-7 flex-col gap-4 pb-4">
<div class="flex justify-between items-center">
<BulkSelectBar
v-model="bulkSelectedIds"
@@ -221,7 +221,7 @@ const addAllExample = async () => {
</template>
</SuggestedRules>
</div>
<div class="flex mt-7 flex-col gap-4">
<div class="flex mt-7 flex-col gap-4 pb-4">
<div class="flex justify-between items-center">
<BulkSelectBar
v-model="bulkSelectedIds"
@@ -73,7 +73,7 @@ onMounted(() =>
</template>
<template #body>
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-4 pb-4">
<InboxCard
v-for="captainInbox in captainInboxes"
:id="captainInbox.id"
@@ -277,7 +277,7 @@ onMounted(() => {
{{ t('CAPTAIN.ASSISTANTS.SCENARIOS.SEARCH_EMPTY_MESSAGE') }}
</span>
</div>
<div v-else class="flex flex-col gap-2">
<div v-else class="flex flex-col gap-2 pb-4">
<ScenariosCard
v-for="scenario in filteredScenarios"
:id="scenario.id"
@@ -129,7 +129,7 @@ onMounted(() => {
<template #body>
<LimitBanner class="mb-5" />
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-4 pb-4">
<DocumentCard
v-for="doc in documents"
:id="doc.id"
@@ -7,6 +7,7 @@ import { FEATURE_FLAGS } from 'dashboard/featureFlags';
import { debounce } from '@chatwoot/utils';
import { useAccount } from 'dashboard/composables/useAccount';
import Button from 'dashboard/components-next/button/Button.vue';
import Banner from 'dashboard/components-next/banner/Banner.vue';
import Input from 'dashboard/components-next/input/Input.vue';
import BulkSelectBar from 'dashboard/components-next/captain/assistant/BulkSelectBar.vue';
@@ -113,7 +114,6 @@ const fetchResponses = (page = 1) => {
// Bulk action
const bulkSelectedIds = ref(new Set());
const hoveredCard = ref(null);
const buildSelectedCountLabel = computed(() => {
const count = responses.value?.length || 0;
@@ -129,10 +129,6 @@ const selectedCountLabel = computed(() => {
});
});
const handleCardHover = (isHovered, id) => {
hoveredCard.value = isHovered ? id : null;
};
const handleCardSelect = id => {
const selected = new Set(bulkSelectedIds.value);
selected[selected.has(id) ? 'delete' : 'add'](id);
@@ -157,6 +153,10 @@ const fetchResponseAfterBulkAction = () => {
bulkSelectedIds.value = new Set();
};
const clearSelection = () => {
bulkSelectedIds.value = new Set();
};
const onPageChange = page => {
const hadSelection = bulkSelectedIds.value.size > 0;
@@ -252,12 +252,23 @@ onMounted(() => {
:select-all-label="buildSelectedCountLabel"
:selected-count-label="selectedCountLabel"
:delete-label="$t('CAPTAIN.RESPONSES.BULK_DELETE_BUTTON')"
class="w-fit"
:class="{
'mb-2': bulkSelectedIds.size > 0,
'mt-2': bulkSelectedIds.size > 0,
}"
class="justify-between"
animation-direction="vertical"
@bulk-delete="bulkDeleteDialog.dialogRef.open()"
/>
>
<template #secondary-actions>
<Button
sm
ghost
:label="t('CAPTAIN.RESPONSES.CLEAR_SELECTION')"
class="!px-1"
@click="clearSelection"
/>
</template>
</BulkSelectBar>
</template>
<template #emptyState>
@@ -271,16 +282,16 @@ onMounted(() => {
<template #body>
<LimitBanner class="mb-5" />
<Banner
v-if="pendingCount > 0"
v-if="pendingCount > 0 && bulkSelectedIds.size === 0"
color="blue"
class="mb-4 -mt-3"
class="mb-4 -mt-2"
:action-label="$t('CAPTAIN.RESPONSES.PENDING_BANNER.ACTION')"
@action="navigateToPendingFAQs"
>
{{ $t('CAPTAIN.RESPONSES.PENDING_BANNER.TITLE') }}
</Banner>
<div class="flex flex-col gap-4">
<div class="flex flex-col divide-y divide-n-weak">
<ResponseCard
v-for="response in responses"
:id="response.id"
@@ -293,13 +304,11 @@ onMounted(() => {
:created-at="response.created_at"
:updated-at="response.updated_at"
:is-selected="bulkSelectedIds.has(response.id)"
:selectable="hoveredCard === response.id || bulkSelectedIds.size > 0"
:show-menu="!bulkSelectedIds.has(response.id)"
:show-actions="false"
@action="handleAction"
@navigate="handleNavigationAction"
@select="handleCardSelect"
@hover="isHovered => handleCardHover(isHovered, response.id)"
/>
</div>
</template>
@@ -140,7 +140,6 @@ const fetchResponses = (page = 1) => {
// Bulk action
const bulkSelectedIds = ref(new Set());
const hoveredCard = ref(null);
const buildSelectedCountLabel = computed(() => {
const count = filteredResponses.value?.length || 0;
@@ -156,10 +155,6 @@ const selectedCountLabel = computed(() => {
});
});
const handleCardHover = (isHovered, id) => {
hoveredCard.value = isHovered ? id : null;
};
const handleCardSelect = id => {
const selected = new Set(bulkSelectedIds.value);
selected[selected.has(id) ? 'delete' : 'add'](id);
@@ -196,6 +191,10 @@ const handleBulkApprove = async () => {
}
};
const clearSelection = () => {
bulkSelectedIds.value = new Set();
};
const onPageChange = page => {
const hadSelection = bulkSelectedIds.value.size > 0;
@@ -291,10 +290,11 @@ onMounted(() => {
:select-all-label="buildSelectedCountLabel"
:selected-count-label="selectedCountLabel"
:delete-label="$t('CAPTAIN.RESPONSES.BULK_DELETE_BUTTON')"
class="w-fit"
:class="{
'mb-2': bulkSelectedIds.size > 0,
}"
class="justify-between"
animation-direction="vertical"
@bulk-delete="bulkDeleteDialog.dialogRef.open()"
>
<template #secondary-actions>
@@ -306,6 +306,14 @@ onMounted(() => {
class="!px-1.5"
@click="handleBulkApprove"
/>
<div class="h-4 w-px bg-n-strong" />
<Button
sm
ghost
:label="$t('CAPTAIN.RESPONSES.CLEAR_SELECTION')"
class="!px-1"
@click="clearSelection"
/>
</template>
</BulkSelectBar>
</template>
@@ -325,7 +333,7 @@ onMounted(() => {
<template #body>
<LimitBanner class="mb-5" />
<div class="flex flex-col gap-4">
<div class="flex flex-col divide-y divide-n-weak">
<ResponseCard
v-for="response in filteredResponses"
:id="response.id"
@@ -338,13 +346,11 @@ onMounted(() => {
:created-at="response.created_at"
:updated-at="response.updated_at"
:is-selected="bulkSelectedIds.has(response.id)"
:selectable="hoveredCard === response.id || bulkSelectedIds.size > 0"
:show-menu="false"
:show-actions="!bulkSelectedIds.has(response.id)"
@action="handleAction"
@navigate="handleNavigationAction"
@select="handleCardSelect"
@hover="isHovered => handleCardHover(isHovered, response.id)"
/>
</div>
</template>
@@ -100,7 +100,7 @@ onMounted(() => {
</template>
<template #body>
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-4 pb-4">
<CustomToolCard
v-for="tool in customTools"
:id="tool.id"
@@ -88,7 +88,8 @@ const handleAssignLabels = labels => {
:all-items="allItems"
:select-all-label="selectAllLabel"
:selected-count-label="selectedCountLabel"
class="py-2 ltr:!pr-2 rtl:!pl-2 justify-between"
animation-direction="vertical"
class="justify-between"
>
<template #secondary-actions>
<Button
@@ -105,7 +106,7 @@ const handleAssignLabels = labels => {
type="contact"
:is-loading="isLoading"
:disabled="!selectedCount"
class="[&>button]:!text-n-blue-11 [&>button]:!px-2"
class="[&>button]:!text-n-blue-11 [&>button>span]:!text-n-blue-11 [&>button]:!px-2"
@assign="handleAssignLabels"
/>
<div class="w-px h-3 bg-n-weak rounded-lg" />
@@ -438,7 +438,7 @@ onMounted(async () => {
<template v-else>
<ContactsBulkActionBar
v-if="hasSelection"
v-show="hasSelection"
:visible-contact-ids="visibleContactIds"
:selected-contact-ids="selectedContactIds"
:is-loading="isBulkActionLoading"