@meistrari/chat-nuxt 3.9.2 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -440
- package/dist/module.d.mts +1 -1
- package/dist/module.json +1 -1
- package/dist/runtime/assets/css/markstream.css +1 -1
- package/dist/runtime/conversations/components/chat-conversation-creator-filter.vue +11 -67
- package/dist/runtime/conversations/components/chat-conversation-item.d.vue.ts +2 -1
- package/dist/runtime/conversations/components/chat-conversation-item.vue +29 -33
- package/dist/runtime/conversations/components/chat-conversation-item.vue.d.ts +2 -1
- package/dist/runtime/conversations/components/chat-conversation-list.d.vue.ts +0 -1
- package/dist/runtime/conversations/components/chat-conversation-list.vue +117 -158
- package/dist/runtime/conversations/components/chat-conversation-list.vue.d.ts +0 -1
- package/dist/runtime/conversations/components/chat-conversation-new-group-button.vue +34 -73
- package/dist/runtime/conversations/components/chat-conversation-search.vue +1 -21
- package/dist/runtime/conversations/components/chat-widget-history-list.d.vue.ts +12 -0
- package/dist/runtime/conversations/components/chat-widget-history-list.vue +27 -0
- package/dist/runtime/conversations/components/chat-widget-history-list.vue.d.ts +12 -0
- package/dist/runtime/conversations/composables/conversation-groups.d.ts +1 -0
- package/dist/runtime/conversations/composables/conversation-groups.js +2 -0
- package/dist/runtime/conversations/composables/conversation-item.js +4 -4
- package/dist/runtime/conversations/composables/conversation-list.js +14 -5
- package/dist/runtime/embed/components/chat-configuration-modal.d.vue.ts +3 -3
- package/dist/runtime/embed/components/chat-configuration-modal.vue.d.ts +3 -3
- package/dist/runtime/embed/components/chat-embed-inner.d.vue.ts +6 -3
- package/dist/runtime/embed/components/chat-embed-inner.vue +442 -174
- package/dist/runtime/embed/components/chat-embed-inner.vue.d.ts +6 -3
- package/dist/runtime/embed/components/chat-embed.d.vue.ts +31 -4
- package/dist/runtime/embed/components/chat-embed.vue +10 -4
- package/dist/runtime/embed/components/chat-embed.vue.d.ts +31 -4
- package/dist/runtime/embed/components/chat-topbar.vue +83 -85
- package/dist/runtime/embed/components/meistrari-chat-embed.d.vue.ts +5 -3
- package/dist/runtime/embed/components/meistrari-chat-embed.vue +118 -57
- package/dist/runtime/embed/components/meistrari-chat-embed.vue.d.ts +5 -3
- package/dist/runtime/embed/components/mobile/chat-mobile-drawer.d.vue.ts +0 -1
- package/dist/runtime/embed/components/mobile/chat-mobile-drawer.vue +2 -45
- package/dist/runtime/embed/components/mobile/chat-mobile-drawer.vue.d.ts +0 -1
- package/dist/runtime/embed/components/mobile/chat-mobile-home-suggestions.d.vue.ts +9 -5
- package/dist/runtime/embed/components/mobile/chat-mobile-home-suggestions.vue +14 -25
- package/dist/runtime/embed/components/mobile/chat-mobile-home-suggestions.vue.d.ts +9 -5
- package/dist/runtime/embed/components/mobile/chat-mobile-shell.d.vue.ts +4 -4
- package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue +2 -4
- package/dist/runtime/embed/components/mobile/chat-mobile-shell.vue.d.ts +4 -4
- package/dist/runtime/embed/composables/use-agent-wake.d.ts +23 -0
- package/dist/runtime/embed/composables/use-agent-wake.js +182 -0
- package/dist/runtime/embed/feature-config.d.ts +7 -0
- package/dist/runtime/embed/feature-config.js +2 -1
- package/dist/runtime/embed/loading-messages.d.ts +16 -0
- package/dist/runtime/embed/loading-messages.js +10 -0
- package/dist/runtime/embed/types.d.ts +47 -8
- package/dist/runtime/files/icons.d.ts +1 -1
- package/dist/runtime/files/icons.js +1 -1
- package/dist/runtime/messages/components/chat-mention-dropdown.d.vue.ts +6 -1
- package/dist/runtime/messages/components/chat-mention-dropdown.vue +46 -22
- package/dist/runtime/messages/components/chat-mention-dropdown.vue.d.ts +6 -1
- package/dist/runtime/messages/components/chat-message-bubble.d.vue.ts +10 -1
- package/dist/runtime/messages/components/chat-message-bubble.vue +69 -76
- package/dist/runtime/messages/components/chat-message-bubble.vue.d.ts +10 -1
- package/dist/runtime/messages/components/chat-message-feedback.vue +25 -229
- package/dist/runtime/messages/components/chat-message-input.vue +176 -134
- package/dist/runtime/messages/components/chat-message-list.d.vue.ts +12 -1
- package/dist/runtime/messages/components/chat-message-list.vue +37 -24
- package/dist/runtime/messages/components/chat-message-list.vue.d.ts +12 -1
- package/dist/runtime/messages/components/chat-model-selector.vue +10 -22
- package/dist/runtime/messages/components/reasoning/chat-reasoning-message.vue +2 -3
- package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.d.vue.ts +2 -1
- package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.vue +69 -77
- package/dist/runtime/messages/components/reasoning/chat-reasoning-thought.vue.d.ts +2 -1
- package/dist/runtime/messages/components/reasoning/chat-thinking-indicator.vue +3 -9
- package/dist/runtime/messages/components/widgets/chat-bash-widget.vue +3 -12
- package/dist/runtime/messages/components/widgets/chat-doc-sdk-widget.vue +3 -12
- package/dist/runtime/messages/components/widgets/chat-generic-tool-widget.vue +3 -12
- package/dist/runtime/messages/components/widgets/chat-grep-widget.vue +7 -17
- package/dist/runtime/messages/components/widgets/chat-read-widget.vue +7 -17
- package/dist/runtime/messages/components/widgets/chat-tela-skill-widget.vue +4 -13
- package/dist/runtime/messages/components/widgets/chat-thinking-widget.vue +11 -22
- package/dist/runtime/messages/components/widgets/chat-web-search-widget.vue +13 -40
- package/dist/runtime/messages/components/widgets/chat-workstation-query-widget.vue +52 -65
- package/dist/runtime/messages/components/widgets/chat-write-widget.vue +8 -18
- package/dist/runtime/messages/composables/chat.d.ts +8 -2
- package/dist/runtime/messages/composables/chat.js +180 -25
- package/dist/runtime/messages/composables/mention-autocomplete.js +4 -0
- package/dist/runtime/messages/composables/use-direct-stream.d.ts +41 -0
- package/dist/runtime/messages/composables/use-direct-stream.js +263 -0
- package/dist/runtime/messages/message-state.js +1 -0
- package/dist/runtime/messages/reasoning.d.ts +7 -0
- package/dist/runtime/messages/reasoning.js +5 -0
- package/dist/runtime/public/files/generic.svg +2 -5
- package/dist/runtime/public/files/image.svg +2 -1
- package/dist/runtime/types/embed.d.ts +1 -1
- package/dist/runtime/types/schemas/chat/conversations.d.ts +4 -0
- package/dist/runtime/types/schemas/chat/conversations.js +3 -0
- package/dist/runtime/types/schemas/chat/messages.d.ts +43 -0
- package/dist/runtime/types/schemas/chat/messages.js +31 -0
- package/dist/runtime/types/schemas/chat/models.d.ts +3 -2
- package/dist/runtime/types/schemas/chat/models.js +2 -1
- package/dist/runtime/types/schemas/chat/ui.d.ts +10 -0
- package/dist/runtime/types/schemas/chat/ui.js +1 -0
- package/dist/runtime/types/schemas/integrations/tela-agent-api.d.ts +35 -0
- package/dist/runtime/types/schemas/integrations/tela-agent-api.js +17 -0
- package/dist/types.d.mts +1 -1
- package/package.json +5 -4
- package/dist/runtime/conversations/components/chat-conversation-new-group-popover-content.d.vue.ts +0 -23
- package/dist/runtime/conversations/components/chat-conversation-new-group-popover-content.vue +0 -75
- package/dist/runtime/conversations/components/chat-conversation-new-group-popover-content.vue.d.ts +0 -23
- package/dist/runtime/messages/components/reasoning/chat-reasoning-step-item.d.vue.ts +0 -37
- package/dist/runtime/messages/components/reasoning/chat-reasoning-step-item.vue +0 -125
- package/dist/runtime/messages/components/reasoning/chat-reasoning-step-item.vue.d.ts +0 -37
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
import { useChatAuth } from "#chat-auth";
|
|
3
3
|
import { useEmbedConfig } from "../../embed/composables/embed-config";
|
|
4
4
|
import { useWorkspaceMembers } from "../../workspace/composables/workspace-members";
|
|
5
|
+
import { isPromptTooLongFailure } from "../../types/schemas/chat/messages.js";
|
|
5
6
|
import { parseContentWithFiles } from "../../files/content-detection";
|
|
6
7
|
import ChatVaultLink from "../../files/components/chat-vault-link.vue";
|
|
7
8
|
const props = defineProps({
|
|
8
|
-
message: { type: null, required: true }
|
|
9
|
+
message: { type: null, required: true },
|
|
10
|
+
streamActive: { type: Boolean, required: false },
|
|
11
|
+
preparingSteps: { type: [Array, null], required: false },
|
|
12
|
+
showTtft: { type: Boolean, required: false, default: true }
|
|
9
13
|
});
|
|
10
14
|
const emit = defineEmits(["retry"]);
|
|
11
15
|
const { user } = useChatAuth();
|
|
@@ -39,6 +43,7 @@ const isOwnMessage = computed(() => {
|
|
|
39
43
|
});
|
|
40
44
|
const isPending = computed(() => props.message.status === "pending");
|
|
41
45
|
const isFailed = computed(() => props.message.status === "failed");
|
|
46
|
+
const isPromptTooLong = computed(() => isFailed.value && isPromptTooLongFailure(props.message.failReason));
|
|
42
47
|
const hasReasoning = computed(() => {
|
|
43
48
|
const data = props.message.reasoningData;
|
|
44
49
|
return props.message.role === "assistant" && data && Array.isArray(data) && data.length > 0;
|
|
@@ -104,9 +109,9 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
104
109
|
</script>
|
|
105
110
|
|
|
106
111
|
<template>
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
:
|
|
112
|
+
<TelaChatMessage
|
|
113
|
+
:from="message.role === 'user' ? 'user' : 'assistant'"
|
|
114
|
+
:align="isOwnMessage ? 'end' : 'start'"
|
|
110
115
|
>
|
|
111
116
|
<template v-if="message.role === 'user'">
|
|
112
117
|
<div
|
|
@@ -121,46 +126,20 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
121
126
|
items-center
|
|
122
127
|
gap-4px
|
|
123
128
|
>
|
|
124
|
-
<
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
h-full w-full object-cover
|
|
130
|
-
>
|
|
131
|
-
<div
|
|
132
|
-
v-else
|
|
133
|
-
h-full w-full
|
|
134
|
-
flex
|
|
135
|
-
items-center
|
|
136
|
-
justify-center
|
|
137
|
-
bg-neutral-200
|
|
138
|
-
>
|
|
139
|
-
<span text-8px text-neutral-500 font-medium>
|
|
140
|
-
{{ senderName.charAt(0).toUpperCase() }}
|
|
141
|
-
</span>
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
129
|
+
<TelaChatMessageAvatar
|
|
130
|
+
:src="senderImage ?? void 0"
|
|
131
|
+
:name="senderName"
|
|
132
|
+
size="2xs"
|
|
133
|
+
/>
|
|
144
134
|
<span text-12px leading-16px font-semibold text-primary>
|
|
145
135
|
{{ senderName }}
|
|
146
136
|
</span>
|
|
147
137
|
</div>
|
|
148
138
|
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
pt-16px
|
|
154
|
-
pb-20px
|
|
155
|
-
bg-subtle
|
|
156
|
-
text-primary
|
|
157
|
-
border-0.5px
|
|
158
|
-
border-solid
|
|
159
|
-
border-subtle
|
|
160
|
-
:class="[
|
|
161
|
-
isOwnMessage ? 'rounded-tl-[16px] rounded-tr-[16px] rounded-br-[4px] rounded-bl-[16px]' : 'rounded-tl-[4px] rounded-tr-[16px] rounded-br-[16px] rounded-bl-[16px]',
|
|
162
|
-
isPending && 'op-80'
|
|
163
|
-
]"
|
|
139
|
+
<TelaChatMessageContent
|
|
140
|
+
variant="contained"
|
|
141
|
+
:align="isOwnMessage ? 'end' : 'start'"
|
|
142
|
+
:class="isPending && 'op-80'"
|
|
164
143
|
>
|
|
165
144
|
<div v-if="hasFiles" flex="~ col" gap-4px mb-8px>
|
|
166
145
|
<a
|
|
@@ -186,7 +165,7 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
186
165
|
<div v-else body-16-regular whitespace-pre-wrap>
|
|
187
166
|
{{ message.content }}
|
|
188
167
|
</div>
|
|
189
|
-
</
|
|
168
|
+
</TelaChatMessageContent>
|
|
190
169
|
|
|
191
170
|
<div
|
|
192
171
|
mt-8px
|
|
@@ -200,48 +179,63 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
200
179
|
</template>
|
|
201
180
|
|
|
202
181
|
<template v-else>
|
|
182
|
+
<!--
|
|
183
|
+
A live stream normally owns the pending state, so this block stays
|
|
184
|
+
out of its way. Stand-in steps are the exception: until the run
|
|
185
|
+
emits anything the stream has nothing to show, and that silence is
|
|
186
|
+
exactly what they cover.
|
|
187
|
+
-->
|
|
203
188
|
<ChatReasoningThought
|
|
204
|
-
v-if="hasReasoning || isPending"
|
|
189
|
+
v-if="hasReasoning || preparingSteps?.length && !message.content || isPending && !streamActive"
|
|
205
190
|
:reasoning-data="message.reasoningData ?? []"
|
|
206
191
|
:animate="wasEverPending"
|
|
207
192
|
:completed="!isPending"
|
|
193
|
+
:preparing-steps="preparingSteps"
|
|
208
194
|
:created-at="message.createdAt"
|
|
209
195
|
:updated-at="message.updatedAt"
|
|
210
196
|
/>
|
|
211
197
|
|
|
212
198
|
<div
|
|
213
|
-
v-if="!isPending && message.content"
|
|
199
|
+
v-if="!isPending && message.content || isPending && streamActive"
|
|
214
200
|
max-w-prose
|
|
215
201
|
:class="[
|
|
216
|
-
isFailed ? 'flex gap-12px items-start
|
|
202
|
+
isFailed ? 'flex gap-12px items-start bg-subtle border border-solid rounded-12px w-full' : 'rounded-12px code-theme'
|
|
217
203
|
]"
|
|
218
204
|
>
|
|
219
205
|
<template v-if="isFailed">
|
|
220
206
|
<div i-ph-warning-circle-fill text-16px text-red-600 shrink-0 mt-2px />
|
|
221
207
|
<div flex flex-col gap-4px grow>
|
|
222
208
|
<div flex items-center gap-4px>
|
|
223
|
-
<span heading-h5-semibold text-primary>Erro</span>
|
|
209
|
+
<span heading-h5-semibold text-primary>{{ isPromptTooLong ? "Limite de contexto atingido" : "Erro" }}</span>
|
|
224
210
|
<span body-10-regular text-secondary>{{ formatTime(message.createdAt) }}</span>
|
|
225
211
|
</div>
|
|
226
|
-
<div body-12-regular text-secondary whitespace-pre-wrap>
|
|
227
|
-
|
|
212
|
+
<div v-if="isPromptTooLong" body-12-regular text-secondary whitespace-pre-wrap>
|
|
213
|
+
Esta conversa atingiu o limite de contexto do modelo e não pode continuar. Inicie uma nova conversa para seguir com o assunto.
|
|
228
214
|
</div>
|
|
229
|
-
<
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
215
|
+
<template v-else>
|
|
216
|
+
<div body-12-regular text-secondary whitespace-pre-wrap>
|
|
217
|
+
{{ displayContent }}
|
|
218
|
+
</div>
|
|
219
|
+
<div v-if="message.failReason" body-10-regular text-tertiary whitespace-pre-wrap>
|
|
220
|
+
{{ message.failReason }}
|
|
221
|
+
</div>
|
|
222
|
+
<button
|
|
223
|
+
mt-8px
|
|
224
|
+
flex items-center gap-6px
|
|
225
|
+
px-12px py-6px
|
|
226
|
+
rounded-6px
|
|
227
|
+
bg-gray-100
|
|
228
|
+
hover:bg-gray-200
|
|
229
|
+
text-13px text-gray-700
|
|
230
|
+
transition-colors
|
|
231
|
+
cursor-pointer
|
|
232
|
+
self-start
|
|
233
|
+
@click="emit('retry', message.id)"
|
|
234
|
+
>
|
|
235
|
+
<div i-ph-arrow-clockwise text-14px />
|
|
236
|
+
<span>Tentar novamente</span>
|
|
237
|
+
</button>
|
|
238
|
+
</template>
|
|
245
239
|
</div>
|
|
246
240
|
</template>
|
|
247
241
|
<template v-else>
|
|
@@ -254,7 +248,7 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
254
248
|
:custom-components="mergedCustomComponents"
|
|
255
249
|
:custom-html-tags="mergedCustomHtmlTags"
|
|
256
250
|
:render-code-blocks-as-pre="true"
|
|
257
|
-
:typewriter="shouldAnimate"
|
|
251
|
+
:typewriter="isPending || shouldAnimate"
|
|
258
252
|
/>
|
|
259
253
|
</ClientOnly>
|
|
260
254
|
<ChatFilePreview
|
|
@@ -277,31 +271,30 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
277
271
|
:custom-components="mergedCustomComponents"
|
|
278
272
|
:custom-html-tags="mergedCustomHtmlTags"
|
|
279
273
|
:render-code-blocks-as-pre="true"
|
|
280
|
-
:typewriter="shouldAnimate"
|
|
274
|
+
:typewriter="isPending || shouldAnimate"
|
|
281
275
|
text-black-900
|
|
282
276
|
/>
|
|
283
277
|
</ClientOnly>
|
|
284
278
|
<div
|
|
279
|
+
v-if="!isPending"
|
|
285
280
|
mt-8px
|
|
286
281
|
flex
|
|
287
282
|
items-center
|
|
288
|
-
gap-
|
|
283
|
+
gap-8px
|
|
289
284
|
body-11-regular
|
|
290
|
-
text-
|
|
285
|
+
text-tertiary
|
|
291
286
|
>
|
|
292
|
-
<
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
<ChatMessageFeedback v-if="hasMessageFeedback" :message="message" />
|
|
300
|
-
</div>
|
|
287
|
+
<span>{{ formatTime(message.updatedAt || message.createdAt) }}</span>
|
|
288
|
+
<TelaDotSeparator v-if="showTtft && formattedTtft || hasMessageFeedback" />
|
|
289
|
+
<div flex items-center gap-8px>
|
|
290
|
+
<span v-if="showTtft && formattedTtft" flex items-center gap-4px>
|
|
291
|
+
{{ formattedTtft }}
|
|
292
|
+
</span>
|
|
293
|
+
<ChatMessageFeedback v-if="hasMessageFeedback" :message="message" />
|
|
301
294
|
</div>
|
|
302
295
|
</div>
|
|
303
296
|
</template>
|
|
304
297
|
</div>
|
|
305
298
|
</template>
|
|
306
|
-
</
|
|
299
|
+
</TelaChatMessage>
|
|
307
300
|
</template>
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import type { Message } from '../../types/schemas/chat/conversations.js';
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
message: Message;
|
|
4
|
+
streamActive?: boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Stand-in steps shown while this turn is still working towards its first
|
|
7
|
+
* output. They yield as soon as the run emits real reasoning or content.
|
|
8
|
+
*/
|
|
9
|
+
preparingSteps?: readonly string[] | null;
|
|
10
|
+
showTtft?: boolean;
|
|
4
11
|
};
|
|
5
12
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
6
13
|
retry: (messageId: string) => any;
|
|
7
14
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
8
15
|
onRetry?: ((messageId: string) => any) | undefined;
|
|
9
|
-
}>, {
|
|
16
|
+
}>, {
|
|
17
|
+
showTtft: boolean;
|
|
18
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
10
19
|
declare const _default: typeof __VLS_export;
|
|
11
20
|
export default _default;
|
|
@@ -7,245 +7,41 @@ const props = defineProps({
|
|
|
7
7
|
const embedConfig = useEmbedConfig();
|
|
8
8
|
const { emit: emitFeedback } = useMessageFeedback();
|
|
9
9
|
const config = computed(() => embedConfig?.feedbackConfig.value ?? null);
|
|
10
|
-
const positiveReasons = computed(() => config.value?.positiveReasons ?? []);
|
|
11
|
-
const negativeReasons = computed(() => config.value?.negativeReasons ?? []);
|
|
10
|
+
const positiveReasons = computed(() => [...config.value?.positiveReasons ?? []]);
|
|
11
|
+
const negativeReasons = computed(() => [...config.value?.negativeReasons ?? []]);
|
|
12
12
|
const persistedMap = computed(() => embedConfig?.messageFeedback.value ?? null);
|
|
13
13
|
const persistedRating = computed(() => persistedMap.value?.[props.message.id] ?? null);
|
|
14
14
|
const localRating = ref(null);
|
|
15
15
|
const rating = computed(() => persistedMap.value ? persistedRating.value : localRating.value);
|
|
16
|
-
|
|
17
|
-
const positiveOpen = computed({
|
|
18
|
-
get: () => openRating.value === "positive",
|
|
19
|
-
set: (open) => {
|
|
20
|
-
openRating.value = open ? "positive" : null;
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
const negativeOpen = computed({
|
|
24
|
-
get: () => openRating.value === "negative",
|
|
25
|
-
set: (open) => {
|
|
26
|
-
openRating.value = open ? "negative" : null;
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
const selectedReasons = ref([]);
|
|
30
|
-
const comment = ref("");
|
|
31
|
-
watch(openRating, (opened) => {
|
|
32
|
-
if (opened) {
|
|
33
|
-
selectedReasons.value = [];
|
|
34
|
-
comment.value = "";
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
const canSubmitNegative = computed(() => {
|
|
38
|
-
if (!config.value?.requireReason)
|
|
39
|
-
return true;
|
|
40
|
-
if (negativeReasons.value.length > 0)
|
|
41
|
-
return selectedReasons.value.length > 0;
|
|
42
|
-
return comment.value.trim().length > 0;
|
|
43
|
-
});
|
|
44
|
-
function submitRating(next) {
|
|
16
|
+
function handleSubmit(payload) {
|
|
45
17
|
emitFeedback({
|
|
46
18
|
conversationId: props.message.conversationId,
|
|
47
19
|
messageId: props.message.id,
|
|
48
|
-
rating:
|
|
49
|
-
reasons:
|
|
50
|
-
comment: comment
|
|
20
|
+
rating: payload.rating,
|
|
21
|
+
reasons: payload.reasons,
|
|
22
|
+
comment: payload.comment
|
|
51
23
|
});
|
|
52
|
-
localRating.value =
|
|
53
|
-
openRating.value = null;
|
|
54
|
-
}
|
|
55
|
-
function toggleReason(reason) {
|
|
56
|
-
selectedReasons.value = selectedReasons.value.includes(reason) ? selectedReasons.value.filter((item) => item !== reason) : [...selectedReasons.value, reason];
|
|
24
|
+
localRating.value = payload.rating;
|
|
57
25
|
}
|
|
58
26
|
</script>
|
|
59
27
|
|
|
60
28
|
<template>
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<TelaPopover v-else v-model:open="positiveOpen">
|
|
80
|
-
<TelaPopoverTrigger as-child>
|
|
81
|
-
<button
|
|
82
|
-
type="button"
|
|
83
|
-
h-28px w-28px
|
|
84
|
-
flex items-center justify-center
|
|
85
|
-
rounded-8px
|
|
86
|
-
transition-colors
|
|
87
|
-
cursor-pointer
|
|
88
|
-
:class="rating === 'positive' ? 'bg-green-100 text-green-700' : 'text-black-400 hover:bg-neutral-100 hover:text-black-600'"
|
|
89
|
-
aria-label="Resposta útil"
|
|
90
|
-
title="Resposta útil"
|
|
91
|
-
>
|
|
92
|
-
<span :class="rating === 'positive' ? 'i-ph-thumbs-up-fill' : 'i-ph-thumbs-up'" text-15px />
|
|
93
|
-
</button>
|
|
94
|
-
</TelaPopoverTrigger>
|
|
95
|
-
<TelaPopoverContent
|
|
96
|
-
align="end"
|
|
97
|
-
:side-offset="4"
|
|
98
|
-
class="!w-auto !rounded-12px !border-neutral-200 !bg-white !p-0 !text-neutral-900 !shadow-lg"
|
|
99
|
-
>
|
|
100
|
-
<div w-288px p-12px flex="~ col" gap-10px>
|
|
101
|
-
<div flex="~ col" gap-2px>
|
|
102
|
-
<span text-14px font-medium text-neutral-900 leading-18px>O que ajudou?</span>
|
|
103
|
-
<span text-12px text-neutral-500 leading-16px>Marcar o que funcionou refina as próximas respostas.</span>
|
|
104
|
-
</div>
|
|
105
|
-
<div flex flex-wrap gap-6px>
|
|
106
|
-
<button
|
|
107
|
-
v-for="reason in positiveReasons"
|
|
108
|
-
:key="reason"
|
|
109
|
-
type="button"
|
|
110
|
-
h-26px px-10px
|
|
111
|
-
rounded-full
|
|
112
|
-
b=".5px solid"
|
|
113
|
-
text-12px leading-16px
|
|
114
|
-
transition-colors
|
|
115
|
-
cursor-pointer
|
|
116
|
-
:class="selectedReasons.includes(reason) ? 'bg-green-100 text-green-700 border-transparent font-medium' : 'bg-white text-neutral-600 border-neutral-300 hover:bg-neutral-50'"
|
|
117
|
-
@click="toggleReason(reason)"
|
|
118
|
-
>
|
|
119
|
-
{{ reason }}
|
|
120
|
-
</button>
|
|
121
|
-
</div>
|
|
122
|
-
<textarea
|
|
123
|
-
v-model="comment"
|
|
124
|
-
rows="2"
|
|
125
|
-
placeholder="Comentário (opcional)…"
|
|
126
|
-
aria-label="Comentário"
|
|
127
|
-
w-full
|
|
128
|
-
px-10px py-8px
|
|
129
|
-
rounded-8px
|
|
130
|
-
b=".5px neutral-300"
|
|
131
|
-
bg-white
|
|
132
|
-
outline-none
|
|
133
|
-
resize-none
|
|
134
|
-
text-13px text-neutral-900 leading-18px
|
|
135
|
-
focus:b-neutral-900
|
|
136
|
-
@keydown.escape.prevent.stop="positiveOpen = false"
|
|
137
|
-
/>
|
|
138
|
-
<div flex justify-end gap-6px>
|
|
139
|
-
<button
|
|
140
|
-
type="button"
|
|
141
|
-
h-28px px-10px rounded-8px
|
|
142
|
-
text-13px font-medium text-neutral-700
|
|
143
|
-
hover:bg-neutral-100
|
|
144
|
-
transition-colors
|
|
145
|
-
@click="positiveOpen = false"
|
|
146
|
-
>
|
|
147
|
-
Cancelar
|
|
148
|
-
</button>
|
|
149
|
-
<button
|
|
150
|
-
type="button"
|
|
151
|
-
h-28px px-10px rounded-8px
|
|
152
|
-
bg-neutral-900 text-white
|
|
153
|
-
text-13px font-medium
|
|
154
|
-
transition-opacity
|
|
155
|
-
@click="submitRating('positive')"
|
|
156
|
-
>
|
|
157
|
-
Enviar
|
|
158
|
-
</button>
|
|
159
|
-
</div>
|
|
160
|
-
</div>
|
|
161
|
-
</TelaPopoverContent>
|
|
162
|
-
</TelaPopover>
|
|
163
|
-
|
|
164
|
-
<TelaPopover v-model:open="negativeOpen">
|
|
165
|
-
<TelaPopoverTrigger as-child>
|
|
166
|
-
<button
|
|
167
|
-
type="button"
|
|
168
|
-
h-28px w-28px
|
|
169
|
-
flex items-center justify-center
|
|
170
|
-
rounded-8px
|
|
171
|
-
transition-colors
|
|
172
|
-
cursor-pointer
|
|
173
|
-
:class="rating === 'negative' ? 'bg-red-100 text-red-700' : 'text-black-400 hover:bg-neutral-100 hover:text-black-600'"
|
|
174
|
-
aria-label="Resposta não útil"
|
|
175
|
-
title="Resposta não útil"
|
|
176
|
-
>
|
|
177
|
-
<span :class="rating === 'negative' ? 'i-ph-thumbs-down-fill' : 'i-ph-thumbs-down'" text-15px />
|
|
178
|
-
</button>
|
|
179
|
-
</TelaPopoverTrigger>
|
|
180
|
-
<TelaPopoverContent
|
|
181
|
-
align="end"
|
|
182
|
-
:side-offset="4"
|
|
183
|
-
class="!w-auto !rounded-12px !border-neutral-200 !bg-white !p-0 !text-neutral-900 !shadow-lg"
|
|
184
|
-
>
|
|
185
|
-
<div w-288px p-12px flex="~ col" gap-10px>
|
|
186
|
-
<div flex="~ col" gap-2px>
|
|
187
|
-
<span text-14px font-medium text-neutral-900 leading-18px>O que não funcionou?</span>
|
|
188
|
-
<span text-12px text-neutral-500 leading-16px>Seu retorno vai para a triagem de qualidade.</span>
|
|
189
|
-
</div>
|
|
190
|
-
<div v-if="negativeReasons.length > 0" flex flex-wrap gap-6px>
|
|
191
|
-
<button
|
|
192
|
-
v-for="reason in negativeReasons"
|
|
193
|
-
:key="reason"
|
|
194
|
-
type="button"
|
|
195
|
-
h-26px px-10px
|
|
196
|
-
rounded-full
|
|
197
|
-
b=".5px solid"
|
|
198
|
-
text-12px leading-16px
|
|
199
|
-
transition-colors
|
|
200
|
-
cursor-pointer
|
|
201
|
-
:class="selectedReasons.includes(reason) ? 'bg-blue-100 text-blue-900 border-transparent font-medium' : 'bg-white text-neutral-600 border-neutral-300 hover:bg-neutral-50'"
|
|
202
|
-
@click="toggleReason(reason)"
|
|
203
|
-
>
|
|
204
|
-
{{ reason }}
|
|
205
|
-
</button>
|
|
206
|
-
</div>
|
|
207
|
-
<textarea
|
|
208
|
-
v-model="comment"
|
|
209
|
-
rows="2"
|
|
210
|
-
placeholder="Conte o que faltou (opcional)…"
|
|
211
|
-
aria-label="Comentário"
|
|
212
|
-
w-full
|
|
213
|
-
px-10px py-8px
|
|
214
|
-
rounded-8px
|
|
215
|
-
b=".5px neutral-300"
|
|
216
|
-
bg-white
|
|
217
|
-
outline-none
|
|
218
|
-
resize-none
|
|
219
|
-
text-13px text-neutral-900 leading-18px
|
|
220
|
-
focus:b-neutral-900
|
|
221
|
-
@keydown.escape.prevent.stop="negativeOpen = false"
|
|
222
|
-
/>
|
|
223
|
-
<div flex justify-end gap-6px>
|
|
224
|
-
<button
|
|
225
|
-
type="button"
|
|
226
|
-
h-28px px-10px rounded-8px
|
|
227
|
-
text-13px font-medium text-neutral-700
|
|
228
|
-
hover:bg-neutral-100
|
|
229
|
-
transition-colors
|
|
230
|
-
@click="negativeOpen = false"
|
|
231
|
-
>
|
|
232
|
-
Cancelar
|
|
233
|
-
</button>
|
|
234
|
-
<button
|
|
235
|
-
type="button"
|
|
236
|
-
h-28px px-10px rounded-8px
|
|
237
|
-
bg-neutral-900 text-white
|
|
238
|
-
text-13px font-medium
|
|
239
|
-
transition-opacity
|
|
240
|
-
:disabled="!canSubmitNegative"
|
|
241
|
-
:class="!canSubmitNegative && 'opacity-40 cursor-not-allowed'"
|
|
242
|
-
@click="submitRating('negative')"
|
|
243
|
-
>
|
|
244
|
-
Enviar
|
|
245
|
-
</button>
|
|
246
|
-
</div>
|
|
247
|
-
</div>
|
|
248
|
-
</TelaPopoverContent>
|
|
249
|
-
</TelaPopover>
|
|
250
|
-
</div>
|
|
29
|
+
<TelaChatMessageFeedback
|
|
30
|
+
:rating="rating"
|
|
31
|
+
:positive-reasons="positiveReasons"
|
|
32
|
+
:negative-reasons="negativeReasons"
|
|
33
|
+
:require-reason="config?.requireReason ?? false"
|
|
34
|
+
positive-title="O que ajudou?"
|
|
35
|
+
positive-description="Marcar o que funcionou refina as próximas respostas."
|
|
36
|
+
positive-comment-placeholder="Comentário (opcional)…"
|
|
37
|
+
negative-title="O que não funcionou?"
|
|
38
|
+
negative-description="Seu retorno vai para a triagem de qualidade."
|
|
39
|
+
negative-comment-placeholder="Conte o que faltou (opcional)…"
|
|
40
|
+
cancel-label="Cancelar"
|
|
41
|
+
submit-label="Enviar"
|
|
42
|
+
helpful-label="Resposta útil"
|
|
43
|
+
not-helpful-label="Resposta não útil"
|
|
44
|
+
comment-label="Comentário"
|
|
45
|
+
@submit="handleSubmit"
|
|
46
|
+
/>
|
|
251
47
|
</template>
|