feat(captain): toggle assistant audience between everyone and specific

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pranav
2026-06-30 17:58:07 -07:00
co-authored by Claude Opus 4.8
parent 9b54bc5096
commit 38fccdd04b
2 changed files with 48 additions and 14 deletions
@@ -2,6 +2,7 @@
import { ref, watch, useTemplateRef } from 'vue';
import { useI18n } from 'vue-i18n';
import Button from 'dashboard/components-next/button/Button.vue';
import RadioCard from 'dashboard/components-next/radioCard/RadioCard.vue';
import AudienceGroup from './audience/AudienceGroup.vue';
import { useAudienceFilterTypes } from './audience/useAudienceFilterTypes.js';
@@ -29,6 +30,7 @@ const hasConditions = node =>
const defaultRoot = () => ({ id: nextId(), operator: 'and', conditions: [] });
const root = ref(defaultRoot());
const mode = ref('everyone');
const findOption = (filterType, value) =>
filterType?.options?.find(option => String(option.id) === String(value));
@@ -106,11 +108,13 @@ const serializeNode = node => {
const groupRef = useTemplateRef('groupRef');
const handleSubmit = () => {
if (!groupRef.value.validate()) return;
const isSpecific = mode.value === 'specific';
if (isSpecific && !groupRef.value.validate()) return;
const audience = root.value.conditions.length
? serializeNode(root.value)
: null;
const audience =
isSpecific && root.value.conditions.length
? serializeNode(root.value)
: null;
emit('submit', {
config: {
@@ -123,20 +127,42 @@ const handleSubmit = () => {
watch(
() => props.assistant,
newAssistant => {
if (newAssistant) root.value = hydrateRoot(newAssistant.config?.audience);
if (!newAssistant) return;
const audience = newAssistant.config?.audience;
root.value = hydrateRoot(audience);
mode.value = audience ? 'specific' : 'everyone';
},
{ immediate: true }
);
</script>
<template>
<div class="flex flex-col gap-6">
<AudienceGroup
ref="groupRef"
v-model="root"
is-root
:filter-types="filterTypes"
/>
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-3">
<RadioCard
id="everyone"
:label="t('CAPTAIN.ASSISTANTS.FORM.AUDIENCE.EVERYONE.LABEL')"
:description="t('CAPTAIN.ASSISTANTS.FORM.AUDIENCE.EVERYONE.DESC')"
:is-active="mode === 'everyone'"
@select="mode = 'everyone'"
/>
<RadioCard
id="specific"
:label="t('CAPTAIN.ASSISTANTS.FORM.AUDIENCE.SPECIFIC.LABEL')"
:description="t('CAPTAIN.ASSISTANTS.FORM.AUDIENCE.SPECIFIC.DESC')"
:is-active="mode === 'specific'"
@select="mode = 'specific'"
>
<AudienceGroup
v-if="mode === 'specific'"
ref="groupRef"
v-model="root"
is-root
class="w-full mt-2"
:filter-types="filterTypes"
/>
</RadioCard>
</div>
<div>
<Button
:label="t('CAPTAIN.ASSISTANTS.FORM.UPDATE')"
@@ -528,10 +528,18 @@
"ALLOW_CONTACT_ATTRIBUTES": "Allow access to contact information"
},
"AUDIENCE": {
"EVERYONE": {
"LABEL": "Everyone",
"DESC": "Captain responds to every conversation in connected inboxes."
},
"SPECIFIC": {
"LABEL": "Specific audience",
"DESC": "Captain responds only to customers who match the conditions below."
},
"ADD_CONDITION": "Add condition",
"ADD_GROUP": "Add condition group",
"EMPTY_TITLE": "No conditions yet",
"EMPTY_BODY": "By default, Captain responds to every conversation in connected inboxes. Add a condition to target specific customers, or a condition group to combine conditions with different and/or logic.",
"EMPTY_BODY": "Add a condition to target specific customers, or a condition group to combine conditions with different and/or logic.",
"MATCH_PREFIX": "Match",
"MATCH_SUFFIX": "of the following",
"MATCH_ALL": "all",
@@ -579,7 +587,7 @@
},
"AUDIENCE": {
"TITLE": "Audience",
"DESCRIPTION": "Limit which customers this assistant responds to. Leave empty to respond to everyone."
"DESCRIPTION": "Choose whether this assistant responds to everyone or only to a specific audience."
},
"SCHEDULE": {
"TITLE": "Schedule",