@adens/openwa 0.1.4 → 0.1.6

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.
Files changed (141) hide show
  1. package/adens-openwa-0.1.5.tgz +0 -0
  2. package/package.json +3 -1
  3. package/server/index.js +30 -12
  4. package/web/.next/BUILD_ID +1 -0
  5. package/web/.next/build-manifest.json +43 -0
  6. package/web/.next/cache/.previewinfo +1 -0
  7. package/web/.next/cache/.rscinfo +1 -0
  8. package/web/.next/cache/webpack/client-production/0.pack +0 -0
  9. package/web/.next/cache/webpack/client-production/1.pack +0 -0
  10. package/web/.next/cache/webpack/client-production/10.pack +0 -0
  11. package/web/.next/cache/webpack/client-production/11.pack +0 -0
  12. package/web/.next/cache/webpack/client-production/12.pack +0 -0
  13. package/web/.next/cache/webpack/client-production/13.pack +0 -0
  14. package/web/.next/cache/webpack/client-production/14.pack +0 -0
  15. package/web/.next/cache/webpack/client-production/15.pack +0 -0
  16. package/web/.next/cache/webpack/client-production/16.pack +0 -0
  17. package/web/.next/cache/webpack/client-production/17.pack +0 -0
  18. package/web/.next/cache/webpack/client-production/18.pack +0 -0
  19. package/web/.next/cache/webpack/client-production/19.pack +0 -0
  20. package/web/.next/cache/webpack/client-production/2.pack +0 -0
  21. package/web/.next/cache/webpack/client-production/20.pack +0 -0
  22. package/web/.next/cache/webpack/client-production/21.pack +0 -0
  23. package/web/.next/cache/webpack/client-production/22.pack +0 -0
  24. package/web/.next/cache/webpack/client-production/23.pack +0 -0
  25. package/web/.next/cache/webpack/client-production/24.pack +0 -0
  26. package/web/.next/cache/webpack/client-production/25.pack +0 -0
  27. package/web/.next/cache/webpack/client-production/26.pack +0 -0
  28. package/web/.next/cache/webpack/client-production/27.pack +0 -0
  29. package/web/.next/cache/webpack/client-production/3.pack +0 -0
  30. package/web/.next/cache/webpack/client-production/4.pack +0 -0
  31. package/web/.next/cache/webpack/client-production/5.pack +0 -0
  32. package/web/.next/cache/webpack/client-production/6.pack +0 -0
  33. package/web/.next/cache/webpack/client-production/7.pack +0 -0
  34. package/web/.next/cache/webpack/client-production/8.pack +0 -0
  35. package/web/.next/cache/webpack/client-production/9.pack +0 -0
  36. package/web/.next/cache/webpack/client-production/index.pack +0 -0
  37. package/web/.next/cache/webpack/client-production/index.pack.old +0 -0
  38. package/web/.next/cache/webpack/edge-server-production/0.pack +0 -0
  39. package/web/.next/cache/webpack/edge-server-production/index.pack +0 -0
  40. package/web/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  41. package/web/.next/cache/webpack/server-production/0.pack +0 -0
  42. package/web/.next/cache/webpack/server-production/1.pack +0 -0
  43. package/web/.next/cache/webpack/server-production/10.pack +0 -0
  44. package/web/.next/cache/webpack/server-production/11.pack +0 -0
  45. package/web/.next/cache/webpack/server-production/12.pack +0 -0
  46. package/web/.next/cache/webpack/server-production/13.pack +0 -0
  47. package/web/.next/cache/webpack/server-production/14.pack +0 -0
  48. package/web/.next/cache/webpack/server-production/15.pack +0 -0
  49. package/web/.next/cache/webpack/server-production/16.pack +0 -0
  50. package/web/.next/cache/webpack/server-production/17.pack +0 -0
  51. package/web/.next/cache/webpack/server-production/18.pack +0 -0
  52. package/web/.next/cache/webpack/server-production/19.pack +0 -0
  53. package/web/.next/cache/webpack/server-production/2.pack +0 -0
  54. package/web/.next/cache/webpack/server-production/20.pack +0 -0
  55. package/web/.next/cache/webpack/server-production/3.pack +0 -0
  56. package/web/.next/cache/webpack/server-production/4.pack +0 -0
  57. package/web/.next/cache/webpack/server-production/5.pack +0 -0
  58. package/web/.next/cache/webpack/server-production/6.pack +0 -0
  59. package/web/.next/cache/webpack/server-production/7.pack +0 -0
  60. package/web/.next/cache/webpack/server-production/8.pack +0 -0
  61. package/web/.next/cache/webpack/server-production/9.pack +0 -0
  62. package/web/.next/cache/webpack/server-production/index.pack +0 -0
  63. package/web/.next/cache/webpack/server-production/index.pack.old +0 -0
  64. package/web/.next/diagnostics/build-diagnostics.json +6 -0
  65. package/web/.next/diagnostics/framework.json +1 -0
  66. package/web/.next/dynamic-css-manifest.json +1 -0
  67. package/web/.next/export-marker.json +6 -0
  68. package/web/.next/images-manifest.json +58 -0
  69. package/web/.next/next-minimal-server.js.nft.json +1 -0
  70. package/web/.next/next-server.js.nft.json +1 -0
  71. package/web/.next/package.json +1 -0
  72. package/web/.next/prerender-manifest.json +11 -0
  73. package/web/.next/react-loadable-manifest.json +14 -0
  74. package/web/.next/required-server-files.json +343 -0
  75. package/web/.next/routes-manifest.json +79 -0
  76. package/web/.next/server/chunks/276.js +19 -0
  77. package/web/.next/server/chunks/508.js +1 -0
  78. package/web/.next/server/chunks/891.js +6 -0
  79. package/web/.next/server/dynamic-css-manifest.js +1 -0
  80. package/web/.next/server/functions-config-manifest.json +4 -0
  81. package/web/.next/server/interception-route-rewrite-manifest.js +1 -0
  82. package/web/.next/server/middleware-build-manifest.js +1 -0
  83. package/web/.next/server/middleware-manifest.json +6 -0
  84. package/web/.next/server/middleware-react-loadable-manifest.js +1 -0
  85. package/web/.next/server/next-font-manifest.js +1 -0
  86. package/web/.next/server/next-font-manifest.json +1 -0
  87. package/web/.next/server/pages/404.html +1 -0
  88. package/web/.next/server/pages/500.html +1 -0
  89. package/web/.next/server/pages/_app.js +1 -0
  90. package/web/.next/server/pages/_app.js.nft.json +1 -0
  91. package/web/.next/server/pages/_document.js +1 -0
  92. package/web/.next/server/pages/_document.js.nft.json +1 -0
  93. package/web/.next/server/pages/_error.js +1 -0
  94. package/web/.next/server/pages/_error.js.nft.json +1 -0
  95. package/web/.next/server/pages/dashboard.html +1 -0
  96. package/web/.next/server/pages/dashboard.js.nft.json +1 -0
  97. package/web/.next/server/pages/index.html +1 -0
  98. package/web/.next/server/pages/index.js.nft.json +1 -0
  99. package/web/.next/server/pages-manifest.json +8 -0
  100. package/web/.next/server/webpack-runtime.js +1 -0
  101. package/web/.next/static/FqMH7OdKFqTxzPemOh9_6/_buildManifest.js +1 -0
  102. package/web/.next/static/FqMH7OdKFqTxzPemOh9_6/_ssgManifest.js +1 -0
  103. package/web/.next/static/chunks/112-1bb8aeaa7c3fd57f.js +1 -0
  104. package/web/.next/static/chunks/131.2b0db2ec21a47401.js +1 -0
  105. package/web/.next/static/chunks/1d0474cf-ced9eb4c00bbcd99.js +1 -0
  106. package/web/.next/static/chunks/750.caaf5a9ddd46b267.js +1 -0
  107. package/web/.next/static/chunks/framework-9ad035430eed8b2b.js +1 -0
  108. package/web/.next/static/chunks/main-a5ca0156a62da1bd.js +1 -0
  109. package/web/.next/static/chunks/pages/_app-37894446f6ae5afe.js +1 -0
  110. package/web/.next/static/chunks/pages/_error-8e5b843ec9d413fc.js +1 -0
  111. package/web/.next/static/chunks/pages/dashboard-ce0d55d3fea9e308.js +1 -0
  112. package/web/.next/static/chunks/pages/index-8d94234d8de13682.js +1 -0
  113. package/web/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  114. package/web/.next/static/chunks/webpack-ea849f90a4c65253.js +1 -0
  115. package/web/.next/static/css/de4e0852dfebd52f.css +3 -0
  116. package/web/.next/trace +2 -0
  117. package/web/.next/types/routes.d.ts +62 -0
  118. package/web/.next/types/validator.ts +16 -0
  119. package/web/components/AppHead.js +0 -14
  120. package/web/components/AuthCard.js +0 -170
  121. package/web/components/BrandLogo.js +0 -11
  122. package/web/components/ChatWindow.js +0 -875
  123. package/web/components/ChatWindow.js.tmp +0 -0
  124. package/web/components/ContactList.js +0 -97
  125. package/web/components/ContactsPanel.js +0 -90
  126. package/web/components/EmojiPicker.js +0 -108
  127. package/web/components/MediaPreviewModal.js +0 -146
  128. package/web/components/MessageActionMenu.js +0 -155
  129. package/web/components/SessionSidebar.js +0 -167
  130. package/web/components/SettingsModal.js +0 -266
  131. package/web/components/Skeletons.js +0 -73
  132. package/web/jsconfig.json +0 -10
  133. package/web/lib/api.js +0 -33
  134. package/web/lib/socket.js +0 -9
  135. package/web/pages/_app.js +0 -5
  136. package/web/pages/dashboard.js +0 -541
  137. package/web/pages/index.js +0 -62
  138. package/web/postcss.config.js +0 -10
  139. package/web/store/useAppStore.js +0 -209
  140. package/web/styles/globals.css +0 -52
  141. package/web/tailwind.config.js +0 -36
