@messenger-box/tailwind-ui-inbox 10.0.3-alpha.71 → 10.0.3-alpha.73
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/CHANGELOG.md +8 -0
- package/lib/components/AIAgent/AIAgent.d.ts +14 -0
- package/lib/components/AIAgent/AIAgent.d.ts.map +1 -0
- package/lib/components/AIAgent/AIAgent.js +1148 -0
- package/lib/components/AIAgent/AIAgent.js.map +1 -0
- package/lib/components/AIAgent/index.d.ts +2 -0
- package/lib/components/AIAgent/index.d.ts.map +1 -0
- package/lib/components/InboxMessage/InputComponent.d.ts +9 -0
- package/lib/components/InboxMessage/InputComponent.d.ts.map +1 -0
- package/lib/components/InboxMessage/InputComponent.js +210 -0
- package/lib/components/InboxMessage/InputComponent.js.map +1 -0
- package/lib/components/InboxMessage/MessageInput.d.ts.map +1 -1
- package/lib/components/InboxMessage/MessageInput.js +14 -10
- package/lib/components/InboxMessage/MessageInput.js.map +1 -1
- package/lib/components/InboxMessage/MessageInputComponent.d.ts +9 -0
- package/lib/components/InboxMessage/MessageInputComponent.d.ts.map +1 -0
- package/lib/components/InboxMessage/Messages.d.ts.map +1 -1
- package/lib/components/InboxMessage/Messages.js +4 -54
- package/lib/components/InboxMessage/Messages.js.map +1 -1
- package/lib/components/InboxMessage/MessagesBuilderUi.d.ts +17 -0
- package/lib/components/InboxMessage/MessagesBuilderUi.d.ts.map +1 -0
- package/lib/components/InboxMessage/UploadImageButton.d.ts +1 -0
- package/lib/components/InboxMessage/UploadImageButton.d.ts.map +1 -1
- package/lib/components/InboxMessage/UploadImageButton.js +3 -3
- package/lib/components/InboxMessage/UploadImageButton.js.map +1 -1
- package/lib/components/InboxMessage/index.d.ts +3 -0
- package/lib/components/InboxMessage/index.d.ts.map +1 -1
- package/lib/components/InboxMessage/message-widgets/CommonMessage.d.ts.map +1 -1
- package/lib/components/InboxMessage/message-widgets/CommonMessage.js +11 -6
- package/lib/components/InboxMessage/message-widgets/CommonMessage.js.map +1 -1
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts +14 -0
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts.map +1 -0
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js +1525 -0
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js.map +1 -0
- package/lib/components/InboxMessage/message-widgets/PlainMessage.d.ts.map +1 -1
- package/lib/components/InboxMessage/message-widgets/PlainMessage.js +6 -3
- package/lib/components/InboxMessage/message-widgets/PlainMessage.js.map +1 -1
- package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.d.ts.map +1 -1
- package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.js +207 -12
- package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.js.map +1 -1
- package/lib/components/InboxMessage/message-widgets/index.d.ts +1 -0
- package/lib/components/InboxMessage/message-widgets/index.d.ts.map +1 -1
- package/lib/components/index.d.ts +2 -1
- package/lib/components/index.d.ts.map +1 -1
- package/lib/compute.d.ts.map +1 -1
- package/lib/compute.js +79 -3
- package/lib/compute.js.map +1 -1
- package/lib/config/env-config.d.ts +6 -0
- package/lib/config/env-config.d.ts.map +1 -1
- package/lib/config/env-config.js +19 -1
- package/lib/config/env-config.js.map +1 -1
- package/lib/container/AiInbox.d.ts +15 -0
- package/lib/container/AiInbox.d.ts.map +1 -0
- package/lib/container/AiInboxWithLoader.d.ts +36 -0
- package/lib/container/AiInboxWithLoader.d.ts.map +1 -0
- package/lib/container/AiLandingInput.d.ts +4 -0
- package/lib/container/AiLandingInput.d.ts.map +1 -0
- package/lib/container/AiLandingInput.js +164 -0
- package/lib/container/AiLandingInput.js.map +1 -0
- package/lib/container/Inbox.d.ts.map +1 -1
- package/lib/container/Inbox.js +6 -4
- package/lib/container/Inbox.js.map +1 -1
- package/lib/container/InboxAiMessagesLoader.d.ts +36 -0
- package/lib/container/InboxAiMessagesLoader.d.ts.map +1 -0
- package/lib/container/InboxAiMessagesLoader.js +44 -0
- package/lib/container/InboxAiMessagesLoader.js.map +1 -0
- package/lib/container/InboxContainer.d.ts +12 -0
- package/lib/container/InboxContainer.d.ts.map +1 -0
- package/lib/container/InboxContainer.js +31 -0
- package/lib/container/InboxContainer.js.map +1 -0
- package/lib/container/InboxTemplate1.d.ts +15 -0
- package/lib/container/InboxTemplate1.d.ts.map +1 -0
- package/lib/container/InboxTemplate1WithLoader.d.ts +36 -0
- package/lib/container/InboxTemplate1WithLoader.d.ts.map +1 -0
- package/lib/container/InboxTemplate2.d.ts +15 -0
- package/lib/container/InboxTemplate2.d.ts.map +1 -0
- package/lib/container/InboxWithAiLoader.d.ts +15 -0
- package/lib/container/InboxWithAiLoader.d.ts.map +1 -0
- package/lib/container/InboxWithAiLoader.js +56 -0
- package/lib/container/InboxWithAiLoader.js.map +1 -0
- package/lib/container/ServiceInbox.js +1 -1
- package/lib/container/ServiceInbox.js.map +1 -1
- package/lib/container/ThreadMessages.js +1 -1
- package/lib/container/ThreadMessages.js.map +1 -1
- package/lib/container/ThreadMessagesInbox.js +1 -1
- package/lib/container/ThreadMessagesInbox.js.map +1 -1
- package/lib/container/Threads.js +1 -1
- package/lib/container/Threads.js.map +1 -1
- package/lib/container/index.d.ts +4 -1
- package/lib/container/index.d.ts.map +1 -1
- package/lib/index.d.ts +3 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/machines/aiAgentMachine.d.ts +3 -0
- package/lib/machines/aiAgentMachine.d.ts.map +1 -0
- package/lib/machines/aiAgentMachine.js +1040 -0
- package/lib/machines/aiAgentMachine.js.map +1 -0
- package/lib/machines/types.d.ts +77 -0
- package/lib/machines/types.d.ts.map +1 -0
- package/lib/routes.json +40 -0
- package/lib/templates/InboxWithAi.d.ts +15 -0
- package/lib/templates/InboxWithAi.d.ts.map +1 -0
- package/lib/templates/InboxWithAi.js +405 -0
- package/lib/templates/InboxWithAi.js.map +1 -0
- package/lib/templates/InboxWithAi.tsx +502 -0
- package/lib/templates/index.d.ts +2 -0
- package/lib/templates/index.d.ts.map +1 -0
- package/lib/templates/index.ts +1 -0
- package/package.json +7 -5
- package/src/components/AIAgent/AIAgent.tsx +1351 -0
- package/src/components/AIAgent/README.md +82 -0
- package/src/components/AIAgent/index.ts +1 -0
- package/src/components/InboxMessage/InputComponent.tsx +263 -0
- package/src/components/InboxMessage/MessageInput.tsx +73 -66
- package/src/components/InboxMessage/MessageInputComponent.tsx +245 -0
- package/src/components/InboxMessage/Messages.tsx +2 -56
- package/src/components/InboxMessage/MessagesBuilderUi.tsx +205 -0
- package/src/components/InboxMessage/UploadImageButton.tsx +3 -2
- package/src/components/InboxMessage/index.ts +3 -0
- package/src/components/InboxMessage/message-widgets/CommonMessage.tsx +39 -21
- package/src/components/InboxMessage/message-widgets/ModernMessageGroup.tsx +1968 -0
- package/src/components/InboxMessage/message-widgets/PlainMessage.tsx +6 -2
- package/src/components/InboxMessage/message-widgets/SlackLikeMessageGroup.tsx +306 -54
- package/src/components/InboxMessage/message-widgets/index.ts +1 -0
- package/src/components/index.ts +4 -0
- package/src/compute.ts +83 -2
- package/src/config/env-config.ts +6 -0
- package/src/container/AiInbox.tsx +1796 -0
- package/src/container/AiInboxWithLoader.tsx +356 -0
- package/src/container/AiLandingInput.tsx +168 -0
- package/src/container/Inbox.tsx +8 -5
- package/src/container/InboxAiMessagesLoader.tsx +58 -0
- package/src/container/InboxContainer.tsx +35 -0
- package/src/container/InboxTemplate1.tsx +1542 -0
- package/src/container/InboxTemplate1WithLoader.tsx +338 -0
- package/src/container/InboxTemplate2.tsx +1606 -0
- package/src/container/InboxWithAiLoader.tsx +76 -0
- package/src/container/index.ts +21 -1
- package/src/index.ts +12 -1
- package/src/machines/aiAgentMachine.ts +1248 -0
- package/src/machines/types.ts +59 -0
- package/src/templates/InboxWithAi.tsx +502 -0
- package/src/templates/index.ts +1 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import React, { useMemo, useCallback } from 'react';
|
|
2
|
+
import { useParams, useLocation } from '@remix-run/react';
|
|
3
|
+
import {
|
|
4
|
+
useGetChannelsByUserQuery,
|
|
5
|
+
useGetChannelsByUserWithLastMessageQuery,
|
|
6
|
+
useMessagesQuery,
|
|
7
|
+
useViewChannelDetailQuery,
|
|
8
|
+
} from 'common/graphql';
|
|
9
|
+
import { RoomType } from 'common';
|
|
10
|
+
import { config } from '../config';
|
|
11
|
+
// import Inbox from './Inbox';
|
|
12
|
+
import Inbox from './AiInbox';
|
|
13
|
+
import AiInbox from './AiInbox';
|
|
14
|
+
import InboxTemplate1 from './InboxTemplate1';
|
|
15
|
+
import InboxTemplate2 from './InboxTemplate2';
|
|
16
|
+
const { MESSAGES_PER_PAGE } = config;
|
|
17
|
+
|
|
18
|
+
// Enhanced query parameters generator with better typing and flexibility
|
|
19
|
+
export const queryParamsGenerator = (params: {
|
|
20
|
+
role?: string;
|
|
21
|
+
criteria?: Record<string, any>;
|
|
22
|
+
supportServices?: boolean;
|
|
23
|
+
orgName?: string;
|
|
24
|
+
}) => ({
|
|
25
|
+
variable1: {
|
|
26
|
+
role: params.role,
|
|
27
|
+
criteria: {
|
|
28
|
+
...params.criteria,
|
|
29
|
+
...(params.orgName && { orgName: params.orgName }),
|
|
30
|
+
},
|
|
31
|
+
supportServices: params.supportServices ? true : false,
|
|
32
|
+
supportServiceCriteria: params.supportServices
|
|
33
|
+
? {
|
|
34
|
+
type: RoomType.Service,
|
|
35
|
+
}
|
|
36
|
+
: undefined,
|
|
37
|
+
orderBy: {
|
|
38
|
+
lastPostAt: 'desc',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Enhanced Skeleton Components
|
|
44
|
+
const InboxSkeleton = React.memo(({ showRightSidebar = false }: { showRightSidebar?: boolean }) => (
|
|
45
|
+
<div
|
|
46
|
+
className="h-full border-t border-gray-300 flex overflow-hidden animate-pulse"
|
|
47
|
+
style={{ height: '100vh !important' }}
|
|
48
|
+
>
|
|
49
|
+
{/* Left Sidebar Skeleton */}
|
|
50
|
+
<div className="flex-shrink-0 w-full md:w-80 lg:w-96 xl:w-[420px] bg-gray-50 border-r border-gray-300 overflow-hidden">
|
|
51
|
+
{/* Header skeleton */}
|
|
52
|
+
<div className="p-4 border-b border-gray-200">
|
|
53
|
+
<div className="flex items-center justify-between mb-4">
|
|
54
|
+
<div className="h-6 bg-gray-300 rounded w-24"></div>
|
|
55
|
+
<div className="h-8 w-8 bg-gray-300 rounded-full"></div>
|
|
56
|
+
</div>
|
|
57
|
+
{/* Search bar skeleton */}
|
|
58
|
+
<div className="h-10 bg-gray-300 rounded-lg w-full"></div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
{/* Conversation list skeleton */}
|
|
62
|
+
<div className="p-4 space-y-3">
|
|
63
|
+
{Array.from({ length: 8 }).map((_, index) => (
|
|
64
|
+
<div key={index} className="flex items-center space-x-3 p-3 rounded-lg">
|
|
65
|
+
{/* Avatar skeleton */}
|
|
66
|
+
<div className="h-12 w-12 bg-gray-300 rounded-full flex-shrink-0"></div>
|
|
67
|
+
<div className="flex-1 min-w-0">
|
|
68
|
+
{/* Name skeleton */}
|
|
69
|
+
<div className="h-4 bg-gray-300 rounded w-3/4 mb-2"></div>
|
|
70
|
+
{/* Message preview skeleton */}
|
|
71
|
+
<div className="h-3 bg-gray-200 rounded w-full"></div>
|
|
72
|
+
</div>
|
|
73
|
+
<div className="flex flex-col items-end space-y-1">
|
|
74
|
+
{/* Time skeleton */}
|
|
75
|
+
<div className="h-3 bg-gray-200 rounded w-12"></div>
|
|
76
|
+
{/* Badge skeleton */}
|
|
77
|
+
<div className="h-4 w-4 bg-gray-300 rounded-full"></div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
))}
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
{/* Main Content Area Skeleton */}
|
|
85
|
+
<div className="flex-1 min-w-0 overflow-hidden">
|
|
86
|
+
<div className="flex h-full">
|
|
87
|
+
{/* Chat content skeleton */}
|
|
88
|
+
<div className="flex-1 flex flex-col">
|
|
89
|
+
{/* Header skeleton */}
|
|
90
|
+
<div className="px-6 py-4 border-b border-gray-200 bg-white flex-shrink-0">
|
|
91
|
+
<div className="flex items-center justify-between">
|
|
92
|
+
<div className="h-6 bg-gray-300 rounded w-32"></div>
|
|
93
|
+
<div className="h-8 bg-gray-300 rounded w-20"></div>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
{/* Messages area skeleton */}
|
|
98
|
+
<div className="flex-1 p-4 space-y-4 overflow-hidden">
|
|
99
|
+
{Array.from({ length: 6 }).map((_, index) => (
|
|
100
|
+
<div key={index} className={`flex ${index % 2 === 0 ? 'justify-start' : 'justify-end'}`}>
|
|
101
|
+
<div
|
|
102
|
+
className={`flex items-start space-x-2 max-w-xs lg:max-w-md ${
|
|
103
|
+
index % 2 === 0 ? '' : 'flex-row-reverse space-x-reverse'
|
|
104
|
+
}`}
|
|
105
|
+
>
|
|
106
|
+
{index % 2 === 0 && (
|
|
107
|
+
<div className="h-8 w-8 bg-gray-300 rounded-full flex-shrink-0"></div>
|
|
108
|
+
)}
|
|
109
|
+
<div
|
|
110
|
+
className={`px-4 py-2 rounded-lg ${
|
|
111
|
+
index % 2 === 0 ? 'bg-gray-200' : 'bg-gray-300'
|
|
112
|
+
}`}
|
|
113
|
+
>
|
|
114
|
+
<div className="h-4 bg-gray-400 rounded w-32 mb-1"></div>
|
|
115
|
+
<div className="h-3 bg-gray-400 rounded w-20"></div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
))}
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
{/* Message input skeleton */}
|
|
123
|
+
<div className="p-4 border-t border-gray-200 bg-white">
|
|
124
|
+
<div className="flex items-center space-x-2">
|
|
125
|
+
<div className="h-10 bg-gray-300 rounded-full w-10"></div>
|
|
126
|
+
<div className="flex-1 h-10 bg-gray-300 rounded-lg"></div>
|
|
127
|
+
<div className="h-10 w-10 bg-gray-300 rounded-full"></div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
{/* Right Sidebar Skeleton - Desktop only, conditionally rendered */}
|
|
133
|
+
{showRightSidebar && (
|
|
134
|
+
<div className="hidden xl:block w-72 lg:w-80 xl:w-96 border-l border-gray-200 bg-white flex-shrink-0">
|
|
135
|
+
<div className="p-4">
|
|
136
|
+
{/* Right sidebar header */}
|
|
137
|
+
<div className="h-6 bg-gray-300 rounded w-24 mb-4"></div>
|
|
138
|
+
|
|
139
|
+
{/* Right sidebar content */}
|
|
140
|
+
<div className="space-y-3">
|
|
141
|
+
{Array.from({ length: 4 }).map((_, index) => (
|
|
142
|
+
<div key={index} className="p-3 border border-gray-200 rounded-lg">
|
|
143
|
+
<div className="h-4 bg-gray-300 rounded w-3/4 mb-2"></div>
|
|
144
|
+
<div className="h-3 bg-gray-200 rounded w-full mb-1"></div>
|
|
145
|
+
<div className="h-3 bg-gray-200 rounded w-2/3"></div>
|
|
146
|
+
</div>
|
|
147
|
+
))}
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
));
|
|
156
|
+
|
|
157
|
+
const TailwindSpinner = React.memo(() => (
|
|
158
|
+
<div className="flex items-center justify-center min-h-screen">
|
|
159
|
+
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
|
160
|
+
</div>
|
|
161
|
+
));
|
|
162
|
+
|
|
163
|
+
interface TailwindAlertProps {
|
|
164
|
+
children: React.ReactNode;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const TailwindAlert = React.memo(({ children }: TailwindAlertProps) => (
|
|
168
|
+
<div className="flex items-center justify-center min-h-screen">
|
|
169
|
+
<div
|
|
170
|
+
className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative max-w-md mx-auto"
|
|
171
|
+
role="alert"
|
|
172
|
+
>
|
|
173
|
+
<span className="block sm:inline">{children}</span>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
));
|
|
177
|
+
|
|
178
|
+
interface InboxWithLoaderProps {
|
|
179
|
+
channelFilters?: Record<string, any>;
|
|
180
|
+
channelRole?: string;
|
|
181
|
+
supportServices?: boolean;
|
|
182
|
+
pathPrefix?: string;
|
|
183
|
+
orgName?: string;
|
|
184
|
+
[key: string]: any; // Allow other props to be passed through to Inbox
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const InboxWithLoader = (props: InboxWithLoaderProps) => {
|
|
188
|
+
const { channelFilters: channelFilterProp, channelRole: channelRoleProp, supportServices, pathPrefix } = props;
|
|
189
|
+
const { orgName, channelRole: channelRoleParam } = useParams();
|
|
190
|
+
const location = useLocation();
|
|
191
|
+
const urlParams = location?.search ? new URLSearchParams(location.search) : null;
|
|
192
|
+
const template = urlParams?.get('template');
|
|
193
|
+
const channelRole = channelRoleParam || channelRoleProp;
|
|
194
|
+
console.log('template', template);
|
|
195
|
+
|
|
196
|
+
// Create new props object with orgName instead of mutating original props
|
|
197
|
+
const inboxProps = useMemo(
|
|
198
|
+
() => ({
|
|
199
|
+
...props,
|
|
200
|
+
orgName: orgName || '',
|
|
201
|
+
pathPrefix: pathPrefix || '',
|
|
202
|
+
}),
|
|
203
|
+
[props, orgName, pathPrefix],
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const { id: pathChannelId } = useParams();
|
|
207
|
+
|
|
208
|
+
// Enhanced query variables with better filtering and stable references
|
|
209
|
+
const channelsQueryVariables = useMemo(() => {
|
|
210
|
+
const channelFilters = { ...channelFilterProp };
|
|
211
|
+
const channelType = channelFilters?.type ?? RoomType.Direct;
|
|
212
|
+
const baseFilters = {
|
|
213
|
+
...channelFilters,
|
|
214
|
+
type: supportServices ? [channelType, RoomType.Service] : channelType,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
return queryParamsGenerator({
|
|
218
|
+
role: channelRole,
|
|
219
|
+
criteria: baseFilters,
|
|
220
|
+
supportServices,
|
|
221
|
+
orgName: orgName || '',
|
|
222
|
+
}).variable1;
|
|
223
|
+
}, [channelRole, channelFilterProp, supportServices, orgName]);
|
|
224
|
+
|
|
225
|
+
const messagesQueryVariables = useMemo(
|
|
226
|
+
() => ({
|
|
227
|
+
channelId: pathChannelId?.toString(),
|
|
228
|
+
parentId: null,
|
|
229
|
+
limit: MESSAGES_PER_PAGE,
|
|
230
|
+
}),
|
|
231
|
+
[pathChannelId],
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const channelDetailQueryVariables = useMemo(
|
|
235
|
+
() => ({
|
|
236
|
+
id: pathChannelId?.toString(),
|
|
237
|
+
}),
|
|
238
|
+
[pathChannelId],
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
// Enhanced Apollo queries with better cache policies and error handling
|
|
242
|
+
const channelsQuery = useGetChannelsByUserQuery({
|
|
243
|
+
variables: channelsQueryVariables,
|
|
244
|
+
fetchPolicy: 'cache-and-network', // Better for real-time updates
|
|
245
|
+
errorPolicy: 'all',
|
|
246
|
+
notifyOnNetworkStatusChange: false,
|
|
247
|
+
returnPartialData: true,
|
|
248
|
+
// Add context for better cache management
|
|
249
|
+
context: {
|
|
250
|
+
cacheKey: 'channels-list',
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const messagesQuery = useMessagesQuery({
|
|
255
|
+
variables: messagesQueryVariables,
|
|
256
|
+
skip: !pathChannelId,
|
|
257
|
+
fetchPolicy: 'cache-and-network', // Better for real-time messaging
|
|
258
|
+
errorPolicy: 'all',
|
|
259
|
+
notifyOnNetworkStatusChange: true,
|
|
260
|
+
returnPartialData: true,
|
|
261
|
+
// Disable polling to allow subscriptions to work properly
|
|
262
|
+
pollInterval: 0,
|
|
263
|
+
// Add context for better cache management
|
|
264
|
+
context: {
|
|
265
|
+
cacheKey: 'messages-list',
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
const channelDetailQuery = useViewChannelDetailQuery({
|
|
270
|
+
variables: channelDetailQueryVariables,
|
|
271
|
+
skip: !pathChannelId,
|
|
272
|
+
fetchPolicy: 'cache-first', // Channel details don't change often
|
|
273
|
+
errorPolicy: 'all',
|
|
274
|
+
notifyOnNetworkStatusChange: false, // No need for real-time updates
|
|
275
|
+
returnPartialData: true,
|
|
276
|
+
// Add context for better cache management
|
|
277
|
+
context: {
|
|
278
|
+
cacheKey: 'channel-detail',
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Enhanced error handling with retry logic
|
|
283
|
+
const handleRetry = useCallback(() => {
|
|
284
|
+
if (channelsQuery.error) {
|
|
285
|
+
channelsQuery.refetch();
|
|
286
|
+
}
|
|
287
|
+
if (messagesQuery.error) {
|
|
288
|
+
messagesQuery.refetch();
|
|
289
|
+
}
|
|
290
|
+
if (channelDetailQuery.error) {
|
|
291
|
+
channelDetailQuery.refetch();
|
|
292
|
+
}
|
|
293
|
+
}, [channelsQuery, messagesQuery, channelDetailQuery]);
|
|
294
|
+
|
|
295
|
+
// Create loader data array for Inbox component with better error handling
|
|
296
|
+
const loaderData = useMemo(
|
|
297
|
+
() => [channelsQuery, messagesQuery, channelDetailQuery],
|
|
298
|
+
[channelsQuery, messagesQuery, channelDetailQuery],
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
// Enhanced loading states with better UX
|
|
302
|
+
if (channelsQuery.loading && !pathChannelId && !channelsQuery.data) {
|
|
303
|
+
return <InboxSkeleton showRightSidebar={false} />;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Show skeleton during initial channel loading
|
|
307
|
+
if (
|
|
308
|
+
pathChannelId &&
|
|
309
|
+
(messagesQuery.loading || channelDetailQuery.loading) &&
|
|
310
|
+
!messagesQuery.data &&
|
|
311
|
+
!channelDetailQuery.data
|
|
312
|
+
) {
|
|
313
|
+
return <InboxSkeleton showRightSidebar={true} />;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Enhanced error handling with retry option
|
|
317
|
+
if (channelsQuery.error && !channelsQuery.data) {
|
|
318
|
+
return (
|
|
319
|
+
<div className="flex flex-col items-center justify-center min-h-screen space-y-4">
|
|
320
|
+
<TailwindAlert>Error loading channels: {channelsQuery.error.message}</TailwindAlert>
|
|
321
|
+
<button
|
|
322
|
+
onClick={handleRetry}
|
|
323
|
+
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
|
324
|
+
>
|
|
325
|
+
Retry
|
|
326
|
+
</button>
|
|
327
|
+
</div>
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Handle individual query errors gracefully
|
|
332
|
+
if (messagesQuery.error && !messagesQuery.data && pathChannelId) {
|
|
333
|
+
console.warn('Messages query error:', messagesQuery.error);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (channelDetailQuery.error && !channelDetailQuery.data && pathChannelId) {
|
|
337
|
+
console.warn('Channel detail query error:', channelDetailQuery.error);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Conditional rendering based on template parameter
|
|
341
|
+
if (template === '1') {
|
|
342
|
+
return <AiInbox data={loaderData} {...inboxProps} />;
|
|
343
|
+
} else if (template === '2') {
|
|
344
|
+
return <InboxTemplate1 data={loaderData} {...inboxProps} />;
|
|
345
|
+
} else if (template === '3') {
|
|
346
|
+
return <InboxTemplate2 data={loaderData} {...inboxProps} />;
|
|
347
|
+
} else {
|
|
348
|
+
// Default fallback to Inbox
|
|
349
|
+
return <Inbox data={loaderData} {...inboxProps} />;
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// Display name for debugging
|
|
354
|
+
InboxWithLoader.displayName = 'InboxWithLoader';
|
|
355
|
+
|
|
356
|
+
export default React.memo(InboxWithLoader);
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useNavigate } from '@remix-run/react';
|
|
3
|
+
import { RoomType } from 'common';
|
|
4
|
+
import { InputComponent } from '../components/InboxMessage';
|
|
5
|
+
import { objectId } from '@messenger-box/core';
|
|
6
|
+
import { useAddChannelMutation, useSendMessagesMutation } from 'common/graphql';
|
|
7
|
+
|
|
8
|
+
const TailwindOverlaySpinner = React.memo(() => (
|
|
9
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-30">
|
|
10
|
+
<div className="animate-spin rounded-full h-10 w-10 border-2 border-white border-t-transparent"></div>
|
|
11
|
+
</div>
|
|
12
|
+
));
|
|
13
|
+
|
|
14
|
+
const AiLandingInput: React.FC = () => {
|
|
15
|
+
const [addChannel] = useAddChannelMutation();
|
|
16
|
+
const [sendMessage] = useSendMessagesMutation();
|
|
17
|
+
const navigate = useNavigate();
|
|
18
|
+
const [isCreatingChannel, setIsCreatingChannel] = React.useState(false);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="flex items-center justify-center min-h-screen bg-gray-50">
|
|
22
|
+
<div className="w-full max-w-2xl mx-4">
|
|
23
|
+
<div className="bg-white rounded-lg shadow-lg p-6">
|
|
24
|
+
<div className="text-center mb-6">
|
|
25
|
+
<h3 className="text-xl font-semibold text-gray-700 mb-2">What would you like to build?</h3>
|
|
26
|
+
<p className="text-gray-500">Describe your idea and I'll help you create it step by step.</p>
|
|
27
|
+
</div>
|
|
28
|
+
<InputComponent
|
|
29
|
+
handleSend={async (message: string, files: File[]) => {
|
|
30
|
+
console.log('Message sent without channel:', message, files);
|
|
31
|
+
setIsCreatingChannel(true);
|
|
32
|
+
const id = objectId();
|
|
33
|
+
addChannel({
|
|
34
|
+
variables: {
|
|
35
|
+
name: `AI-Assistant-${id}`,
|
|
36
|
+
type: RoomType.Aiassistant,
|
|
37
|
+
description: 'AI Assistant',
|
|
38
|
+
},
|
|
39
|
+
onCompleted: (data: any) => {
|
|
40
|
+
console.log('Channel created:', data);
|
|
41
|
+
if (!data.createChannel) {
|
|
42
|
+
setIsCreatingChannel(false);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const createdId = data.createChannel.id;
|
|
46
|
+
sendMessage({
|
|
47
|
+
variables: {
|
|
48
|
+
channelId: createdId,
|
|
49
|
+
content: message,
|
|
50
|
+
},
|
|
51
|
+
onCompleted: () => {
|
|
52
|
+
navigate(`/ai-messenger/app?id=${createdId}`, { replace: true });
|
|
53
|
+
setIsCreatingChannel(false);
|
|
54
|
+
},
|
|
55
|
+
onError: (error: any) => {
|
|
56
|
+
console.error('Error sending message:', error);
|
|
57
|
+
setIsCreatingChannel(false);
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
onError: (error: any) => {
|
|
62
|
+
console.error('Error creating channel:', error);
|
|
63
|
+
setIsCreatingChannel(false);
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}}
|
|
67
|
+
placeholder="Type your message here..."
|
|
68
|
+
/>
|
|
69
|
+
<div className="mt-2">
|
|
70
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
71
|
+
<div className="space-y-2">
|
|
72
|
+
<div className="relative">
|
|
73
|
+
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
74
|
+
<div className="w-5 h-5 bg-purple-500 rounded-full flex items-center justify-center">
|
|
75
|
+
<span className="text-white text-xs font-bold">N</span>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
<select className="block w-full pl-10 pr-10 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 bg-white">
|
|
79
|
+
<option value="nextjs">Next.js</option>
|
|
80
|
+
<option value="vuejs">Vue.js</option>
|
|
81
|
+
<option value="vite-remix">Vite with Remix</option>
|
|
82
|
+
</select>
|
|
83
|
+
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
|
|
84
|
+
<svg
|
|
85
|
+
className="h-5 w-5 text-gray-400"
|
|
86
|
+
fill="none"
|
|
87
|
+
stroke="currentColor"
|
|
88
|
+
viewBox="0 0 24 24"
|
|
89
|
+
>
|
|
90
|
+
<path
|
|
91
|
+
strokeLinecap="round"
|
|
92
|
+
strokeLinejoin="round"
|
|
93
|
+
strokeWidth={2}
|
|
94
|
+
d="M19 9l-7 7-7-7"
|
|
95
|
+
/>
|
|
96
|
+
</svg>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
<div className="space-y-2">
|
|
101
|
+
<div className="relative">
|
|
102
|
+
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
103
|
+
<div className="w-5 h-5 bg-pink-500 rounded-full flex items-center justify-center">
|
|
104
|
+
<svg className="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20">
|
|
105
|
+
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
106
|
+
</svg>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
<select className="block w-full pl-10 pr-10 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-pink-500 focus:border-pink-500 bg-white">
|
|
110
|
+
<option value="claude-3-5-sonnet">Claude 3.5 Sonnet</option>
|
|
111
|
+
<option value="claude-3-5-haiku">Claude 3.5 Haiku</option>
|
|
112
|
+
<option value="claude-3-opus">Claude 3 Opus</option>
|
|
113
|
+
<option value="claude-3-sonnet">Claude 3 Sonnet</option>
|
|
114
|
+
<option value="claude-3-haiku">Claude 3 Haiku</option>
|
|
115
|
+
</select>
|
|
116
|
+
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
|
|
117
|
+
<svg
|
|
118
|
+
className="h-5 w-5 text-gray-400"
|
|
119
|
+
fill="none"
|
|
120
|
+
stroke="currentColor"
|
|
121
|
+
viewBox="0 0 24 24"
|
|
122
|
+
>
|
|
123
|
+
<path
|
|
124
|
+
strokeLinecap="round"
|
|
125
|
+
strokeLinejoin="round"
|
|
126
|
+
strokeWidth={2}
|
|
127
|
+
d="M19 9l-7 7-7-7"
|
|
128
|
+
/>
|
|
129
|
+
</svg>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
<div className="space-y-2">
|
|
134
|
+
<div className="relative">
|
|
135
|
+
<select className="block w-full pr-10 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white">
|
|
136
|
+
<option value="">API Key</option>
|
|
137
|
+
<option value="saved-key-1">••••••••••••</option>
|
|
138
|
+
<option value="saved-key-2">••••••••••••</option>
|
|
139
|
+
</select>
|
|
140
|
+
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
|
|
141
|
+
<svg
|
|
142
|
+
className="h-5 w-5 text-gray-400"
|
|
143
|
+
fill="none"
|
|
144
|
+
stroke="currentColor"
|
|
145
|
+
viewBox="0 0 24 24"
|
|
146
|
+
>
|
|
147
|
+
<path
|
|
148
|
+
strokeLinecap="round"
|
|
149
|
+
strokeLinejoin="round"
|
|
150
|
+
strokeWidth={2}
|
|
151
|
+
d="M19 9l-7 7-7-7"
|
|
152
|
+
/>
|
|
153
|
+
</svg>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
{isCreatingChannel && <TailwindOverlaySpinner />}
|
|
162
|
+
</div>
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
AiLandingInput.displayName = 'AiLandingInput';
|
|
167
|
+
|
|
168
|
+
export default React.memo(AiLandingInput);
|
package/src/container/Inbox.tsx
CHANGED
|
@@ -8,10 +8,11 @@ import {
|
|
|
8
8
|
} from 'common/graphql';
|
|
9
9
|
import { useUploadFiles } from '@messenger-box/platform-client';
|
|
10
10
|
import { IFileInfo, RoomType, PostTypeEnum } from 'common';
|
|
11
|
-
import { useSelector } from 'react-redux';
|
|
11
|
+
import { useSelector, shallowEqual } from 'react-redux';
|
|
12
12
|
import { useNavigate, useParams } from '@remix-run/react';
|
|
13
13
|
import { LeftSidebar, MessageInput, Messages, RightSidebar } from '../components';
|
|
14
|
-
import { userSelector } from '@adminide-stack/user-auth0-client';
|
|
14
|
+
import { Store, userSelector } from '@adminide-stack/user-auth0-client';
|
|
15
|
+
import { IUserState } from '@adminide-stack/core';
|
|
15
16
|
import { config } from '../config';
|
|
16
17
|
import { applyFooterStyles } from './apply-footer-styles';
|
|
17
18
|
import { objectId } from '@messenger-box/core';
|
|
@@ -172,7 +173,9 @@ const Inbox = (props: InboxProps) => {
|
|
|
172
173
|
const isDesktopView = useMediaQuery('(min-width: 1025px)');
|
|
173
174
|
const isLargeDesktopView = useMediaQuery('(min-width: 1440px)');
|
|
174
175
|
const isSmallScreen = useMediaQuery('(max-width: 900px)');
|
|
175
|
-
const auth = useSelector(userSelector);
|
|
176
|
+
// const auth = useSelector(userSelector);
|
|
177
|
+
const auth: any = useSelector<Store.Auth, IUserState>(userSelector, shallowEqual);
|
|
178
|
+
// const user = useSelector((state: any) => state.user, shallowEqual);
|
|
176
179
|
|
|
177
180
|
// Data destructuring from Apollo queries
|
|
178
181
|
const GetChannelsByUserQuery = data?.[0];
|
|
@@ -235,8 +238,8 @@ const Inbox = (props: InboxProps) => {
|
|
|
235
238
|
return title || 'Direct Message';
|
|
236
239
|
}
|
|
237
240
|
|
|
238
|
-
if (
|
|
239
|
-
if (members[0].user?.givenName && members[0]
|
|
241
|
+
if (type === RoomType.Direct && members?.length === 1) {
|
|
242
|
+
if (members[0].user?.givenName && members[0]?.user?.familyName) {
|
|
240
243
|
return `${members[0].user?.givenName} ${members[0].user?.familyName}`;
|
|
241
244
|
}
|
|
242
245
|
return members[0].user?.givenName || members[0].user?.familyName || 'Direct Message';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React, { useMemo, useCallback } from 'react';
|
|
2
|
+
import { useParams, useLocation } from '@remix-run/react';
|
|
3
|
+
import { RoomType } from 'common';
|
|
4
|
+
import { config } from '../config';
|
|
5
|
+
import { AIAgent } from '../components/AIAgent';
|
|
6
|
+
import { useSelector, shallowEqual } from 'react-redux';
|
|
7
|
+
import { Store, userSelector } from '@adminide-stack/user-auth0-client';
|
|
8
|
+
import { IUserState } from '@adminide-stack/core';
|
|
9
|
+
|
|
10
|
+
// Enhanced query parameters generator with better typing and flexibility
|
|
11
|
+
export const queryParamsGenerator = (params: {
|
|
12
|
+
role?: string;
|
|
13
|
+
criteria?: Record<string, any>;
|
|
14
|
+
supportServices?: boolean;
|
|
15
|
+
orgName?: string;
|
|
16
|
+
}) => ({
|
|
17
|
+
variable1: {
|
|
18
|
+
role: params.role,
|
|
19
|
+
criteria: {
|
|
20
|
+
...params.criteria,
|
|
21
|
+
...(params.orgName && { orgName: params.orgName }),
|
|
22
|
+
},
|
|
23
|
+
supportServices: params.supportServices ? true : false,
|
|
24
|
+
supportServiceCriteria: params.supportServices
|
|
25
|
+
? {
|
|
26
|
+
type: RoomType.Service,
|
|
27
|
+
}
|
|
28
|
+
: undefined,
|
|
29
|
+
orderBy: {
|
|
30
|
+
lastPostAt: 'desc',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
interface InboxWithAiLoaderOutletProps {
|
|
36
|
+
channelFilters?: Record<string, any>;
|
|
37
|
+
channelRole?: string;
|
|
38
|
+
supportServices?: boolean;
|
|
39
|
+
pathPrefix?: string;
|
|
40
|
+
orgName?: string;
|
|
41
|
+
[key: string]: any; // Allow other props to be passed through to Inbox
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const InboxWithAiLoaderOutlet = (props: InboxWithAiLoaderOutletProps) => {
|
|
45
|
+
const { channelFilters: channelFilterProp, channelRole: channelRoleProp, supportServices, pathPrefix } = props;
|
|
46
|
+
const { orgName, channelRole: channelRoleParam } = useParams();
|
|
47
|
+
const location = useLocation();
|
|
48
|
+
const urlParams = location?.search ? new URLSearchParams(location.search) : null;
|
|
49
|
+
const channelId = urlParams?.get('id');
|
|
50
|
+
const user: any = useSelector<Store.Auth, IUserState>(userSelector, shallowEqual);
|
|
51
|
+
|
|
52
|
+
return <AIAgent channelId={channelId} placeholder="Ask me anything..." className="h-full" currentUser={user} />;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Display name for debugging
|
|
56
|
+
InboxWithAiLoaderOutlet.displayName = 'InboxWithAiLoaderOutlet';
|
|
57
|
+
|
|
58
|
+
export default React.memo(InboxWithAiLoaderOutlet);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React, { useMemo, useCallback } from 'react';
|
|
2
|
+
import Inbox from '../templates/InboxWithAi';
|
|
3
|
+
|
|
4
|
+
interface InboxContainerProps {
|
|
5
|
+
channelId: string;
|
|
6
|
+
channelFilters?: Record<string, any>;
|
|
7
|
+
channelRole?: string;
|
|
8
|
+
supportServices?: boolean;
|
|
9
|
+
orgName?: string;
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const InboxSkeleton = React.memo(({ showRightSidebar = false }: { showRightSidebar?: boolean }) => (
|
|
14
|
+
<div
|
|
15
|
+
className="h-full border-t border-gray-300 flex overflow-hidden animate-pulse"
|
|
16
|
+
style={{ height: '100vh !important' }}
|
|
17
|
+
>
|
|
18
|
+
<div className="flex-1 min-w-0 bg-white border-r border-gray-300"></div>
|
|
19
|
+
{showRightSidebar && <div className="w-80 border-l border-gray-200 bg-white flex-shrink-0"></div>}
|
|
20
|
+
</div>
|
|
21
|
+
));
|
|
22
|
+
|
|
23
|
+
const InboxContainer: React.FC<InboxContainerProps> = (props) => {
|
|
24
|
+
const { channelId, channelFilters: channelFilterProp, channelRole, supportServices, orgName } = props;
|
|
25
|
+
|
|
26
|
+
if (!channelId) {
|
|
27
|
+
return <InboxSkeleton showRightSidebar={true} />;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return <Inbox {...props} />;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
InboxContainer.displayName = 'InboxContainer';
|
|
34
|
+
|
|
35
|
+
export default React.memo(InboxContainer);
|