@meistrari/chat-nuxt 1.10.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/header.d.vue.ts +4 -4
- package/dist/runtime/components/chat/mobile/shell/header.vue.d.ts +4 -4
- package/dist/runtime/components/chat/mobile/shell/shell.d.vue.ts +20 -20
- package/dist/runtime/components/chat/mobile/shell/shell.vue +1 -1
- package/dist/runtime/components/chat/mobile/shell/shell.vue.d.ts +20 -20
- package/dist/runtime/components/chat/topbar.d.vue.ts +8 -8
- package/dist/runtime/components/chat/topbar.vue.d.ts +8 -8
- 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 +246 -107
- 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/embed/components/configuration/ChatConfigurationContextFilesTab.d.vue.ts +4 -4
- package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.vue.d.ts +4 -4
- package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.d.vue.ts +2 -2
- package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.vue.d.ts +2 -2
- 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/chat/conversations/metadata.get.d.ts +1 -0
- package/dist/runtime/server/api/chat/conversations/metadata.get.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/api/conversations/metadata.get.d.ts +2 -0
- package/dist/runtime/server/api/conversations/metadata.get.js +20 -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 +6 -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
|
@@ -50,7 +50,11 @@ type __VLS_Slots = {} & {
|
|
|
50
50
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
51
51
|
delete: () => any;
|
|
52
52
|
cancel: () => any;
|
|
53
|
-
|
|
53
|
+
rename: () => any;
|
|
54
|
+
duplicate: () => any;
|
|
55
|
+
export: () => any;
|
|
56
|
+
copyLink: () => any;
|
|
57
|
+
selectConversation: (id: string) => any;
|
|
54
58
|
send: (content: string, files: FileUploadInput[], model?: "claude-sonnet-4-5" | "claude-sonnet-4-6" | "claude-haiku-4-5" | "claude-opus-4-5" | "claude-opus-4-6" | undefined, agentInputs?: ({
|
|
55
59
|
type: "file";
|
|
56
60
|
name: string;
|
|
@@ -63,26 +67,26 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
|
|
|
63
67
|
content: string;
|
|
64
68
|
})[] | undefined) => any;
|
|
65
69
|
"update:model": (value: "claude-sonnet-4-5" | "claude-sonnet-4-6" | "claude-haiku-4-5" | "claude-opus-4-5" | "claude-opus-4-6") => any;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
settings: () => any;
|
|
71
|
+
"update:editTitle": (value: string) => any;
|
|
72
|
+
saveTitle: () => any;
|
|
73
|
+
cancelEditing: () => any;
|
|
70
74
|
debug: () => any;
|
|
71
75
|
tabChange: (tab: TopbarTab) => any;
|
|
72
|
-
newConversation: () => any;
|
|
76
|
+
newConversation: (groupId?: string | null | undefined) => any;
|
|
73
77
|
switchWorkspace: () => any;
|
|
74
78
|
signout: () => any;
|
|
75
|
-
cancelEditing: () => any;
|
|
76
79
|
dismissOutdatedBanner: () => any;
|
|
77
|
-
saveTitle: () => any;
|
|
78
|
-
selectConversation: (id: string) => any;
|
|
79
80
|
"update:drawerOpen": (value: boolean) => any;
|
|
80
|
-
"update:editTitle": (value: string) => any;
|
|
81
81
|
"update:workspaceSheetOpen": (value: boolean) => any;
|
|
82
82
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
83
83
|
onDelete?: (() => any) | undefined;
|
|
84
84
|
onCancel?: (() => any) | undefined;
|
|
85
|
-
|
|
85
|
+
onRename?: (() => any) | undefined;
|
|
86
|
+
onDuplicate?: (() => any) | undefined;
|
|
87
|
+
onExport?: (() => any) | undefined;
|
|
88
|
+
onCopyLink?: (() => any) | undefined;
|
|
89
|
+
onSelectConversation?: ((id: string) => any) | undefined;
|
|
86
90
|
onSend?: ((content: string, files: FileUploadInput[], model?: "claude-sonnet-4-5" | "claude-sonnet-4-6" | "claude-haiku-4-5" | "claude-opus-4-5" | "claude-opus-4-6" | undefined, agentInputs?: ({
|
|
87
91
|
type: "file";
|
|
88
92
|
name: string;
|
|
@@ -95,21 +99,17 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
|
|
|
95
99
|
content: string;
|
|
96
100
|
})[] | undefined) => any) | undefined;
|
|
97
101
|
"onUpdate:model"?: ((value: "claude-sonnet-4-5" | "claude-sonnet-4-6" | "claude-haiku-4-5" | "claude-opus-4-5" | "claude-opus-4-6") => any) | undefined;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
onSettings?: (() => any) | undefined;
|
|
103
|
+
"onUpdate:editTitle"?: ((value: string) => any) | undefined;
|
|
104
|
+
onSaveTitle?: (() => any) | undefined;
|
|
105
|
+
onCancelEditing?: (() => any) | undefined;
|
|
102
106
|
onDebug?: (() => any) | undefined;
|
|
103
107
|
onTabChange?: ((tab: TopbarTab) => any) | undefined;
|
|
104
|
-
onNewConversation?: (() => any) | undefined;
|
|
108
|
+
onNewConversation?: ((groupId?: string | null | undefined) => any) | undefined;
|
|
105
109
|
onSwitchWorkspace?: (() => any) | undefined;
|
|
106
110
|
onSignout?: (() => any) | undefined;
|
|
107
|
-
onCancelEditing?: (() => any) | undefined;
|
|
108
111
|
onDismissOutdatedBanner?: (() => any) | undefined;
|
|
109
|
-
onSaveTitle?: (() => any) | undefined;
|
|
110
|
-
onSelectConversation?: ((id: string) => any) | undefined;
|
|
111
112
|
"onUpdate:drawerOpen"?: ((value: boolean) => any) | undefined;
|
|
112
|
-
"onUpdate:editTitle"?: ((value: string) => any) | undefined;
|
|
113
113
|
"onUpdate:workspaceSheetOpen"?: ((value: boolean) => any) | undefined;
|
|
114
114
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
115
115
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -20,24 +20,24 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
20
20
|
duplicate: () => any;
|
|
21
21
|
export: () => any;
|
|
22
22
|
copyLink: () => any;
|
|
23
|
+
"update:isEditing": (value: boolean) => any;
|
|
24
|
+
"update:editTitle": (value: string) => any;
|
|
25
|
+
saveTitle: () => any;
|
|
26
|
+
cancelEditing: () => any;
|
|
23
27
|
debug: () => any;
|
|
24
28
|
tabChange: (tab: TopbarTab) => any;
|
|
25
|
-
cancelEditing: () => any;
|
|
26
|
-
saveTitle: () => any;
|
|
27
|
-
"update:editTitle": (value: string) => any;
|
|
28
|
-
"update:isEditing": (value: boolean) => any;
|
|
29
29
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
30
30
|
onDelete?: (() => any) | undefined;
|
|
31
31
|
onRename?: (() => any) | undefined;
|
|
32
32
|
onDuplicate?: (() => any) | undefined;
|
|
33
33
|
onExport?: (() => any) | undefined;
|
|
34
34
|
onCopyLink?: (() => any) | undefined;
|
|
35
|
+
"onUpdate:isEditing"?: ((value: boolean) => any) | undefined;
|
|
36
|
+
"onUpdate:editTitle"?: ((value: string) => any) | undefined;
|
|
37
|
+
onSaveTitle?: (() => any) | undefined;
|
|
38
|
+
onCancelEditing?: (() => any) | undefined;
|
|
35
39
|
onDebug?: (() => any) | undefined;
|
|
36
40
|
onTabChange?: ((tab: TopbarTab) => any) | undefined;
|
|
37
|
-
onCancelEditing?: (() => any) | undefined;
|
|
38
|
-
onSaveTitle?: (() => any) | undefined;
|
|
39
|
-
"onUpdate:editTitle"?: ((value: string) => any) | undefined;
|
|
40
|
-
"onUpdate:isEditing"?: ((value: boolean) => any) | undefined;
|
|
41
41
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
42
42
|
declare const _default: typeof __VLS_export;
|
|
43
43
|
export default _default;
|
|
@@ -20,24 +20,24 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
|
|
|
20
20
|
duplicate: () => any;
|
|
21
21
|
export: () => any;
|
|
22
22
|
copyLink: () => any;
|
|
23
|
+
"update:isEditing": (value: boolean) => any;
|
|
24
|
+
"update:editTitle": (value: string) => any;
|
|
25
|
+
saveTitle: () => any;
|
|
26
|
+
cancelEditing: () => any;
|
|
23
27
|
debug: () => any;
|
|
24
28
|
tabChange: (tab: TopbarTab) => any;
|
|
25
|
-
cancelEditing: () => any;
|
|
26
|
-
saveTitle: () => any;
|
|
27
|
-
"update:editTitle": (value: string) => any;
|
|
28
|
-
"update:isEditing": (value: boolean) => any;
|
|
29
29
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
30
30
|
onDelete?: (() => any) | undefined;
|
|
31
31
|
onRename?: (() => any) | undefined;
|
|
32
32
|
onDuplicate?: (() => any) | undefined;
|
|
33
33
|
onExport?: (() => any) | undefined;
|
|
34
34
|
onCopyLink?: (() => any) | undefined;
|
|
35
|
+
"onUpdate:isEditing"?: ((value: boolean) => any) | undefined;
|
|
36
|
+
"onUpdate:editTitle"?: ((value: string) => any) | undefined;
|
|
37
|
+
onSaveTitle?: (() => any) | undefined;
|
|
38
|
+
onCancelEditing?: (() => any) | undefined;
|
|
35
39
|
onDebug?: (() => any) | undefined;
|
|
36
40
|
onTabChange?: ((tab: TopbarTab) => any) | undefined;
|
|
37
|
-
onCancelEditing?: (() => any) | undefined;
|
|
38
|
-
onSaveTitle?: (() => any) | undefined;
|
|
39
|
-
"onUpdate:editTitle"?: ((value: string) => any) | undefined;
|
|
40
|
-
"onUpdate:isEditing"?: ((value: boolean) => any) | undefined;
|
|
41
41
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
42
42
|
declare const _default: typeof __VLS_export;
|
|
43
43
|
export default _default;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ConversationGroup } from '../types/chat.js';
|
|
2
|
+
export declare function useConversationGroups(): {
|
|
3
|
+
groups: any;
|
|
4
|
+
loading: any;
|
|
5
|
+
error: any;
|
|
6
|
+
fetchGroups: (options?: {
|
|
7
|
+
force?: boolean;
|
|
8
|
+
}) => Promise<void>;
|
|
9
|
+
createGroup: (name: string) => Promise<ConversationGroup | null>;
|
|
10
|
+
renameGroup: (groupId: string, name: string) => Promise<boolean>;
|
|
11
|
+
deleteGroup: (groupId: string) => Promise<boolean>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import * as Sentry from "@sentry/nuxt";
|
|
2
|
+
import { useChatAuth } from "#chat-auth";
|
|
3
|
+
import { resolveChatStateScope } from "../utils/tela-chat.js";
|
|
4
|
+
function sortConversationGroups(groups) {
|
|
5
|
+
return [...groups].sort((a, b) => {
|
|
6
|
+
const createdDelta = new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
|
7
|
+
if (createdDelta !== 0)
|
|
8
|
+
return createdDelta;
|
|
9
|
+
return a.name.localeCompare(b.name);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export function useConversationGroups() {
|
|
13
|
+
const chatApi = useChatApi();
|
|
14
|
+
const embedConfig = useEmbedConfig();
|
|
15
|
+
const { activeOrganization } = useChatAuth();
|
|
16
|
+
const groupBuckets = useState("conversation-groups", () => ({}));
|
|
17
|
+
const loadingBuckets = useState("conversation-groups-loading", () => ({}));
|
|
18
|
+
const fetchedBuckets = useState("conversation-groups-fetched", () => ({}));
|
|
19
|
+
const errorBuckets = useState("conversation-groups-error", () => ({}));
|
|
20
|
+
const runtimeScope = computed(() => resolveChatStateScope(
|
|
21
|
+
embedConfig?.workspaceId.value || activeOrganization.value?.id,
|
|
22
|
+
embedConfig?.telaAgentId.value,
|
|
23
|
+
embedConfig?.conversationScope?.value
|
|
24
|
+
));
|
|
25
|
+
const groups = computed({
|
|
26
|
+
get: () => groupBuckets.value[runtimeScope.value] ?? [],
|
|
27
|
+
set: (value) => {
|
|
28
|
+
groupBuckets.value = {
|
|
29
|
+
...groupBuckets.value,
|
|
30
|
+
[runtimeScope.value]: sortConversationGroups(value)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const loading = computed({
|
|
35
|
+
get: () => loadingBuckets.value[runtimeScope.value] ?? false,
|
|
36
|
+
set: (value) => {
|
|
37
|
+
loadingBuckets.value = {
|
|
38
|
+
...loadingBuckets.value,
|
|
39
|
+
[runtimeScope.value]: value
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
const error = computed({
|
|
44
|
+
get: () => errorBuckets.value[runtimeScope.value] ?? null,
|
|
45
|
+
set: (value) => {
|
|
46
|
+
errorBuckets.value = {
|
|
47
|
+
...errorBuckets.value,
|
|
48
|
+
[runtimeScope.value]: value
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
function setFetched(value) {
|
|
53
|
+
fetchedBuckets.value = {
|
|
54
|
+
...fetchedBuckets.value,
|
|
55
|
+
[runtimeScope.value]: value
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function upsertGroup(group) {
|
|
59
|
+
groups.value = [
|
|
60
|
+
group,
|
|
61
|
+
...groups.value.filter((item) => item.id !== group.id)
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
function removeGroup(groupId) {
|
|
65
|
+
groups.value = groups.value.filter((group) => group.id !== groupId);
|
|
66
|
+
}
|
|
67
|
+
const fetchGroups = async (options = {}) => {
|
|
68
|
+
if (loading.value || fetchedBuckets.value[runtimeScope.value] && !options.force) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
loading.value = true;
|
|
72
|
+
error.value = null;
|
|
73
|
+
try {
|
|
74
|
+
groups.value = await $fetch(
|
|
75
|
+
chatApi.path("/conversations/groups"),
|
|
76
|
+
chatApi.withChatHeaders()
|
|
77
|
+
);
|
|
78
|
+
setFetched(true);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
error.value = err instanceof Error ? err.message : "Erro ao carregar pastas";
|
|
81
|
+
Sentry.captureException(err);
|
|
82
|
+
} finally {
|
|
83
|
+
loading.value = false;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const createGroup = async (name) => {
|
|
87
|
+
error.value = null;
|
|
88
|
+
try {
|
|
89
|
+
const group = await $fetch(
|
|
90
|
+
chatApi.path("/conversations/groups"),
|
|
91
|
+
chatApi.withChatHeaders({
|
|
92
|
+
method: "POST",
|
|
93
|
+
body: { name }
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
upsertGroup(group);
|
|
97
|
+
setFetched(true);
|
|
98
|
+
return group;
|
|
99
|
+
} catch (err) {
|
|
100
|
+
error.value = err instanceof Error ? err.message : "Erro ao criar pasta";
|
|
101
|
+
Sentry.captureException(err);
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const renameGroup = async (groupId, name) => {
|
|
106
|
+
error.value = null;
|
|
107
|
+
const previousGroups = groups.value;
|
|
108
|
+
const group = groups.value.find((item) => item.id === groupId);
|
|
109
|
+
if (!group)
|
|
110
|
+
return false;
|
|
111
|
+
upsertGroup({ ...group, name, updatedAt: /* @__PURE__ */ new Date() });
|
|
112
|
+
try {
|
|
113
|
+
const updated = await $fetch(
|
|
114
|
+
chatApi.path(`/conversations/groups/${groupId}`),
|
|
115
|
+
chatApi.withChatHeaders({
|
|
116
|
+
method: "PATCH",
|
|
117
|
+
body: { name }
|
|
118
|
+
})
|
|
119
|
+
);
|
|
120
|
+
upsertGroup(updated);
|
|
121
|
+
return true;
|
|
122
|
+
} catch (err) {
|
|
123
|
+
groups.value = previousGroups;
|
|
124
|
+
error.value = err instanceof Error ? err.message : "Erro ao renomear pasta";
|
|
125
|
+
Sentry.captureException(err);
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const deleteGroup = async (groupId) => {
|
|
130
|
+
error.value = null;
|
|
131
|
+
const previousGroups = groups.value;
|
|
132
|
+
const { clearGroupFromConversations } = useConversations();
|
|
133
|
+
removeGroup(groupId);
|
|
134
|
+
const restoreConversations = clearGroupFromConversations(groupId);
|
|
135
|
+
try {
|
|
136
|
+
await $fetch(
|
|
137
|
+
chatApi.path(`/conversations/groups/${groupId}`),
|
|
138
|
+
chatApi.withChatHeaders({
|
|
139
|
+
method: "DELETE"
|
|
140
|
+
})
|
|
141
|
+
);
|
|
142
|
+
return true;
|
|
143
|
+
} catch (err) {
|
|
144
|
+
groups.value = previousGroups;
|
|
145
|
+
restoreConversations();
|
|
146
|
+
error.value = err instanceof Error ? err.message : "Erro ao excluir pasta";
|
|
147
|
+
Sentry.captureException(err);
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
groups: readonly(groups),
|
|
153
|
+
loading: readonly(loading),
|
|
154
|
+
error: readonly(error),
|
|
155
|
+
fetchGroups,
|
|
156
|
+
createGroup,
|
|
157
|
+
renameGroup,
|
|
158
|
+
deleteGroup
|
|
159
|
+
};
|
|
160
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { DeepReadonly } from 'vue';
|
|
2
|
+
import type { Conversation } from '../types/chat.js';
|
|
3
|
+
export type ConversationItemOptions = {
|
|
4
|
+
conversation: DeepReadonly<Conversation>;
|
|
5
|
+
active: boolean;
|
|
6
|
+
draggable?: boolean;
|
|
7
|
+
userImage?: string;
|
|
8
|
+
selectConversation?: (id: string) => void | Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
export type ConversationItemEmit = {
|
|
11
|
+
(e: 'delete', id: string): void;
|
|
12
|
+
(e: 'rename', id: string, title: string): void;
|
|
13
|
+
(e: 'duplicate', id: string): void;
|
|
14
|
+
(e: 'export', id: string): void;
|
|
15
|
+
(e: 'dragStart', id: string): void;
|
|
16
|
+
(e: 'dragEnd'): void;
|
|
17
|
+
};
|
|
18
|
+
export declare function useConversationItem(options: ConversationItemOptions, emit: ConversationItemEmit): {
|
|
19
|
+
isRenamingInline: any;
|
|
20
|
+
renameTitle: any;
|
|
21
|
+
renameInputRef: any;
|
|
22
|
+
showDeleteModal: any;
|
|
23
|
+
menuButtonRef: any;
|
|
24
|
+
isMenuOpen: any;
|
|
25
|
+
conversationPath: any;
|
|
26
|
+
displayTitle: any;
|
|
27
|
+
menuItems: ({
|
|
28
|
+
label: string;
|
|
29
|
+
icon: string;
|
|
30
|
+
click: () => void;
|
|
31
|
+
color?: undefined;
|
|
32
|
+
} | {
|
|
33
|
+
label: string;
|
|
34
|
+
icon: string;
|
|
35
|
+
color: "negative";
|
|
36
|
+
click: () => boolean;
|
|
37
|
+
})[];
|
|
38
|
+
confirmDelete: () => void;
|
|
39
|
+
cancelDelete: () => void;
|
|
40
|
+
handleConversationClick: (event: MouseEvent) => void;
|
|
41
|
+
startRename: () => void;
|
|
42
|
+
handleConversationDoubleClick: () => void;
|
|
43
|
+
handleConversationDragStart: (event: DragEvent) => void;
|
|
44
|
+
handleConversationDragEnd: () => void;
|
|
45
|
+
cancelRename: () => void;
|
|
46
|
+
submitRename: () => void;
|
|
47
|
+
handleRenameBlur: () => void;
|
|
48
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { useMagicKeys, useMutationObserver } from "@vueuse/core";
|
|
2
|
+
import { resolveConversationRenameTitle } from "../utils/conversation-title.js";
|
|
3
|
+
import { capitalizeFirst } from "../utils/string.js";
|
|
4
|
+
const conversationNavigationDelayMs = 250;
|
|
5
|
+
export function useConversationItem(options, emit) {
|
|
6
|
+
const isRenamingInline = ref(false);
|
|
7
|
+
const renameTitle = ref("");
|
|
8
|
+
const renameInputRef = ref(null);
|
|
9
|
+
const isIgnoringInitialRenameBlur = ref(false);
|
|
10
|
+
const showDeleteModal = ref(false);
|
|
11
|
+
const menuButtonRef = ref(null);
|
|
12
|
+
const isMenuOpen = ref(false);
|
|
13
|
+
const conversationPath = computed(() => `/${options.conversation.id}`);
|
|
14
|
+
const displayTitle = computed(() => capitalizeFirst(options.conversation.title));
|
|
15
|
+
const { escape } = useMagicKeys();
|
|
16
|
+
let conversationNavigationTimer = null;
|
|
17
|
+
let renameBlurTimer = null;
|
|
18
|
+
watch(() => escape?.value, (pressed) => {
|
|
19
|
+
if (pressed && isRenamingInline.value) {
|
|
20
|
+
cancelRename();
|
|
21
|
+
}
|
|
22
|
+
if (pressed && showDeleteModal.value) {
|
|
23
|
+
cancelDelete();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
useMutationObserver(menuButtonRef, (mutations) => {
|
|
27
|
+
for (const mutation of mutations) {
|
|
28
|
+
if (mutation.attributeName === "data-state") {
|
|
29
|
+
isMenuOpen.value = menuButtonRef.value?.getAttribute("data-state") === "open";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}, { attributes: true });
|
|
33
|
+
function clearPendingConversationNavigation() {
|
|
34
|
+
if (conversationNavigationTimer === null)
|
|
35
|
+
return;
|
|
36
|
+
clearTimeout(conversationNavigationTimer);
|
|
37
|
+
conversationNavigationTimer = null;
|
|
38
|
+
}
|
|
39
|
+
function clearRenameBlurTimer() {
|
|
40
|
+
if (renameBlurTimer === null)
|
|
41
|
+
return;
|
|
42
|
+
clearTimeout(renameBlurTimer);
|
|
43
|
+
renameBlurTimer = null;
|
|
44
|
+
}
|
|
45
|
+
function allowRenameBlurSoon() {
|
|
46
|
+
clearRenameBlurTimer();
|
|
47
|
+
renameBlurTimer = setTimeout(() => {
|
|
48
|
+
isIgnoringInitialRenameBlur.value = false;
|
|
49
|
+
renameBlurTimer = null;
|
|
50
|
+
}, 100);
|
|
51
|
+
}
|
|
52
|
+
function confirmDelete() {
|
|
53
|
+
showDeleteModal.value = false;
|
|
54
|
+
emit("delete", options.conversation.id);
|
|
55
|
+
}
|
|
56
|
+
function cancelDelete() {
|
|
57
|
+
showDeleteModal.value = false;
|
|
58
|
+
}
|
|
59
|
+
function handleConversationClick(event) {
|
|
60
|
+
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
|
|
61
|
+
return;
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
clearPendingConversationNavigation();
|
|
64
|
+
if (event.detail > 1)
|
|
65
|
+
return;
|
|
66
|
+
conversationNavigationTimer = setTimeout(() => {
|
|
67
|
+
if (options.selectConversation) {
|
|
68
|
+
void options.selectConversation(options.conversation.id);
|
|
69
|
+
} else {
|
|
70
|
+
navigateTo(conversationPath.value);
|
|
71
|
+
}
|
|
72
|
+
conversationNavigationTimer = null;
|
|
73
|
+
}, conversationNavigationDelayMs);
|
|
74
|
+
}
|
|
75
|
+
async function focusRenameInput() {
|
|
76
|
+
await nextTick();
|
|
77
|
+
renameInputRef.value?.focus();
|
|
78
|
+
renameInputRef.value?.select();
|
|
79
|
+
allowRenameBlurSoon();
|
|
80
|
+
}
|
|
81
|
+
function startRename() {
|
|
82
|
+
isMenuOpen.value = false;
|
|
83
|
+
clearPendingConversationNavigation();
|
|
84
|
+
clearRenameBlurTimer();
|
|
85
|
+
renameTitle.value = options.conversation.title;
|
|
86
|
+
isIgnoringInitialRenameBlur.value = true;
|
|
87
|
+
isRenamingInline.value = true;
|
|
88
|
+
void focusRenameInput();
|
|
89
|
+
}
|
|
90
|
+
function handleConversationDoubleClick() {
|
|
91
|
+
clearPendingConversationNavigation();
|
|
92
|
+
startRename();
|
|
93
|
+
}
|
|
94
|
+
function handleConversationDragStart(event) {
|
|
95
|
+
if (!options.draggable)
|
|
96
|
+
return;
|
|
97
|
+
event.dataTransfer?.setData("text/plain", options.conversation.id);
|
|
98
|
+
if (event.dataTransfer) {
|
|
99
|
+
event.dataTransfer.effectAllowed = "move";
|
|
100
|
+
}
|
|
101
|
+
emit("dragStart", options.conversation.id);
|
|
102
|
+
}
|
|
103
|
+
function handleConversationDragEnd() {
|
|
104
|
+
emit("dragEnd");
|
|
105
|
+
}
|
|
106
|
+
function cancelRename() {
|
|
107
|
+
isRenamingInline.value = false;
|
|
108
|
+
isIgnoringInitialRenameBlur.value = false;
|
|
109
|
+
renameTitle.value = "";
|
|
110
|
+
clearRenameBlurTimer();
|
|
111
|
+
}
|
|
112
|
+
function submitRename() {
|
|
113
|
+
if (!isRenamingInline.value)
|
|
114
|
+
return;
|
|
115
|
+
const title = resolveConversationRenameTitle(renameTitle.value, options.conversation.title);
|
|
116
|
+
if (!title) {
|
|
117
|
+
cancelRename();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
emit("rename", options.conversation.id, title);
|
|
121
|
+
cancelRename();
|
|
122
|
+
}
|
|
123
|
+
function handleRenameBlur() {
|
|
124
|
+
if (isIgnoringInitialRenameBlur.value)
|
|
125
|
+
return;
|
|
126
|
+
submitRename();
|
|
127
|
+
}
|
|
128
|
+
const menuItems = [
|
|
129
|
+
{
|
|
130
|
+
label: "Renomear",
|
|
131
|
+
icon: "i-ph-pencil-simple",
|
|
132
|
+
click: () => startRename()
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
label: "Duplicar",
|
|
136
|
+
icon: "i-ph-copy",
|
|
137
|
+
click: () => emit("duplicate", options.conversation.id)
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
label: "Exportar",
|
|
141
|
+
icon: "i-ph-download-simple",
|
|
142
|
+
click: () => emit("export", options.conversation.id)
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
label: "Excluir",
|
|
146
|
+
icon: "i-ph-trash",
|
|
147
|
+
color: "negative",
|
|
148
|
+
click: () => showDeleteModal.value = true
|
|
149
|
+
}
|
|
150
|
+
];
|
|
151
|
+
onBeforeUnmount(() => {
|
|
152
|
+
clearPendingConversationNavigation();
|
|
153
|
+
clearRenameBlurTimer();
|
|
154
|
+
});
|
|
155
|
+
return {
|
|
156
|
+
isRenamingInline,
|
|
157
|
+
renameTitle,
|
|
158
|
+
renameInputRef,
|
|
159
|
+
showDeleteModal,
|
|
160
|
+
menuButtonRef,
|
|
161
|
+
isMenuOpen,
|
|
162
|
+
conversationPath,
|
|
163
|
+
displayTitle,
|
|
164
|
+
menuItems,
|
|
165
|
+
confirmDelete,
|
|
166
|
+
cancelDelete,
|
|
167
|
+
handleConversationClick,
|
|
168
|
+
startRename,
|
|
169
|
+
handleConversationDoubleClick,
|
|
170
|
+
handleConversationDragStart,
|
|
171
|
+
handleConversationDragEnd,
|
|
172
|
+
cancelRename,
|
|
173
|
+
submitRename,
|
|
174
|
+
handleRenameBlur
|
|
175
|
+
};
|
|
176
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { DeepReadonly } from 'vue';
|
|
2
|
+
import type { Conversation } from '../types/chat.js';
|
|
3
|
+
export type ConversationListOptions = {
|
|
4
|
+
conversations: DeepReadonly<Conversation[]>;
|
|
5
|
+
currentId?: string;
|
|
6
|
+
getMemberImage?: (userId: string) => string | undefined;
|
|
7
|
+
selectConversation?: (id: string) => void | Promise<void>;
|
|
8
|
+
resetConversation?: () => void | Promise<void>;
|
|
9
|
+
startConversation?: (groupId?: string | null) => void | Promise<void>;
|
|
10
|
+
loading?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type GroupDeleteTarget = {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
conversationIds: string[];
|
|
16
|
+
conversationCount: number;
|
|
17
|
+
};
|
|
18
|
+
export declare function useConversationList(options: ConversationListOptions): {
|
|
19
|
+
conversationSearch: any;
|
|
20
|
+
groupedConversations: any;
|
|
21
|
+
hasConversationSearchQuery: any;
|
|
22
|
+
hasConversationContent: any;
|
|
23
|
+
shouldShowSkeleton: any;
|
|
24
|
+
emptyConversationMessage: any;
|
|
25
|
+
skeletonRows: readonly [{
|
|
26
|
+
readonly id: "row-1";
|
|
27
|
+
readonly width: "72%";
|
|
28
|
+
}, {
|
|
29
|
+
readonly id: "row-2";
|
|
30
|
+
readonly width: "54%";
|
|
31
|
+
}, {
|
|
32
|
+
readonly id: "row-3";
|
|
33
|
+
readonly width: "64%";
|
|
34
|
+
}, {
|
|
35
|
+
readonly id: "row-4";
|
|
36
|
+
readonly width: "46%";
|
|
37
|
+
}, {
|
|
38
|
+
readonly id: "row-5";
|
|
39
|
+
readonly width: "78%";
|
|
40
|
+
}, {
|
|
41
|
+
readonly id: "row-6";
|
|
42
|
+
readonly width: "58%";
|
|
43
|
+
}, {
|
|
44
|
+
readonly id: "row-7";
|
|
45
|
+
readonly width: "69%";
|
|
46
|
+
}, {
|
|
47
|
+
readonly id: "row-8";
|
|
48
|
+
readonly width: "51%";
|
|
49
|
+
}, {
|
|
50
|
+
readonly id: "row-9";
|
|
51
|
+
readonly width: "74%";
|
|
52
|
+
}, {
|
|
53
|
+
readonly id: "row-10";
|
|
54
|
+
readonly width: "62%";
|
|
55
|
+
}];
|
|
56
|
+
groupItems: any;
|
|
57
|
+
ungroupedConversations: any;
|
|
58
|
+
ungroupedDropTarget: any;
|
|
59
|
+
ungroupedConversationLabel: any;
|
|
60
|
+
shouldShowUngroupedHeader: any;
|
|
61
|
+
showGroupCreationPrompt: any;
|
|
62
|
+
showDeleteGroupModal: any;
|
|
63
|
+
showDeleteGroupConversationsModal: any;
|
|
64
|
+
groupActionLoading: any;
|
|
65
|
+
deleteGroupTarget: any;
|
|
66
|
+
deleteGroupConversationsTarget: any;
|
|
67
|
+
renamingGroupId: any;
|
|
68
|
+
renameGroupName: any;
|
|
69
|
+
renameGroupError: any;
|
|
70
|
+
deleteGroupConfirmationMessage: any;
|
|
71
|
+
deleteGroupConversationsConfirmationMessage: any;
|
|
72
|
+
isGroupOpen: (groupId: string) => any;
|
|
73
|
+
setGroupOpen: (groupId: string, open: boolean) => void;
|
|
74
|
+
toggleGroup: (groupId: string) => void;
|
|
75
|
+
openDeleteGroupModal: (groupId: string, groupName: string, conversations: readonly Pick<Conversation, "id">[]) => void;
|
|
76
|
+
openDeleteGroupConversationsModal: (groupId: string, groupName: string, conversations: readonly Pick<Conversation, "id">[]) => void;
|
|
77
|
+
startRenameGroup: (groupId: string, groupName: string) => void;
|
|
78
|
+
setRenameGroupInputRef: (element: unknown, groupId: string) => void;
|
|
79
|
+
getGroupMenuItems: (groupId: string, groupName: string, conversations: readonly Pick<Conversation, "id">[]) => ({
|
|
80
|
+
label: string;
|
|
81
|
+
icon: string;
|
|
82
|
+
click: () => void;
|
|
83
|
+
color?: undefined;
|
|
84
|
+
disabled?: undefined;
|
|
85
|
+
} | {
|
|
86
|
+
label: string;
|
|
87
|
+
icon: string;
|
|
88
|
+
color: "negative";
|
|
89
|
+
click: () => void;
|
|
90
|
+
disabled?: undefined;
|
|
91
|
+
} | {
|
|
92
|
+
label: string;
|
|
93
|
+
icon: string;
|
|
94
|
+
color: "negative";
|
|
95
|
+
disabled: boolean;
|
|
96
|
+
click: () => void;
|
|
97
|
+
})[];
|
|
98
|
+
clearDeleteGroupTarget: () => void;
|
|
99
|
+
clearDeleteGroupConversationsTarget: () => void;
|
|
100
|
+
cancelRenameGroup: () => void;
|
|
101
|
+
formatConversationCount: (count: number) => string;
|
|
102
|
+
groupConversationsMoveMessage: (count: number) => "A conversa desta pasta será movida para Conversas." | "As conversas desta pasta serão movidas para Conversas.";
|
|
103
|
+
confirmDeleteGroup: () => Promise<void>;
|
|
104
|
+
confirmDeleteGroupConversations: () => Promise<void>;
|
|
105
|
+
submitRenameGroup: () => Promise<void>;
|
|
106
|
+
handleRenameGroupBlur: () => void;
|
|
107
|
+
handleDelete: (id: string) => Promise<void>;
|
|
108
|
+
handleRename: (id: string, title: string) => Promise<void>;
|
|
109
|
+
handleDuplicate: (id: string) => Promise<void>;
|
|
110
|
+
handleExport: (id: string) => Promise<void>;
|
|
111
|
+
handleConversationDragStart: (id: string) => void;
|
|
112
|
+
clearDragState: () => void;
|
|
113
|
+
handleDragEnter: (groupId: string | null) => void;
|
|
114
|
+
handleDragOver: (event: DragEvent, groupId: string | null) => void;
|
|
115
|
+
handleDragLeave: (event: DragEvent, groupId: string | null) => void;
|
|
116
|
+
isDropTarget: (groupId: string | null) => boolean;
|
|
117
|
+
getConversationItemProps: (conversation: DeepReadonly<Conversation>) => {
|
|
118
|
+
conversation: {
|
|
119
|
+
readonly [x: string]: any;
|
|
120
|
+
};
|
|
121
|
+
active: boolean;
|
|
122
|
+
draggable: boolean;
|
|
123
|
+
userImage: string | undefined;
|
|
124
|
+
selectConversation: ((id: string) => void | Promise<void>) | undefined;
|
|
125
|
+
};
|
|
126
|
+
handleConversationDrop: (event: DragEvent, groupId: string | null) => Promise<void>;
|
|
127
|
+
startConversationInGroup: (groupId: string) => void;
|
|
128
|
+
};
|