@@ -1,541 +0,0 @@
1
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
- import { useRouter } from "next/router";
3
- import { AppHead } from "@/components/AppHead";
4
- import { ChatWindow } from "@/components/ChatWindow";
5
- import { ContactList } from "@/components/ContactList";
6
- import { ContactsPanel } from "@/components/ContactsPanel";
7
- import { SettingsModal } from "@/components/SettingsModal";
8
- import { apiFetch } from "@/lib/api";
9
- import { createSocket } from "@/lib/socket";
10
- import { useAppStore } from "@/store/useAppStore";
11
-
12
- export default function DashboardPage() {
13
- const router = useRouter();
14
- const {
15
- token,
16
- user,
17
- hydrateAuth,
18
- logout,
19
- setBootstrapData,
20
- setMessages,
21
- prependMessages,
22
- setActiveChat,
23
- upsertSession,
24
- upsertChat,
25
- addMessage,
26
- updateMessageStatus,
27
- updateMessage,
28
- setSocket,
29
- socket,
30
- chats,
31
- sessions,
32
- activeChatId,
33
- activeSessionId,
34
- setActiveSession,
35
- messagesByChat,
36
- messageMetaByChat,
37
- typingByChat
38
- } = useAppStore();
39
-
40
- const [loading, setLoading] = useState(true);
41
- const [contactsLoading, setContactsLoading] = useState(false);
42
- const [error, setError] = useState("");
43
- const [sessionName, setSessionName] = useState("");
44
- const [sessionPhone, setSessionPhone] = useState("");
45
- const [chatQuery, setChatQuery] = useState("");
46
- const [contactQuery, setContactQuery] = useState("");
47
- const [messageQuery, setMessageQuery] = useState("");
48
- const [loadingOlder, setLoadingOlder] = useState(false);
49
- const [messagesLoading, setMessagesLoading] = useState(false);
50
- const [contacts, setContacts] = useState([]);
51
- const [apiKeys, setApiKeys] = useState([]);
52
- const [apiKeysLoading, setApiKeysLoading] = useState(false);
53
- const [apiKeyName, setApiKeyName] = useState("");
54
- const [apiKeySecret, setApiKeySecret] = useState("");
55
- const [startingContactId, setStartingContactId] = useState(null);
56
- const [settingsOpen, setSettingsOpen] = useState(false);
57
- const [contactsPanelOpen, setContactsPanelOpen] = useState(false);
58
- const chatWindowRef = useRef(null);
59
-
60
- const activeChat = useMemo(() => chats.find((chat) => chat.id === activeChatId) || null, [activeChatId, chats]);
61
- const activeMessages = messagesByChat[activeChatId] || [];
62
- const activeMeta = messageMetaByChat[activeChatId] || { hasMore: false, nextBefore: null };
63
- const activeTyping = typingByChat[activeChatId];
64
- const readySessions = sessions.filter((session) => session.status === "ready").length;
65
-
66
- const loadContacts = useCallback(async () => {
67
- if (!token) {
68
- return;
69
- }
70
-
71
- setContactsLoading(true);
72
- try {
73
- const data = await apiFetch("/api/contacts", { token });
74
- setContacts(data.contacts || []);
75
- } catch (requestError) {
76
- setError(requestError.message);
77
- } finally {
78
- setContactsLoading(false);
79
- }
80
- }, [token]);
81
-
82
- const loadApiKeys = useCallback(async () => {
83
- if (!token) {
84
- return;
85
- }
86
-
87
- setApiKeysLoading(true);
88
- try {
89
- const data = await apiFetch("/api/api-keys", { token });
90
- setApiKeys(data.apiKeys || []);
91
- } catch (requestError) {
92
- setError(requestError.message);
93
- } finally {
94
- setApiKeysLoading(false);
95
- }
96
- }, [token]);
97
-
98
- const loadWorkspace = useCallback(async (showSpinner = false) => {
99
- if (!token) {
100
- return;
101
- }
102
-
103
- if (showSpinner) {
104
- setLoading(true);
105
- }
106
-
107
- try {
108
- const data = await apiFetch("/api/bootstrap", { token });
109
- setBootstrapData(data);
110
- } catch (requestError) {
111
- setError(requestError.message);
112
- if (requestError.status === 401) {
113
- logout();
114
- router.replace("/");
115
- }
116
- } finally {
117
- if (showSpinner) {
118
- setLoading(false);
119
- }
120
- }
121
- }, [logout, router, setBootstrapData, token]);
122
-
123
- useEffect(() => {
124
- hydrateAuth();
125
- }, [hydrateAuth]);
126
-
127
- useEffect(() => {
128
- if (!token) {
129
- router.replace("/");
130
- return;
131
- }
132
-
133
- Promise.all([loadWorkspace(true), loadContacts(), loadApiKeys()]).finally(() => {
134
- setLoading(false);
135
- });
136
- }, [loadApiKeys, loadContacts, loadWorkspace, router, token]);
137
-
138
- useEffect(() => {
139
- if (!activeSessionId && sessions[0]?.id) {
140
- setActiveSession(sessions[0].id);
141
- }
142
- }, [activeSessionId, sessions, setActiveSession]);
143
-
144
- useEffect(() => {
145
- if (!token) {
146
- return undefined;
147
- }
148
-
149
- const socketClient = createSocket(token);
150
- setSocket(socketClient);
151
-
152
- socketClient.on("new_message", (message) => {
153
- addMessage(message);
154
- });
155
-
156
- socketClient.on("message_status_update", (payload) => {
157
- updateMessageStatus(payload);
158
- });
159
-
160
- socketClient.on("contact_list_update", (chat) => {
161
- upsertChat(chat);
162
- loadContacts();
163
- });
164
-
165
- socketClient.on("session_status_update", (session) => {
166
- upsertSession(session);
167
- });
168
-
169
- socketClient.on("workspace_synced", () => {
170
- loadWorkspace();
171
- loadContacts();
172
- });
173
-
174
- socketClient.on("typing_event", (payload) => {
175
- useAppStore.getState().setTyping(payload);
176
- });
177
-
178
- return () => {
179
- socketClient.close();
180
- setSocket(null);
181
- };
182
- }, [addMessage, loadContacts, loadWorkspace, setSocket, token, updateMessageStatus, upsertChat, upsertSession]);
183
-
184
- useEffect(() => {
185
- if (!activeChatId) {
186
- setMessagesLoading(false);
187
- return;
188
- }
189
-
190
- if (messagesByChat[activeChatId]) {
191
- setMessagesLoading(false);
192
- return;
193
- }
194
-
195
- if (!token) {
196
- return;
197
- }
198
-
199
- setMessagesLoading(true);
200
- apiFetch(`/api/chats/${activeChatId}/messages`, { token })
201
- .then((data) => {
202
- setMessages(activeChatId, data.messages, {
203
- hasMore: Boolean(data.hasMore),
204
- nextBefore: data.nextBefore || null
205
- });
206
- })
207
- .catch((requestError) => {
208
- setError(requestError.message);
209
- })
210
- .finally(() => {
211
- setMessagesLoading(false);
212
- });
213
- }, [activeChatId, messagesByChat, setMessages, token]);
214
-
215
- const handleCreateSession = async (event) => {
216
- event.preventDefault();
217
- setError("");
218
-
219
- try {
220
- const data = await apiFetch("/api/sessions", {
221
- method: "POST",
222
- token,
223
- body: {
224
- name: sessionName,
225
- phoneNumber: sessionPhone
226
- }
227
- });
228
-
229
- upsertSession(data.session);
230
- setActiveSession(data.session.id);
231
- setSessionName("");
232
- setSessionPhone("");
233
- setSettingsOpen(true);
234
- await loadWorkspace();
235
- } catch (requestError) {
236
- setError(requestError.message);
237
- }
238
- };
239
-
240
- const handleConnectSession = async (sessionId) => {
241
- setError("");
242
- try {
243
- const data = await apiFetch(`/api/sessions/${sessionId}/connect`, {
244
- method: "POST",
245
- token
246
- });
247
-
248
- upsertSession(data.session);
249
- setActiveSession(sessionId);
250
- } catch (requestError) {
251
- setError(requestError.message);
252
- }
253
- };
254
-
255
- const handleDisconnectSession = async (sessionId) => {
256
- setError("");
257
- try {
258
- const data = await apiFetch(`/api/sessions/${sessionId}/disconnect`, {
259
- method: "POST",
260
- token
261
- });
262
-
263
- upsertSession(data.session);
264
- } catch (requestError) {
265
- setError(requestError.message);
266
- }
267
- };
268
-
269
- const handleCreateApiKey = async (event) => {
270
- event.preventDefault();
271
- setError("");
272
-
273
- try {
274
- const result = await apiFetch("/api/api-keys", {
275
- method: "POST",
276
- token,
277
- body: {
278
- name: apiKeyName
279
- }
280
- });
281
-
282
- setApiKeySecret(result.secret);
283
- setApiKeyName("");
284
- setApiKeys((current) => [result.apiKey, ...current]);
285
- setSettingsOpen(true);
286
- } catch (requestError) {
287
- setError(requestError.message);
288
- }
289
- };
290
-
291
- const handleRevokeApiKey = async (apiKeyId) => {
292
- setError("");
293
-
294
- try {
295
- await apiFetch(`/api/api-keys/${apiKeyId}`, {
296
- method: "DELETE",
297
- token
298
- });
299
-
300
- setApiKeys((current) => current.filter((item) => item.id !== apiKeyId));
301
- } catch (requestError) {
302
- setError(requestError.message);
303
- }
304
- };
305
-
306
- const handleOpenChat = async (chatId) => {
307
- setActiveChat(chatId);
308
- setMessageQuery("");
309
- socket?.emit("open_chat", { chatId });
310
- };
311
-
312
- const handleStartChat = async (contactId) => {
313
- setStartingContactId(contactId);
314
- setError("");
315
-
316
- try {
317
- const result = await apiFetch(`/api/contacts/${contactId}/open`, {
318
- method: "POST",
319
- token
320
- });
321
-
322
- upsertChat(result.chat);
323
- await handleOpenChat(result.chat.id);
324
- setContacts((current) => current.map((item) => (item.id === contactId ? { ...item, hasChat: true, chatId: result.chat.id } : item)));
325
- setContactsPanelOpen(false);
326
- setTimeout(() => {
327
- chatWindowRef.current?.focusComposer();
328
- }, 80);
329
- } catch (requestError) {
330
- setError(requestError.message);
331
- } finally {
332
- setStartingContactId(null);
333
- }
334
- };
335
-
336
- const handleSendMessage = async ({ body, replyToId }) => {
337
- if (!socket) {
338
- throw new Error("Socket connection is not ready yet.");
339
- }
340
-
341
- await new Promise((resolve, reject) => {
342
- socket.emit(
343
- "send_message",
344
- {
345
- chatId: activeChatId,
346
- body,
347
- type: "text",
348
- replyToId
349
- },
350
- (response) => {
351
- if (response?.ok) {
352
- resolve(response.message);
353
- return;
354
- }
355
-
356
- reject(new Error(response?.error || "Failed to send message."));
357
- }
358
- );
359
- });
360
- };
361
-
362
- const handleSendMedia = async ({ file, caption }) => {
363
- if (!socket) {
364
- throw new Error("Socket connection is not ready yet.");
365
- }
366
-
367
- const formData = new FormData();
368
- formData.append("file", file);
369
-
370
- const upload = await apiFetch("/api/media", {
371
- method: "POST",
372
- token,
373
- formData
374
- });
375
-
376
- await new Promise((resolve, reject) => {
377
- socket.emit(
378
- "send_media",
379
- {
380
- chatId: activeChatId,
381
- mediaFileId: upload.mediaFile.id,
382
- body: caption,
383
- type: upload.type
384
- },
385
- (response) => {
386
- if (response?.ok) {
387
- resolve(response.message);
388
- return;
389
- }
390
-
391
- reject(new Error(response?.error || "Failed to send media."));
392
- }
393
- );
394
- });
395
- };
396
-
397
- const handleTyping = (isTyping) => {
398
- socket?.emit("typing", {
399
- chatId: activeChatId,
400
- isTyping
401
- });
402
- };
403
-
404
- const handleLoadOlder = async () => {
405
- if (!activeChatId || !activeMeta.hasMore || !activeMeta.nextBefore) {
406
- return;
407
- }
408
-
409
- setLoadingOlder(true);
410
- try {
411
- const data = await apiFetch(`/api/chats/${activeChatId}/messages?before=${encodeURIComponent(activeMeta.nextBefore)}&take=30`, { token });
412
- prependMessages(activeChatId, data.messages, {
413
- hasMore: Boolean(data.hasMore),
414
- nextBefore: data.nextBefore || null
415
- });
416
- } catch (requestError) {
417
- setError(requestError.message);
418
- } finally {
419
- setLoadingOlder(false);
420
- }
421
- };
422
-
423
- const handleDeleteMessage = async (messageId) => {
424
- try {
425
- const result = await apiFetch(`/api/messages/${messageId}`, {
426
- method: "DELETE",
427
- token
428
- });
429
- updateMessage(result.message);
430
- upsertChat(result.chat);
431
- } catch (requestError) {
432
- setError(requestError.message);
433
- }
434
- };
435
-
436
- const handleForwardMessage = async (messageId, targetChatId) => {
437
- try {
438
- const result = await apiFetch(`/api/messages/${messageId}/forward`, {
439
- method: "POST",
440
- token,
441
- body: { targetChatId }
442
- });
443
- addMessage(result.message);
444
- upsertChat(result.chat);
445
- } catch (requestError) {
446
- setError(requestError.message);
447
- }
448
- };
449
-
450
- if (!token) {
451
- return null;
452
- }
453
-
454
- return (
455
- <>
456
- <AppHead
457
- title="Dashboard"
458
- description="Dashboard OpenWA untuk mengelola percakapan, kontak, device, dan session WhatsApp."
459
- />
460
-
461
- <main className="h-screen overflow-hidden bg-[#161717] text-white">
462
- <div className="flex h-full w-full overflow-hidden bg-[#161717]">
463
- <ContactList
464
- chats={chats}
465
- activeChatId={activeChatId}
466
- loading={loading}
467
- onSelectChat={handleOpenChat}
468
- currentUser={user}
469
- query={chatQuery}
470
- onQueryChange={setChatQuery}
471
- />
472
-
473
- <section className="flex min-w-0 flex-1 flex-col">
474
- {error ? <div className="mx-4 mt-4 rounded-2xl border border-red-400/25 bg-red-500/10 px-4 py-3 text-sm text-red-100">{error}</div> : null}
475
-
476
- <div className="flex min-h-0 flex-1">
477
- <ChatWindow
478
- ref={chatWindowRef}
479
- chat={activeChat}
480
- messages={activeMessages}
481
- chats={chats}
482
- typingState={activeTyping}
483
- loading={loading}
484
- messagesLoading={messagesLoading}
485
- loadingOlder={loadingOlder}
486
- hasMoreMessages={activeMeta.hasMore}
487
- messageQuery={messageQuery}
488
- onMessageQueryChange={setMessageQuery}
489
- onLoadOlder={handleLoadOlder}
490
- onSendMessage={handleSendMessage}
491
- onSendMedia={handleSendMedia}
492
- onTyping={handleTyping}
493
- onDeleteMessage={handleDeleteMessage}
494
- onForwardMessage={handleForwardMessage}
495
- onOpenContacts={() => setContactsPanelOpen(true)}
496
- onOpenSettings={() => setSettingsOpen(true)}
497
- onLogout={() => {
498
- logout();
499
- router.replace("/");
500
- }}
501
- />
502
-
503
- <ContactsPanel
504
- contacts={contacts}
505
- loading={contactsLoading}
506
- open={contactsPanelOpen}
507
- query={contactQuery}
508
- onQueryChange={setContactQuery}
509
- onStartChat={handleStartChat}
510
- onClose={() => setContactsPanelOpen(false)}
511
- startingContactId={startingContactId}
512
- />
513
- </div>
514
- </section>
515
- </div>
516
- </main>
517
-
518
- <SettingsModal
519
- open={settingsOpen}
520
- sessions={sessions}
521
- activeSessionId={activeSessionId}
522
- onClose={() => setSettingsOpen(false)}
523
- onSelect={setActiveSession}
524
- onConnect={handleConnectSession}
525
- onDisconnect={handleDisconnectSession}
526
- sessionName={sessionName}
527
- sessionPhone={sessionPhone}
528
- onSessionNameChange={setSessionName}
529
- onSessionPhoneChange={setSessionPhone}
530
- onCreateSession={handleCreateSession}
531
- apiKeys={apiKeys}
532
- apiKeysLoading={apiKeysLoading}
533
- apiKeyName={apiKeyName}
534
- apiKeySecret={apiKeySecret}
535
- onApiKeyNameChange={setApiKeyName}
536
- onCreateApiKey={handleCreateApiKey}
537
- onRevokeApiKey={handleRevokeApiKey}
538
- />
539
- </>
540
- );
541
- }
@@ -1,62 +0,0 @@
1
- import { useEffect, useState } from "react";
2
- import { useRouter } from "next/router";
3
- import { AppHead } from "@/components/AppHead";
4
- import { AuthCard } from "@/components/AuthCard";
5
- import { apiFetch } from "@/lib/api";
6
- import { useAppStore } from "@/store/useAppStore";
7
-
8
- export default function HomePage() {
9
- const router = useRouter();
10
- const { token, hydrateAuth, setAuth } = useAppStore();
11
- const [mode, setMode] = useState("login");
12
- const [error, setError] = useState("");
13
- const [submitting, setSubmitting] = useState(false);
14
-
15
- useEffect(() => {
16
- hydrateAuth();
17
- }, [hydrateAuth]);
18
-
19
- useEffect(() => {
20
- if (token) {
21
- router.replace("/dashboard");
22
- }
23
- }, [router, token]);
24
-
25
- const handleSubmit = async (values) => {
26
- setSubmitting(true);
27
- setError("");
28
-
29
- try {
30
- const result = await apiFetch(`/api/auth/${mode}`, {
31
- method: "POST",
32
- body: values
33
- });
34
-
35
- setAuth(result);
36
- router.replace("/dashboard");
37
- } catch (requestError) {
38
- setError(requestError.message);
39
- } finally {
40
- setSubmitting(false);
41
- }
42
- };
43
-
44
- return (
45
- <>
46
- <AppHead
47
- title={mode === "login" ? "Login" : "Register"}
48
- description="Sign in or register for your OpenWA workspace to manage sessions and chats from one dashboard."
49
- />
50
-
51
- <main className="min-h-screen bg-[linear-gradient(180deg,#0b141a_0%,#111b21_100%)] px-6 py-8">
52
- <AuthCard
53
- mode={mode}
54
- error={error}
55
- busy={submitting}
56
- onModeChange={setMode}
57
- onSubmit={handleSubmit}
58
- />
59
- </main>
60
- </>
61
- );
62
- }
@@ -1,10 +0,0 @@
1
- const path = require("path");
2
-
3
- module.exports = {
4
- plugins: {
5
- tailwindcss: {
6
- config: path.join(__dirname, "tailwind.config.js")
7
- },
8
- autoprefixer: {}
9
- }
10
- };