@meistrari/chat-nuxt 1.11.0 → 1.12.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/dist/module.json +1 -1
- package/dist/module.mjs +15 -0
- package/dist/runtime/components/chat/conversation-item.d.vue.ts +6 -1
- package/dist/runtime/components/chat/conversation-item.vue +30 -141
- package/dist/runtime/components/chat/conversation-item.vue.d.ts +6 -1
- package/dist/runtime/components/chat/conversation-list.d.vue.ts +1 -0
- package/dist/runtime/components/chat/conversation-list.vue +300 -72
- package/dist/runtime/components/chat/conversation-list.vue.d.ts +1 -0
- package/dist/runtime/components/chat/conversation-new-group-button.d.vue.ts +7 -0
- package/dist/runtime/components/chat/conversation-new-group-button.vue +90 -0
- package/dist/runtime/components/chat/conversation-new-group-button.vue.d.ts +7 -0
- package/dist/runtime/components/chat/conversation-new-group-popover-content.d.vue.ts +23 -0
- package/dist/runtime/components/chat/conversation-new-group-popover-content.vue +75 -0
- package/dist/runtime/components/chat/conversation-new-group-popover-content.vue.d.ts +23 -0
- package/dist/runtime/components/chat/mobile/shell/drawer.d.vue.ts +2 -2
- package/dist/runtime/components/chat/mobile/shell/drawer.vue +370 -41
- package/dist/runtime/components/chat/mobile/shell/drawer.vue.d.ts +2 -2
- package/dist/runtime/components/chat/mobile/shell/shell.d.vue.ts +6 -6
- package/dist/runtime/components/chat/mobile/shell/shell.vue +1 -1
- package/dist/runtime/components/chat/mobile/shell/shell.vue.d.ts +6 -6
- package/dist/runtime/components/chat/topbar.d.vue.ts +2 -2
- package/dist/runtime/components/chat/topbar.vue.d.ts +2 -2
- package/dist/runtime/composables/useConversationGroups.d.ts +12 -0
- package/dist/runtime/composables/useConversationGroups.js +160 -0
- package/dist/runtime/composables/useConversationItem.d.ts +48 -0
- package/dist/runtime/composables/useConversationItem.js +176 -0
- package/dist/runtime/composables/useConversationList.d.ts +128 -0
- package/dist/runtime/composables/useConversationList.js +553 -0
- package/dist/runtime/composables/useConversations.d.ts +2 -0
- package/dist/runtime/composables/useConversations.js +45 -1
- package/dist/runtime/composables/usePendingConversationGroup.d.ts +5 -0
- package/dist/runtime/composables/usePendingConversationGroup.js +33 -0
- package/dist/runtime/embed/components/ChatConfigurationModal.d.vue.ts +1 -1
- package/dist/runtime/embed/components/ChatConfigurationModal.vue.d.ts +1 -1
- package/dist/runtime/embed/components/ChatEmbedInner.vue +24 -5
- package/dist/runtime/server/api/chat/conversations/[id]/group.patch.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/[id]/group.patch.js +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.delete.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.delete.js +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.patch.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/[groupId]/index.patch.js +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/index.get.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/index.get.js +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/index.post.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/groups/index.post.js +1 -0
- package/dist/runtime/server/api/conversations/[id]/duplicate.post.js +1 -0
- package/dist/runtime/server/api/conversations/[id]/group.patch.d.ts +2 -0
- package/dist/runtime/server/api/conversations/[id]/group.patch.js +46 -0
- package/dist/runtime/server/api/conversations/groups/[groupId]/index.delete.d.ts +4 -0
- package/dist/runtime/server/api/conversations/groups/[groupId]/index.delete.js +25 -0
- package/dist/runtime/server/api/conversations/groups/[groupId]/index.patch.d.ts +2 -0
- package/dist/runtime/server/api/conversations/groups/[groupId]/index.patch.js +30 -0
- package/dist/runtime/server/api/conversations/groups/index.get.d.ts +2 -0
- package/dist/runtime/server/api/conversations/groups/index.get.js +13 -0
- package/dist/runtime/server/api/conversations/groups/index.post.d.ts +2 -0
- package/dist/runtime/server/api/conversations/groups/index.post.js +26 -0
- package/dist/runtime/server/api/conversations/index.post.js +1 -0
- package/dist/runtime/server/db/schema/conversation-groups.d.ts +143 -0
- package/dist/runtime/server/db/schema/conversation-groups.js +20 -0
- package/dist/runtime/server/db/schema/conversations.d.ts +17 -0
- package/dist/runtime/server/db/schema/conversations.js +3 -0
- package/dist/runtime/server/db/schema/index.d.ts +1 -0
- package/dist/runtime/server/db/schema/index.js +1 -0
- package/dist/runtime/server/utils/conversation-group.d.ts +4 -0
- package/dist/runtime/server/utils/conversation-group.js +48 -0
- package/dist/runtime/types/chat.d.ts +2 -1
- package/dist/runtime/utils/conversation-groups.d.ts +18 -0
- package/dist/runtime/utils/conversation-groups.js +30 -0
- package/drizzle/0016_dry_aaron_stack.sql +16 -0
- package/drizzle/meta/0016_snapshot.json +887 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
|
@@ -1,68 +1,55 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import {
|
|
3
|
-
import { groupConversationsByDate } from "../../utils/format-time";
|
|
4
|
-
import { exportConversationToMarkdown } from "../../utils/export-conversation";
|
|
5
|
-
import { conversationListSkeletonRows } from "../../utils/conversation-list-skeleton";
|
|
2
|
+
import { useConversationList } from "../../composables/useConversationList";
|
|
6
3
|
const props = defineProps({
|
|
7
4
|
conversations: { type: null, required: true },
|
|
8
5
|
currentId: { type: String, required: false },
|
|
9
6
|
getMemberImage: { type: Function, required: false },
|
|
10
7
|
selectConversation: { type: Function, required: false },
|
|
11
8
|
resetConversation: { type: Function, required: false },
|
|
9
|
+
startConversation: { type: Function, required: false },
|
|
12
10
|
loading: { type: Boolean, required: false }
|
|
13
11
|
});
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
async function handleExport(id) {
|
|
56
|
-
try {
|
|
57
|
-
const conversation = await $fetch(
|
|
58
|
-
chatApi.path(`/conversations/${id}`),
|
|
59
|
-
chatApi.withChatHeaders()
|
|
60
|
-
);
|
|
61
|
-
exportConversationToMarkdown(conversation);
|
|
62
|
-
} catch (err) {
|
|
63
|
-
console.error("Failed to export conversation:", err);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
12
|
+
const {
|
|
13
|
+
conversationSearch,
|
|
14
|
+
hasConversationContent,
|
|
15
|
+
shouldShowSkeleton,
|
|
16
|
+
emptyConversationMessage,
|
|
17
|
+
skeletonRows,
|
|
18
|
+
groupItems,
|
|
19
|
+
ungroupedConversations,
|
|
20
|
+
ungroupedDropTarget,
|
|
21
|
+
ungroupedConversationLabel,
|
|
22
|
+
shouldShowUngroupedHeader,
|
|
23
|
+
showGroupCreationPrompt,
|
|
24
|
+
showDeleteGroupModal,
|
|
25
|
+
showDeleteGroupConversationsModal,
|
|
26
|
+
groupActionLoading,
|
|
27
|
+
renameGroupName,
|
|
28
|
+
renameGroupError,
|
|
29
|
+
deleteGroupConfirmationMessage,
|
|
30
|
+
deleteGroupConversationsConfirmationMessage,
|
|
31
|
+
toggleGroup,
|
|
32
|
+
clearDeleteGroupTarget,
|
|
33
|
+
clearDeleteGroupConversationsTarget,
|
|
34
|
+
confirmDeleteGroup,
|
|
35
|
+
confirmDeleteGroupConversations,
|
|
36
|
+
cancelRenameGroup,
|
|
37
|
+
submitRenameGroup,
|
|
38
|
+
handleRenameGroupBlur,
|
|
39
|
+
setRenameGroupInputRef,
|
|
40
|
+
handleDelete,
|
|
41
|
+
handleRename,
|
|
42
|
+
handleDuplicate,
|
|
43
|
+
handleExport,
|
|
44
|
+
handleConversationDragStart,
|
|
45
|
+
clearDragState,
|
|
46
|
+
handleDragEnter,
|
|
47
|
+
handleDragOver,
|
|
48
|
+
handleDragLeave,
|
|
49
|
+
getConversationItemProps,
|
|
50
|
+
handleConversationDrop,
|
|
51
|
+
startConversationInGroup
|
|
52
|
+
} = useConversationList(props);
|
|
66
53
|
</script>
|
|
67
54
|
|
|
68
55
|
<template>
|
|
@@ -72,14 +59,14 @@ async function handleExport(id) {
|
|
|
72
59
|
<ChatConversationCreatorFilter />
|
|
73
60
|
</div>
|
|
74
61
|
|
|
75
|
-
<div v-if="
|
|
62
|
+
<div v-if="shouldShowSkeleton" flex="~ col" gap-4px>
|
|
76
63
|
<div h-20px flex items-center pl-12px pr-4px mb-4px>
|
|
77
64
|
<TelaSkeleton rounded-4px bg-neutral-200 style="width: 68px; height: 12px;" />
|
|
78
65
|
</div>
|
|
79
66
|
|
|
80
67
|
<div flex="~ col" gap-1px>
|
|
81
68
|
<div
|
|
82
|
-
v-for="row in
|
|
69
|
+
v-for="row in skeletonRows"
|
|
83
70
|
:key="row.id"
|
|
84
71
|
flex items-center h-32px px-8px rounded-8px
|
|
85
72
|
>
|
|
@@ -88,37 +75,278 @@ async function handleExport(id) {
|
|
|
88
75
|
</div>
|
|
89
76
|
</div>
|
|
90
77
|
|
|
91
|
-
<div v-else-if="
|
|
78
|
+
<div v-else-if="!hasConversationContent" p-16px text-center>
|
|
92
79
|
<p text-14px text-neutral-400>
|
|
93
|
-
{{
|
|
80
|
+
{{ emptyConversationMessage }}
|
|
94
81
|
</p>
|
|
95
82
|
</div>
|
|
96
83
|
|
|
97
84
|
<template v-else>
|
|
98
|
-
<div
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
85
|
+
<div
|
|
86
|
+
v-if="showGroupCreationPrompt"
|
|
87
|
+
mx-8px
|
|
88
|
+
flex="~ col" gap-10px
|
|
89
|
+
>
|
|
90
|
+
<div h-20px flex items-center px-4px>
|
|
91
|
+
<span text-12px font-semibold text-neutral-400 uppercase style="letter-spacing: 0">
|
|
92
|
+
PASTAS
|
|
93
|
+
</span>
|
|
94
|
+
</div>
|
|
95
|
+
<div flex="~ col" gap-6px>
|
|
96
|
+
<div
|
|
97
|
+
h-36px px-10px
|
|
98
|
+
flex items-center gap-10px
|
|
99
|
+
rounded-8px
|
|
100
|
+
b=".5px dashed neutral-200"
|
|
101
|
+
>
|
|
102
|
+
<span i-ph-folder text-16px text-neutral-400 shrink-0 />
|
|
103
|
+
<span h-10px w-120px rounded-full bg-neutral-100 />
|
|
104
|
+
</div>
|
|
105
|
+
<div
|
|
106
|
+
h-36px px-10px
|
|
107
|
+
flex items-center gap-10px
|
|
108
|
+
rounded-8px
|
|
109
|
+
b=".5px dashed neutral-200"
|
|
110
|
+
>
|
|
111
|
+
<span i-ph-folder text-16px text-neutral-400 shrink-0 />
|
|
112
|
+
<span h-10px w-84px rounded-full bg-neutral-100 />
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
<p px-4px text-14px text-neutral-500 leading-20px>
|
|
116
|
+
Suas pastas aparecem aqui. Crie uma para começar a agrupar conversas.
|
|
117
|
+
</p>
|
|
118
|
+
<ChatConversationNewGroupButton label="Criar pasta" variant="empty-state" />
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div v-if="groupItems.length > 0" flex="~ col" gap-2px>
|
|
122
|
+
<div
|
|
123
|
+
v-for="groupBucket in groupItems"
|
|
124
|
+
:key="groupBucket.group.id"
|
|
125
|
+
flex="~ col"
|
|
126
|
+
rounded-8px
|
|
127
|
+
transition-colors
|
|
128
|
+
:class="groupBucket.dropTarget ? 'bg-neutral-200' : ''"
|
|
129
|
+
@dragenter.prevent="handleDragEnter(groupBucket.group.id)"
|
|
130
|
+
@dragover.prevent="handleDragOver($event, groupBucket.group.id)"
|
|
131
|
+
@dragleave="handleDragLeave($event, groupBucket.group.id)"
|
|
132
|
+
@drop.prevent="handleConversationDrop($event, groupBucket.group.id)"
|
|
133
|
+
>
|
|
134
|
+
<div
|
|
135
|
+
v-if="groupBucket.isRenaming"
|
|
136
|
+
h-32px w-full px-8px
|
|
137
|
+
flex items-center gap-8px
|
|
138
|
+
rounded-8px
|
|
139
|
+
:class="groupBucket.dropTarget ? 'bg-neutral-200' : ''"
|
|
140
|
+
>
|
|
141
|
+
<span
|
|
142
|
+
i-ph-caret-right
|
|
143
|
+
text-12px text-neutral-400
|
|
144
|
+
transition-transform
|
|
145
|
+
:class="groupBucket.open && 'rotate-90'"
|
|
146
|
+
/>
|
|
147
|
+
<span
|
|
148
|
+
:class="groupBucket.folderIcon"
|
|
149
|
+
text-16px text-neutral-500 shrink-0
|
|
150
|
+
/>
|
|
151
|
+
<div
|
|
152
|
+
flex-1 min-w-0 flex items-center
|
|
153
|
+
pb-1px
|
|
154
|
+
b-b=".5px solid #A3A3A3"
|
|
155
|
+
:class="renameGroupError && 'border-red-500'"
|
|
156
|
+
>
|
|
157
|
+
<input
|
|
158
|
+
:ref="(element) => setRenameGroupInputRef(element, groupBucket.group.id)"
|
|
159
|
+
v-model="renameGroupName"
|
|
160
|
+
type="text"
|
|
161
|
+
autocomplete="off"
|
|
162
|
+
maxlength="80"
|
|
163
|
+
aria-label="Nome da pasta"
|
|
164
|
+
w-full min-w-0
|
|
165
|
+
bg-transparent border-none outline-none
|
|
166
|
+
text-14px font-semibold text-neutral-700 leading-18px
|
|
167
|
+
p-0
|
|
168
|
+
:disabled="groupActionLoading"
|
|
169
|
+
@blur="handleRenameGroupBlur"
|
|
170
|
+
@keydown.enter.prevent="submitRenameGroup"
|
|
171
|
+
@keydown.escape.prevent.stop="cancelRenameGroup"
|
|
172
|
+
@click.stop
|
|
173
|
+
@dblclick.stop
|
|
174
|
+
>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<div v-else rounded-8px class="group" :class="groupBucket.dropTarget ? '' : 'hover:bg-neutral-100'">
|
|
179
|
+
<div h-32px w-full px-8px flex items-center gap-4px text-left>
|
|
180
|
+
<button
|
|
181
|
+
type="button"
|
|
182
|
+
h-full flex-1 min-w-0
|
|
183
|
+
flex items-center gap-8px
|
|
184
|
+
text-left
|
|
185
|
+
cursor-pointer
|
|
186
|
+
@click="toggleGroup(groupBucket.group.id)"
|
|
187
|
+
>
|
|
188
|
+
<span
|
|
189
|
+
i-ph-caret-right
|
|
190
|
+
text-12px text-neutral-400
|
|
191
|
+
transition-transform
|
|
192
|
+
:class="groupBucket.open && 'rotate-90'"
|
|
193
|
+
/>
|
|
194
|
+
<span
|
|
195
|
+
:class="groupBucket.folderIcon"
|
|
196
|
+
text-16px text-neutral-500 shrink-0
|
|
197
|
+
/>
|
|
198
|
+
<span flex-1 min-w-0 truncate text-14px font-semibold text-neutral-900 leading-18px>
|
|
199
|
+
{{ groupBucket.group.name }}
|
|
200
|
+
</span>
|
|
201
|
+
<span v-if="groupBucket.conversationCountLabel" text-13px font-medium text-neutral-400 tabular-nums>
|
|
202
|
+
{{ groupBucket.conversationCountLabel }}
|
|
203
|
+
</span>
|
|
204
|
+
</button>
|
|
205
|
+
|
|
206
|
+
<div
|
|
207
|
+
flex items-center justify-center shrink-0
|
|
208
|
+
class="opacity-0 group-hover:opacity-100 focus-within:opacity-100 transition-opacity"
|
|
209
|
+
>
|
|
210
|
+
<TelaDropdownMenu
|
|
211
|
+
:items="groupBucket.menuItems"
|
|
212
|
+
align="end"
|
|
213
|
+
content-class="chat-sidebar-floating"
|
|
214
|
+
>
|
|
215
|
+
<button
|
|
216
|
+
type="button"
|
|
217
|
+
aria-label="Ações da pasta"
|
|
218
|
+
p-4px
|
|
219
|
+
rounded-6px
|
|
220
|
+
hover:bg-neutral-200
|
|
221
|
+
transition-colors
|
|
222
|
+
cursor-pointer
|
|
223
|
+
@click.prevent.stop
|
|
224
|
+
>
|
|
225
|
+
<div i-ph-dots-three-vertical text-16px text-neutral-400 />
|
|
226
|
+
</button>
|
|
227
|
+
</TelaDropdownMenu>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<div
|
|
233
|
+
v-if="groupBucket.isRenaming && renameGroupError"
|
|
234
|
+
ml-44px mr-8px mb-4px
|
|
235
|
+
>
|
|
236
|
+
<span text-12px text-red-600 leading-16px aria-live="polite">
|
|
237
|
+
{{ renameGroupError }}
|
|
238
|
+
</span>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<Transition name="group-slide">
|
|
242
|
+
<div v-if="groupBucket.open" class="group-slide-panel">
|
|
243
|
+
<div class="group-slide__inner">
|
|
244
|
+
<div flex="~ col" gap-1px ml-20px mt-1px mb-4px>
|
|
245
|
+
<ChatConversationItem
|
|
246
|
+
v-for="conversation in groupBucket.items"
|
|
247
|
+
:key="conversation.id"
|
|
248
|
+
v-bind="getConversationItemProps(conversation)"
|
|
249
|
+
@delete="handleDelete"
|
|
250
|
+
@rename="handleRename"
|
|
251
|
+
@duplicate="handleDuplicate"
|
|
252
|
+
@export="handleExport"
|
|
253
|
+
@drag-start="handleConversationDragStart"
|
|
254
|
+
@drag-end="clearDragState"
|
|
255
|
+
/>
|
|
256
|
+
<button
|
|
257
|
+
v-if="groupBucket.showNewConversationAction"
|
|
258
|
+
type="button"
|
|
259
|
+
h-32px px-8px
|
|
260
|
+
flex items-center gap-8px
|
|
261
|
+
rounded-8px
|
|
262
|
+
text-left
|
|
263
|
+
hover:bg-neutral-100
|
|
264
|
+
transition-colors
|
|
265
|
+
cursor-pointer
|
|
266
|
+
@click="startConversationInGroup(groupBucket.group.id)"
|
|
267
|
+
>
|
|
268
|
+
<span i-ph-plus text-14px text-neutral-500 shrink-0 />
|
|
269
|
+
<span text-14px font-medium text-neutral-500 leading-18px>
|
|
270
|
+
Nova conversa
|
|
271
|
+
</span>
|
|
272
|
+
</button>
|
|
273
|
+
<div
|
|
274
|
+
v-else-if="groupBucket.showSearchEmpty"
|
|
275
|
+
h-32px px-8px flex items-center
|
|
276
|
+
>
|
|
277
|
+
<span text-13px text-neutral-400 leading-17px>
|
|
278
|
+
Sem resultados
|
|
279
|
+
</span>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
</Transition>
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
<div
|
|
289
|
+
flex="~ col" gap-4px
|
|
290
|
+
rounded-8px
|
|
291
|
+
transition-colors
|
|
292
|
+
:class="ungroupedDropTarget ? 'bg-neutral-100' : ''"
|
|
293
|
+
@dragenter.prevent="handleDragEnter(null)"
|
|
294
|
+
@dragover.prevent="handleDragOver($event, null)"
|
|
295
|
+
@dragleave="handleDragLeave($event, null)"
|
|
296
|
+
@drop.prevent="handleConversationDrop($event, null)"
|
|
297
|
+
>
|
|
298
|
+
<div v-if="shouldShowUngroupedHeader" h-20px flex items-center pl-12px pr-4px mb-4px>
|
|
299
|
+
<span text-12px font-semibold text-neutral-400 uppercase style="letter-spacing: 0">
|
|
300
|
+
{{ ungroupedConversationLabel }}
|
|
103
301
|
</span>
|
|
104
302
|
</div>
|
|
105
303
|
|
|
106
|
-
|
|
107
|
-
<div flex="~ col" gap-1px>
|
|
304
|
+
<div v-if="ungroupedConversations.length > 0" flex="~ col" gap-1px>
|
|
108
305
|
<ChatConversationItem
|
|
109
|
-
v-for="conversation in
|
|
306
|
+
v-for="conversation in ungroupedConversations"
|
|
110
307
|
:key="conversation.id"
|
|
111
|
-
|
|
112
|
-
:active="conversation.id === currentId"
|
|
113
|
-
:user-image="getMemberImage?.(conversation.userId)"
|
|
114
|
-
:select-conversation="selectConversation"
|
|
308
|
+
v-bind="getConversationItemProps(conversation)"
|
|
115
309
|
@delete="handleDelete"
|
|
116
310
|
@rename="handleRename"
|
|
117
311
|
@duplicate="handleDuplicate"
|
|
118
312
|
@export="handleExport"
|
|
313
|
+
@drag-start="handleConversationDragStart"
|
|
314
|
+
@drag-end="clearDragState"
|
|
119
315
|
/>
|
|
120
316
|
</div>
|
|
121
317
|
</div>
|
|
122
318
|
</template>
|
|
123
319
|
</div>
|
|
320
|
+
|
|
321
|
+
<TelaConfirmationModal
|
|
322
|
+
v-model:open="showDeleteGroupModal"
|
|
323
|
+
title="Excluir pasta"
|
|
324
|
+
variant="danger"
|
|
325
|
+
class="chat-sidebar-floating"
|
|
326
|
+
confirm-button-text="Excluir pasta"
|
|
327
|
+
cancel-button-text="Cancelar"
|
|
328
|
+
:loading="groupActionLoading"
|
|
329
|
+
@confirm="confirmDeleteGroup"
|
|
330
|
+
@cancel="clearDeleteGroupTarget"
|
|
331
|
+
>
|
|
332
|
+
{{ deleteGroupConfirmationMessage }}
|
|
333
|
+
</TelaConfirmationModal>
|
|
334
|
+
|
|
335
|
+
<TelaConfirmationModal
|
|
336
|
+
v-model:open="showDeleteGroupConversationsModal"
|
|
337
|
+
title="Excluir conversas"
|
|
338
|
+
variant="danger"
|
|
339
|
+
class="chat-sidebar-floating"
|
|
340
|
+
confirm-button-text="Excluir conversas"
|
|
341
|
+
cancel-button-text="Cancelar"
|
|
342
|
+
:loading="groupActionLoading"
|
|
343
|
+
@confirm="confirmDeleteGroupConversations"
|
|
344
|
+
@cancel="clearDeleteGroupConversationsTarget"
|
|
345
|
+
>
|
|
346
|
+
{{ deleteGroupConversationsConfirmationMessage }}
|
|
347
|
+
</TelaConfirmationModal>
|
|
124
348
|
</template>
|
|
349
|
+
|
|
350
|
+
<style scoped>
|
|
351
|
+
.group-slide-panel{display:grid;grid-template-rows:1fr}.group-slide-enter-active,.group-slide-leave-active{overflow:hidden;transition:grid-template-rows .18s cubic-bezier(.2,.8,.2,1),opacity .14s ease,transform .18s cubic-bezier(.2,.8,.2,1)}.group-slide-enter-from,.group-slide-leave-to{grid-template-rows:0fr;opacity:0;transform:translateY(-4px)}.group-slide-enter-to,.group-slide-leave-from{grid-template-rows:1fr;opacity:1;transform:translateY(0)}.group-slide__inner{min-height:0;overflow:hidden}
|
|
352
|
+
</style>
|
|
@@ -6,6 +6,7 @@ type __VLS_Props = {
|
|
|
6
6
|
getMemberImage?: (userId: string) => string | undefined;
|
|
7
7
|
selectConversation?: (id: string) => void | Promise<void>;
|
|
8
8
|
resetConversation?: () => void | Promise<void>;
|
|
9
|
+
startConversation?: (groupId?: string | null) => void | Promise<void>;
|
|
9
10
|
loading?: boolean;
|
|
10
11
|
};
|
|
11
12
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
label?: string;
|
|
3
|
+
variant?: 'empty-state';
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
|
+
declare const _default: typeof __VLS_export;
|
|
7
|
+
export default _default;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import ChatConversationNewGroupPopoverContent from "./conversation-new-group-popover-content.vue";
|
|
3
|
+
const props = defineProps({
|
|
4
|
+
label: { type: String, required: false },
|
|
5
|
+
variant: { type: String, required: false }
|
|
6
|
+
});
|
|
7
|
+
const { createGroup, error: groupCreationError } = useConversationGroups();
|
|
8
|
+
const isOpen = ref(false);
|
|
9
|
+
const groupName = ref("");
|
|
10
|
+
const isSaving = ref(false);
|
|
11
|
+
const creationErrorMessage = ref(null);
|
|
12
|
+
const popoverContentRef = ref(null);
|
|
13
|
+
const hasLabel = computed(() => Boolean(props.label?.trim()));
|
|
14
|
+
const isEmptyState = computed(() => props.variant === "empty-state");
|
|
15
|
+
const buttonClasses = computed(() => {
|
|
16
|
+
if (isEmptyState.value)
|
|
17
|
+
return "h-32px w-full gap-6px px-12px border-neutral-300 hover:bg-neutral-50";
|
|
18
|
+
if (hasLabel.value)
|
|
19
|
+
return "h-28px px-10px gap-6px self-start border-neutral-300 hover:bg-neutral-50";
|
|
20
|
+
return "h-32px w-40px border-neutral-300 hover:bg-neutral-50";
|
|
21
|
+
});
|
|
22
|
+
const iconClasses = computed(() => isEmptyState.value ? "text-neutral-900" : "text-neutral-700");
|
|
23
|
+
const labelClasses = computed(
|
|
24
|
+
() => isEmptyState.value ? "text-14px font-semibold text-neutral-900 leading-18px" : "text-13px font-medium text-neutral-700 leading-17px"
|
|
25
|
+
);
|
|
26
|
+
watch(isOpen, async (open) => {
|
|
27
|
+
if (!open) {
|
|
28
|
+
creationErrorMessage.value = null;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
await nextTick();
|
|
32
|
+
popoverContentRef.value?.focusInput?.();
|
|
33
|
+
});
|
|
34
|
+
watch(groupName, () => {
|
|
35
|
+
creationErrorMessage.value = null;
|
|
36
|
+
});
|
|
37
|
+
async function submitGroup() {
|
|
38
|
+
const name = groupName.value.trim();
|
|
39
|
+
if (!name || isSaving.value)
|
|
40
|
+
return;
|
|
41
|
+
creationErrorMessage.value = null;
|
|
42
|
+
isSaving.value = true;
|
|
43
|
+
const group = await createGroup(name);
|
|
44
|
+
isSaving.value = false;
|
|
45
|
+
if (!group) {
|
|
46
|
+
creationErrorMessage.value = groupCreationError.value || "Erro ao criar pasta";
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
groupName.value = "";
|
|
50
|
+
creationErrorMessage.value = null;
|
|
51
|
+
isOpen.value = false;
|
|
52
|
+
}
|
|
53
|
+
function cancelGroup() {
|
|
54
|
+
groupName.value = "";
|
|
55
|
+
creationErrorMessage.value = null;
|
|
56
|
+
isOpen.value = false;
|
|
57
|
+
}
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<template>
|
|
61
|
+
<TelaPopover v-model:open="isOpen">
|
|
62
|
+
<TelaPopoverTrigger as-child>
|
|
63
|
+
<button
|
|
64
|
+
type="button"
|
|
65
|
+
flex items-center justify-center
|
|
66
|
+
bg-white b=".5px solid"
|
|
67
|
+
rounded-10px
|
|
68
|
+
transition-colors
|
|
69
|
+
cursor-pointer
|
|
70
|
+
:class="buttonClasses"
|
|
71
|
+
:aria-label="label || 'Nova pasta'"
|
|
72
|
+
:title="label || 'Nova pasta'"
|
|
73
|
+
>
|
|
74
|
+
<span i-ph-folder-plus text-16px :class="iconClasses" />
|
|
75
|
+
<span v-if="hasLabel" :class="labelClasses">
|
|
76
|
+
{{ label }}
|
|
77
|
+
</span>
|
|
78
|
+
</button>
|
|
79
|
+
</TelaPopoverTrigger>
|
|
80
|
+
|
|
81
|
+
<ChatConversationNewGroupPopoverContent
|
|
82
|
+
ref="popoverContentRef"
|
|
83
|
+
v-model="groupName"
|
|
84
|
+
:error-message="creationErrorMessage"
|
|
85
|
+
:saving="isSaving"
|
|
86
|
+
@submit="submitGroup"
|
|
87
|
+
@cancel="cancelGroup"
|
|
88
|
+
/>
|
|
89
|
+
</TelaPopover>
|
|
90
|
+
</template>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
label?: string;
|
|
3
|
+
variant?: 'empty-state';
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
|
+
declare const _default: typeof __VLS_export;
|
|
7
|
+
export default _default;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
errorMessage?: string | null;
|
|
3
|
+
saving?: boolean;
|
|
4
|
+
};
|
|
5
|
+
declare function focusInput(): void;
|
|
6
|
+
type __VLS_ModelProps = {
|
|
7
|
+
modelValue: string;
|
|
8
|
+
};
|
|
9
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
10
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
11
|
+
focusInput: typeof focusInput;
|
|
12
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
13
|
+
"update:modelValue": (value: string) => any;
|
|
14
|
+
} & {
|
|
15
|
+
submit: () => any;
|
|
16
|
+
cancel: () => any;
|
|
17
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
18
|
+
onSubmit?: (() => any) | undefined;
|
|
19
|
+
onCancel?: (() => any) | undefined;
|
|
20
|
+
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
21
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
22
|
+
declare const _default: typeof __VLS_export;
|
|
23
|
+
export default _default;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
defineProps({
|
|
3
|
+
errorMessage: { type: [String, null], required: false },
|
|
4
|
+
saving: { type: Boolean, required: false }
|
|
5
|
+
});
|
|
6
|
+
const emit = defineEmits(["submit", "cancel"]);
|
|
7
|
+
const groupName = defineModel({ type: String, ...{ required: true } });
|
|
8
|
+
const inputRef = ref(null);
|
|
9
|
+
function focusInput() {
|
|
10
|
+
inputRef.value?.focus();
|
|
11
|
+
}
|
|
12
|
+
defineExpose({
|
|
13
|
+
focusInput
|
|
14
|
+
});
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<TelaPopoverContent
|
|
19
|
+
align="end"
|
|
20
|
+
:side-offset="4"
|
|
21
|
+
data-chat-sidebar-floating
|
|
22
|
+
class="!w-auto !rounded-12px !border-neutral-200 !bg-white !p-0 !text-neutral-900 !shadow-lg"
|
|
23
|
+
>
|
|
24
|
+
<form w-236px p-8px flex="~ col" gap-8px @submit.prevent="emit('submit')">
|
|
25
|
+
<input
|
|
26
|
+
ref="inputRef"
|
|
27
|
+
v-model="groupName"
|
|
28
|
+
type="text"
|
|
29
|
+
autocomplete="off"
|
|
30
|
+
maxlength="80"
|
|
31
|
+
placeholder="Nome da pasta"
|
|
32
|
+
aria-label="Nome da pasta"
|
|
33
|
+
h-32px w-full
|
|
34
|
+
px-10px
|
|
35
|
+
rounded-8px
|
|
36
|
+
b=".5px neutral-300"
|
|
37
|
+
bg-white
|
|
38
|
+
outline-none
|
|
39
|
+
text-14px text-neutral-900 leading-18px
|
|
40
|
+
focus:b-neutral-900
|
|
41
|
+
@keydown.escape.prevent.stop="emit('cancel')"
|
|
42
|
+
>
|
|
43
|
+
<p
|
|
44
|
+
v-if="errorMessage"
|
|
45
|
+
text-12px text-red-600 leading-16px
|
|
46
|
+
aria-live="polite"
|
|
47
|
+
>
|
|
48
|
+
{{ errorMessage }}
|
|
49
|
+
</p>
|
|
50
|
+
<div flex justify-end gap-6px>
|
|
51
|
+
<button
|
|
52
|
+
type="button"
|
|
53
|
+
h-28px px-10px rounded-8px
|
|
54
|
+
text-13px font-medium text-neutral-700
|
|
55
|
+
hover:bg-neutral-100
|
|
56
|
+
transition-colors
|
|
57
|
+
@click="emit('cancel')"
|
|
58
|
+
>
|
|
59
|
+
Cancelar
|
|
60
|
+
</button>
|
|
61
|
+
<button
|
|
62
|
+
type="submit"
|
|
63
|
+
h-28px px-10px rounded-8px
|
|
64
|
+
bg-neutral-900 text-white
|
|
65
|
+
text-13px font-medium
|
|
66
|
+
transition-opacity
|
|
67
|
+
:disabled="!groupName.trim() || saving"
|
|
68
|
+
:class="(!groupName.trim() || saving) && 'opacity-40 cursor-not-allowed'"
|
|
69
|
+
>
|
|
70
|
+
Criar
|
|
71
|
+
</button>
|
|
72
|
+
</div>
|
|
73
|
+
</form>
|
|
74
|
+
</TelaPopoverContent>
|
|
75
|
+
</template>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
errorMessage?: string | null;
|
|
3
|
+
saving?: boolean;
|
|
4
|
+
};
|
|
5
|
+
declare function focusInput(): void;
|
|
6
|
+
type __VLS_ModelProps = {
|
|
7
|
+
modelValue: string;
|
|
8
|
+
};
|
|
9
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
10
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
11
|
+
focusInput: typeof focusInput;
|
|
12
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
13
|
+
"update:modelValue": (value: string) => any;
|
|
14
|
+
} & {
|
|
15
|
+
submit: () => any;
|
|
16
|
+
cancel: () => any;
|
|
17
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
18
|
+
onSubmit?: (() => any) | undefined;
|
|
19
|
+
onCancel?: (() => any) | undefined;
|
|
20
|
+
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
21
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
22
|
+
declare const _default: typeof __VLS_export;
|
|
23
|
+
export default _default;
|