@pubuduth-aplicy/chat-ui 2.1.74 → 2.1.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,50 +1,153 @@
1
- # React + TypeScript + Vite
1
+ # @pubuduth-aplicy/chat-ui
2
2
 
3
- This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
3
+ A flexible and easy-to-use React chat UI component.
4
4
 
5
- Currently, two official plugins are available:
5
+ ## Description
6
6
 
7
- - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8
- - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
7
+ `@pubuduth-aplicy/chat-ui` provides a complete chat interface that can be easily integrated into any React application. It includes features like real-time messaging, user presence, and a customizable interface. The component is built with TypeScript, React, and Zustand for state management.
9
8
 
10
- ## Expanding the ESLint configuration
9
+ ## Installation
11
10
 
12
- If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
11
+ To install the package, use npm or yarn:
13
12
 
14
- - Configure the top-level `parserOptions` property like this:
13
+ ```bash
14
+ npm install @pubuduth-aplicy/chat-ui
15
+ ```
16
+
17
+ or
15
18
 
16
- ```js
17
- export default tseslint.config({
18
- languageOptions: {
19
- // other options...
20
- parserOptions: {
21
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
22
- tsconfigRootDir: import.meta.dirname,
23
- },
24
- },
25
- })
19
+ ```bash
20
+ yarn add @pubuduth-aplicy/chat-ui
26
21
  ```
27
22
 
28
- - Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
29
- - Optionally add `...tseslint.configs.stylisticTypeChecked`
30
- - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
31
-
32
- ```js
33
- // eslint.config.js
34
- import react from 'eslint-plugin-react'
35
-
36
- export default tseslint.config({
37
- // Set the react version
38
- settings: { react: { version: '18.3' } },
39
- plugins: {
40
- // Add the react plugin
41
- react,
42
- },
43
- rules: {
44
- // other rules...
45
- // Enable its recommended rules
46
- ...react.configs.recommended.rules,
47
- ...react.configs['jsx-runtime'].rules,
48
- },
49
- })
23
+ ## Usage
24
+
25
+ To use the chat component, you need to initialize the configuration, wrap your application with the `ChatProvider`, and then render the `Chat` component.
26
+
27
+ ### 1. Initialize the Configuration
28
+
29
+ First, you need to initialize the chat configuration at the entry point of your application (e.g., `index.tsx` or `main.tsx`).
30
+
31
+ ```tsx
32
+ // src/main.tsx
33
+ import React from 'react';
34
+ import ReactDOM from 'react-dom/client';
35
+ import App from './App';
36
+ import { initChatConfig } from '@pubuduth-aplicy/chat-ui';
37
+
38
+ // Initialize the chat configuration
39
+ initChatConfig({
40
+ apiUrl: 'YOUR_API_URL', // Your backend API URL
41
+ role: 'user', // Optional: 'user' or 'admin'
42
+ });
43
+
44
+ ReactDOM.createRoot(document.getElementById('root')!).render(
45
+ <React.StrictMode>
46
+ <App />
47
+ </React.StrictMode>,
48
+ );
49
+ ```
50
+
51
+ ### 2. Wrap with ChatProvider
52
+
53
+ Next, wrap your component tree with `ChatProvider`. This provider manages the chat state and WebSocket connection.
54
+
55
+ ```tsx
56
+ // src/App.tsx
57
+ import { ChatProvider, Chat } from '@pubuduth-aplicy/chat-ui';
58
+
59
+ function App() {
60
+ const userId = 'CURRENT_USER_ID'; // The ID of the currently logged-in user
61
+
62
+ return (
63
+ <ChatProvider userId={userId}>
64
+ <div className="App">
65
+ {/* Your other components */}
66
+ <Chat />
67
+ </div>
68
+ </ChatProvider>
69
+ );
70
+ }
71
+
72
+ export default App;
50
73
  ```
74
+
75
+ ### 3. Render the Chat Component
76
+
77
+ Finally, render the `<Chat />` component wherever you want the chat interface to appear.
78
+
79
+ ## Configuration
80
+
81
+ ### `initChatConfig(config)`
82
+
83
+ This function initializes the chat component's configuration. It must be called once before any chat components are rendered.
84
+
85
+ **Parameters:**
86
+
87
+ * `config` (object):
88
+ * `apiUrl` (string, required): The base URL of your chat backend. The WebSocket connection will be derived from this URL.
89
+ * `role` (string, optional): The role of the user, e.g., 'user' or 'admin'.
90
+
91
+ ## API Reference
92
+
93
+ ### `<ChatProvider />`
94
+
95
+ This component provides the chat context to its children.
96
+
97
+ **Props:**
98
+
99
+ * `userId` (string, required): The unique identifier for the current user.
100
+ * `children` (ReactNode, required): The child components. The `<Chat />` component must be a descendant of `ChatProvider`.
101
+
102
+ ### `<Chat />`
103
+
104
+ This component renders the main chat interface. It takes no props.
105
+
106
+ ## Development
107
+
108
+ To set up the project for local development:
109
+
110
+ 1. **Clone the repository:**
111
+ ```bash
112
+ git clone https://github.com/pubuduth-aplicy/chat-ui.git
113
+ cd chat-ui
114
+ ```
115
+
116
+ 2. **Install dependencies:**
117
+ ```bash
118
+ npm install
119
+ ```
120
+
121
+ 3. **Run the development server:**
122
+ This project uses Vite. To run the development server, you can add a `dev` script to your `package.json`:
123
+ ```json
124
+ "scripts": {
125
+ "dev": "vite",
126
+ "build": "tsc",
127
+ "prepare": "npm run build"
128
+ }
129
+ ```
130
+ Then run:
131
+ ```bash
132
+ npm run dev
133
+ ```
134
+
135
+ ## Building
136
+
137
+ To build the component for production, run the following command. This will transpile the TypeScript code.
138
+
139
+ ```bash
140
+ npm run build
141
+ ```
142
+
143
+ For a full production build, you should use `vite build`. You can add this to your `package.json`:
144
+
145
+ ```json
146
+ "scripts": {
147
+ "build:vite": "vite build"
148
+ }
149
+ ```
150
+
151
+ ## License
152
+
153
+ This project is licensed under the [ISC License](LICENSE).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pubuduth-aplicy/chat-ui",
3
- "version": "2.1.74",
3
+ "version": "2.1.76",
4
4
  "description": "This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.",
5
5
  "license": "ISC",
6
6
  "author": "",
@@ -2,6 +2,7 @@
2
2
  export interface ChatConfig {
3
3
  apiUrl: string;
4
4
  role?: string;
5
+ cdnUrl?: string;
5
6
  }
6
7
 
7
8
  let chatConfig: ChatConfig;
@@ -51,7 +51,7 @@ export const Chat = () => {
51
51
  return (
52
52
  <div className="container mx-auto mb-5">
53
53
  <div className="grid-container">
54
- <div className={`sidebarContainer`}>
54
+ <div className={`sidebarContainer dark:bg-gray-800`}>
55
55
  <Sidebar />
56
56
  </div>
57
57
  <div className="messageContainer">
@@ -24,7 +24,7 @@ const CollapsibleSection = ({
24
24
  className="flex justify-between items-center p-2 cursor-pointer hover:bg-gray-50"
25
25
  onClick={toggleOpen}
26
26
  >
27
- <p className="text-xs uppercase">{title}</p>
27
+ <p className="text-xs uppercase text-gray-900 dark:text-gray-200">{title}</p>
28
28
  <ChevronDown
29
29
  size={20}
30
30
  className={`transform transition-transform duration-200 ${
@@ -32,7 +32,7 @@ const CollapsibleSection = ({
32
32
  }`}
33
33
  />
34
34
  </div>
35
- {isOpen && <div className="p-2">{children}</div>}
35
+ {isOpen && <div className="p-2 text-gray-900 dark:text-gray-200">{children}</div>}
36
36
  </div>
37
37
  );
38
38
  };
@@ -34,6 +34,7 @@ interface MessageProps {
34
34
  type?: "user" | "system" | "system-completion";
35
35
  meta?: {
36
36
  bookingDetails?: {
37
+ status: string; // e.g., "confirmed", "pending", "cancelled"
37
38
  serviceId: string;
38
39
  date: string;
39
40
  time: string;
@@ -47,18 +48,30 @@ interface MessageProps {
47
48
 
48
49
  const Message = ({ message }: MessageProps) => {
49
50
  const { userId } = useChatContext();
50
- const { apiUrl } = getChatConfig();
51
+ const { apiUrl,cdnUrl } = getChatConfig();
51
52
 
52
53
  if (message.type === "system") {
53
54
  return (
54
55
  <div className="system-message booking-details">
55
- <h4>Booking Confirmed</h4>
56
+ <h4>{message.meta?.bookingDetails?.status || 'Unknown'}</h4>
56
57
  <div className="details">
57
58
  <p>Service: {message.meta?.bookingDetails?.serviceId}</p>
58
59
  <p>Date: {message.meta?.bookingDetails?.date}</p>
59
60
  <p>Time: {message.meta?.bookingDetails?.time}</p>
60
61
  <p>Price: ${message.meta?.bookingDetails?.price}</p>
61
62
  </div>
63
+ <button
64
+ className="confirm-button"
65
+ onClick={() => {
66
+ // Add your confirmation logic here
67
+ console.log('Booking confirmed!');
68
+ }}
69
+ >
70
+ Confirm Booking
71
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
72
+ <path d="M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.4-6.425a.247.247 0 0 1 .02-.022Z" />
73
+ </svg>
74
+ </button>
62
75
  </div>
63
76
  );
64
77
  }
@@ -194,27 +207,6 @@ const Message = ({ message }: MessageProps) => {
194
207
  }
195
208
  };
196
209
 
197
- // const handleDownload = async (url: string, name: string, index: number) => {
198
- // setDownloadingIndex(index);
199
- // try {
200
- // var xhr = new XMLHttpRequest()
201
- // xhr.open('HEAD', url, false)
202
- // xhr.send()
203
- // const blob = await response.blob();
204
- // const link = document.createElement("a");
205
- // link.href = URL.createObjectURL(blob);
206
- // link.download = name;
207
- // link.click();
208
- // URL.revokeObjectURL(link.href);
209
- // } catch (error) {
210
- // console.error("Download failed:", error);
211
- // } finally {
212
- // setDownloadingIndex(null);
213
- // }
214
- // };
215
-
216
- // const renderMedia = () => {
217
- // if (!message.media || message.media.length === 0) return null;
218
210
 
219
211
  const handleDownload = async (url: string, name: string, index: number) => {
220
212
  setDownloadingIndex(index);
@@ -224,7 +216,6 @@ const Message = ({ message }: MessageProps) => {
224
216
  setDownloadController(controller);
225
217
 
226
218
  try {
227
- // First try to download directly
228
219
  try {
229
220
  const response = await fetch(
230
221
  `${apiUrl}${Path.apiProxy}?url=${encodeURIComponent(
@@ -320,7 +311,7 @@ const Message = ({ message }: MessageProps) => {
320
311
  <div className="circular-progress-container">
321
312
  <div className="media-preview-background">
322
313
  <img
323
- src={media.url}
314
+ src={`${cdnUrl}${`${cdnUrl}${media.url}`}`}
324
315
  alt={media.name}
325
316
  className="blurred-preview"
326
317
  />
@@ -365,16 +356,16 @@ const Message = ({ message }: MessageProps) => {
365
356
  <>
366
357
  {media.type === "image" ? (
367
358
  <img
368
- src={media.url}
359
+ src={`${cdnUrl}${media.url}`}
369
360
  alt={media.name}
370
361
  className="media-content"
371
- onClick={() => window.open(media.url, "_blank")}
362
+ onClick={() => window.open(`${cdnUrl}${media.url}`, "_blank")}
372
363
  />
373
364
  ) : media.type === "video" ? (
374
365
  <video controls className="media-content">
375
366
  <source
376
- src={media.url}
377
- type={`video/${media.url.split(".").pop()}`}
367
+ src={`${cdnUrl}${media.url}`}
368
+ type={`video/${`${cdnUrl}${media.url}`.split(".").pop()}`}
378
369
  />
379
370
  </video>
380
371
  ) : (
@@ -397,7 +388,7 @@ const Message = ({ message }: MessageProps) => {
397
388
  if (downloadingIndex === index) {
398
389
  cancelDownload();
399
390
  } else {
400
- handleDownload(media.url, media.name, index);
391
+ handleDownload(`${cdnUrl}${media.url}`, media.name, index);
401
392
  }
402
393
  }}
403
394
  title={downloadingIndex === index ? "Cancel" : "Download"}
@@ -490,19 +481,28 @@ const Message = ({ message }: MessageProps) => {
490
481
  }
491
482
  };
492
483
 
484
+ const isMessageOlderThanOneDay = (createdAt: string | Date) => {
485
+ const messageDate = new Date(createdAt);
486
+ const now = new Date();
487
+ const oneDayInMs = 24 * 60 * 60 * 1000;
488
+ return now.getTime() - messageDate.getTime() > oneDayInMs;
489
+ };
490
+
491
+ const hasMedia = message.media && message.media.length > 0;
492
+
493
493
  return (
494
494
  <div className="chat-container">
495
495
  <div className={`message-row ${alignItems}`}>
496
496
  <div
497
497
  className="bubble-container"
498
- onMouseEnter={() => fromMe && setShowOptions(true)}
498
+ onMouseEnter={() => fromMe && !hasMedia && setShowOptions(true)}
499
499
  onMouseLeave={() =>
500
500
  fromMe && !showDeleteOption && setShowOptions(false)
501
501
  }
502
502
  >
503
503
  {message.status === "deleted" ? (
504
504
  <div className="chat-bubble compact-bubble deleted-message">
505
- <div className="message-text">This message was deleted</div>
505
+ <div className="message-text text-gray-900 dark:text-gray-100">This message was deleted</div>
506
506
  </div>
507
507
  ) : (
508
508
  (message.message ||
@@ -533,7 +533,7 @@ const Message = ({ message }: MessageProps) => {
533
533
  </div>
534
534
  ) : (
535
535
  message.message && (
536
- <div className="message-text">{message.message}</div>
536
+ <div className="message-text text-gray-900 dark:text-gray-100">{message.message}</div>
537
537
  )
538
538
  )}
539
539
 
@@ -541,7 +541,9 @@ const Message = ({ message }: MessageProps) => {
541
541
  {fromMe &&
542
542
  showOptions &&
543
543
  !isEditingMode &&
544
- !message.isDeleted && (
544
+ !message.isDeleted &&
545
+ !hasMedia &&
546
+ !isMessageOlderThanOneDay(message.createdAt) && (
545
547
  <div className="message-options">
546
548
  <button
547
549
  className="message-option-btn edit-btn"
@@ -8,6 +8,7 @@ import { useChatContext } from "../../providers/ChatProvider";
8
8
  import { getChatConfig } from "../../Chat.config";
9
9
 
10
10
  const MessageContainer = () => {
11
+ const { userId } = useChatContext();
11
12
  const { selectedConversation, setSelectedConversation, setOnlineUsers } =
12
13
  useChatUIStore();
13
14
  const { socket, isUserOnline } = useChatContext();
@@ -67,6 +68,7 @@ const MessageContainer = () => {
67
68
  event: "joinChat",
68
69
  data: {
69
70
  chatId: selectedConversation._id,
71
+ userId: userId
70
72
  },
71
73
  })
72
74
  );
@@ -98,19 +100,19 @@ const MessageContainer = () => {
98
100
 
99
101
  // Listen for online users updates
100
102
 
101
- const { userId } = useChatContext();
103
+
102
104
 
103
- // const participantDetails = Array.isArray(selectedConversation?.participantDetails)
104
- // ? selectedConversation?.participantDetails
105
- // : [selectedConversation?.participantDetails].filter(Boolean);
105
+ const participantDetails = Array.isArray(selectedConversation?.participantDetails)
106
+ ? selectedConversation?.participantDetails
107
+ : [selectedConversation?.participantDetails].filter(Boolean);
106
108
 
107
- // const participant = participantDetails.find(
108
- // (p: any) => p._id !== userId
109
- // );
110
-
111
- const participant = selectedConversation?.participantDetails?.find(
109
+ const participant = participantDetails.find(
112
110
  (p: any) => p._id !== userId
113
111
  );
112
+
113
+ // const participant = selectedConversation?.participantDetails?.find(
114
+ // (p: any) => p._id !== userId
115
+ // );
114
116
 
115
117
  const isOnline = isUserOnline(participant?._id || "");
116
118
 
@@ -138,7 +140,7 @@ const MessageContainer = () => {
138
140
  <img
139
141
  className="chatMessageContainerInnerImg"
140
142
  alt="Profile"
141
- src={participant?.profilePic}
143
+ src={participant?.profilePicture}
142
144
  />
143
145
  )}
144
146
  <div className="chatMessageContainerOutter">
@@ -155,7 +157,7 @@ const MessageContainer = () => {
155
157
  ) : (
156
158
  <>
157
159
  <p className="chatMessageContainerOutterDiv_name">
158
- {participant?.firstname || ""}
160
+ {participant?.name || ""}
159
161
  </p>
160
162
  <p className="text-sm">
161
163
  {isOnline ? "Online" : "Offline"}
@@ -57,6 +57,15 @@ const MessageInput = () => {
57
57
  const generateTempId = () =>
58
58
  `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
59
59
  // Join chat room when conversation is selected
60
+
61
+ const participantDetails = Array.isArray(selectedConversation?.participantDetails)
62
+ ? selectedConversation?.participantDetails
63
+ : [selectedConversation?.participantDetails].filter(Boolean);
64
+
65
+ const otherParticipant = participantDetails.find(
66
+ (p: any) => p._id !== userId
67
+ );
68
+
60
69
  useEffect(() => {
61
70
  if (selectedConversation?._id && socket?.readyState === WebSocket.OPEN) {
62
71
  sendMessage({
@@ -67,7 +76,8 @@ const MessageInput = () => {
67
76
  });
68
77
  }
69
78
  }, [selectedConversation?._id, socket, sendMessage]);
70
-
79
+ console.log('selected', selectedConversation);
80
+
71
81
  useEffect(() => {
72
82
  // Clear all input state when conversation changes
73
83
  setMessage("");
@@ -93,6 +103,7 @@ const MessageInput = () => {
93
103
  data: {
94
104
  chatId: selectedConversation._id,
95
105
  userId,
106
+ reciverId: otherParticipant?._id,
96
107
  },
97
108
  });
98
109
  }
@@ -105,6 +116,7 @@ const MessageInput = () => {
105
116
  data: {
106
117
  chatId: selectedConversation._id,
107
118
  userId,
119
+ reciverId: otherParticipant?._id,
108
120
  },
109
121
  });
110
122
  }
@@ -192,6 +204,7 @@ const MessageInput = () => {
192
204
  const response = await apiClient.post(`${Path.preSignUrl}`, {
193
205
  fileName: file.name,
194
206
  fileType: file.type,
207
+ conversationId: selectedConversation?._id
195
208
  });
196
209
 
197
210
  const { signedUrl, fileUrl } = await response.data;
@@ -225,18 +238,12 @@ const MessageInput = () => {
225
238
  });
226
239
  };
227
240
 
228
- // const participantDetails = Array.isArray(selectedConversation?.participantDetails)
229
- // ? selectedConversation?.participantDetails
230
- // : [selectedConversation?.participantDetails].filter(Boolean);
241
+
231
242
 
232
- // const otherParticipant = participantDetails.find(
233
- // (p: any) => p._id !== userId
243
+ // const otherParticipant = selectedConversation?.participantDetails?.find(
244
+ // (p:any) => p._id !== userId
234
245
  // );
235
246
 
236
- const otherParticipant = selectedConversation?.participantDetails?.find(
237
- (p:any) => p._id !== userId
238
- );
239
-
240
247
  const handleSubmit = useCallback(
241
248
  async (e: React.FormEvent) => {
242
249
  e.preventDefault();
@@ -16,32 +16,10 @@ const Conversation = ({ conversation }: ConversationProps) => {
16
16
  );
17
17
  const { userId } = useChatContext();
18
18
 
19
- // This should return the other participant
20
19
  const participant = conversation.participantDetails?.find(
21
20
  (p:any) => p._id !== userId
22
21
  );
23
22
 
24
-
25
- // const handleSelectConversation = () => {
26
- // console.log("Selected Conversation Data:", JSON.stringify(conversation, null, 2));
27
- // setSelectedConversation(conversation);
28
-
29
- // const unreadMessages = conversation.unreadMessageIds || [];
30
- // if (unreadMessages.length > 0 && socket?.readyState === WebSocket.OPEN) {
31
- // console.log('unread meassge',unreadMessages);
32
-
33
- // const message = {
34
- // event: "messageRead",
35
- // data: {
36
- // messageIds: unreadMessages,
37
- // senderId: participant?._id,
38
- // chatId: conversation._id,
39
- // },
40
- // };
41
- // socket.send(JSON.stringify(message));
42
- // }
43
- // };
44
-
45
23
  const handleSelectConversation = () => {
46
24
  console.log(
47
25
  "Selected Conversation Data:",
@@ -62,7 +40,6 @@ const Conversation = ({ conversation }: ConversationProps) => {
62
40
  },
63
41
  };
64
42
  socket.send(JSON.stringify(message));
65
- // updateConversationReadStatus(conversation._id, unreadMessageIds);
66
43
  }
67
44
  };
68
45
 
@@ -87,7 +64,6 @@ const Conversation = ({ conversation }: ConversationProps) => {
87
64
  };
88
65
  }, [socket, setOnlineUsers]);
89
66
 
90
- // Temporary debug in your component
91
67
  useEffect(() => {
92
68
  console.log("Current conversation state:", conversation);
93
69
  }, [conversation]);
@@ -98,7 +74,7 @@ const Conversation = ({ conversation }: ConversationProps) => {
98
74
  const conversationName =
99
75
  conversation.type === "service" && conversation.bookingId
100
76
  ? `Booking #${conversation.bookingId}`
101
- : participant?.firstname || "Conversation";
77
+ : participant?.name || "Conversation";
102
78
 
103
79
  const lastMessageTimestamp = new Date(
104
80
  conversation.lastMessage?.updatedAt || conversation.lastMessage?.createdAt
@@ -123,7 +99,7 @@ const Conversation = ({ conversation }: ConversationProps) => {
123
99
  <>
124
100
  <img
125
101
  className="w-10 h-10 rounded-full"
126
- src={participant?.profilePic}
102
+ src={participant?.profilePicture}
127
103
  alt="User Avatar"
128
104
  />
129
105
  <span
@@ -150,7 +126,7 @@ const Conversation = ({ conversation }: ConversationProps) => {
150
126
  </div>
151
127
  {/* <span className="text-xs text-gray-500">{lastMessageTimestamp}</span> */}
152
128
  </div>
153
- <p className="text-xs text-gray-600 truncate">
129
+ <p className="text-xs text-gray-600 truncate dark:text-gray-500">
154
130
  {conversation.lastMessage?.status === "deleted"
155
131
  ? "This message was deleted"
156
132
  : conversation.lastMessage?.media?.length > 0
@@ -35,6 +35,8 @@ const Conversations = () => {
35
35
  const allServiceChatsMap = new Map<string, GroupedServiceChats[string]>();
36
36
 
37
37
  groups.forEach((group) => {
38
+ console.log("group", group.personalConversation);
39
+
38
40
  if (group.personalConversation) {
39
41
  allGeneralChats.push(group.personalConversation);
40
42
  }
@@ -225,7 +227,7 @@ const Conversations = () => {
225
227
  convo.participantDetails?.some((p:any) =>
226
228
  typeof p === "string"
227
229
  ? p.toLowerCase().includes(lowerSearch)
228
- : p.firstname?.toLowerCase().includes(lowerSearch)
230
+ : p.name?.toLowerCase().includes(lowerSearch)
229
231
  )
230
232
  );
231
233
 
@@ -244,6 +246,9 @@ const Conversations = () => {
244
246
  .filter(([_, group]) => group.conversations.length > 0) // Remove empty groups
245
247
  );
246
248
 
249
+ console.log("filteredGeneralChats",filteredGeneralChats);
250
+ console.log("filteredGroupedServiceChats");
251
+
247
252
  return (
248
253
  <div className="chatSidebarConversations">
249
254
  {isEmpty ? (
@@ -18,8 +18,8 @@ const SearchInput = () => {
18
18
  };
19
19
  return (
20
20
  <>
21
- <div className="chatSidebarSearchbar">
22
- <div className="chatSidebarSearchbarContainer">
21
+ <div className="chatSidebarSearchbar bg-{FFFFFF} dark:bg-gray-800 text-gray-900 dark:text-gray-300">
22
+ <div className="chatSidebarSearchbarContainer bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-300">
23
23
  <img src={searchicon} className="chatSidebarSearchbarImg" />
24
24
  <input
25
25
  className="chatSidebarInput"
@@ -6,7 +6,6 @@ export const useMessageStatus = () => {
6
6
  const { socket,userId } = useChatContext();
7
7
  const { messages, setMessages, selectedConversation } = useChatUIStore();
8
8
 
9
- // Handle status updates from server
10
9
  useEffect(() => {
11
10
  if (!socket) return;
12
11
 
@@ -19,7 +18,6 @@ export const useMessageStatus = () => {
19
18
 
20
19
  setMessages(prev => prev.map(msg => {
21
20
  if (msg._id === messageId) {
22
- // Only update if new status is higher priority
23
21
  const statusOrder = ['sent', 'delivered', 'read'];
24
22
  const currentIdx = statusOrder.indexOf(msg.status);
25
23
  const newIdx = statusOrder.indexOf(status);
@@ -40,7 +38,6 @@ export const useMessageStatus = () => {
40
38
  return () => socket.removeEventListener('message', handleStatusUpdate);
41
39
  }, [socket, setMessages]);
42
40
 
43
- // Send delivery confirmations for visible messages
44
41
  useEffect(() => {
45
42
  if (!socket || !selectedConversation) return;
46
43
 
@@ -67,14 +64,12 @@ export const useMessageStatus = () => {
67
64
  { threshold: 0.7 }
68
65
  );
69
66
 
70
- // Observe all messages in current chat
71
67
  const messageElements = document.querySelectorAll('[data-message-id]');
72
68
  messageElements.forEach(el => observer.observe(el));
73
69
 
74
70
  return () => observer.disconnect();
75
71
  }, [messages, socket, selectedConversation]);
76
72
 
77
- // Mark messages as read when chat is active
78
73
  useEffect(() => {
79
74
  if (!socket || !selectedConversation) return;
80
75
 
@@ -9,6 +9,7 @@ import React, {
9
9
  useCallback,
10
10
  } from "react";
11
11
  import { getChatConfig } from "../Chat.config";
12
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
12
13
 
13
14
  interface ChatProviderProps {
14
15
  userId: string;
@@ -26,6 +27,7 @@ interface ChatContextType {
26
27
  }
27
28
 
28
29
  const ChatContext = createContext<ChatContextType | null>(null);
30
+ const queryClient = new QueryClient();
29
31
 
30
32
  export const ChatProvider: React.FC<ChatProviderProps> = ({
31
33
  userId,
@@ -131,18 +133,20 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
131
133
  );
132
134
 
133
135
  return (
134
- <ChatContext.Provider
135
- value={{
136
- socket,
137
- userId,
138
- onlineUsers,
139
- sendMessage,
140
- isConnected,
141
- isUserOnline,
142
- }}
143
- >
144
- {children}
145
- </ChatContext.Provider>
136
+ <QueryClientProvider client={queryClient}>
137
+ <ChatContext.Provider
138
+ value={{
139
+ socket,
140
+ userId,
141
+ onlineUsers,
142
+ sendMessage,
143
+ isConnected,
144
+ isUserOnline,
145
+ }}
146
+ >
147
+ {children}
148
+ </ChatContext.Provider>
149
+ </QueryClientProvider>
146
150
  );
147
151
  };
148
152
 
@@ -34,6 +34,7 @@ export const sendMessage = async (params: {
34
34
  serviceTitle,
35
35
  type,
36
36
  serviceId,
37
+ messageType: "user"
37
38
  }
38
39
  );
39
40
  return response.data;
@@ -46,11 +47,11 @@ export const fetchMessages = async (chatId: string | undefined, userid: string,
46
47
  const response = await apiClient.get(`${Path.getmessage}/${chatId}/${userid}`, {
47
48
  params: { pagenum, limit: 20 },
48
49
  });
49
- console.log(response); // Check the full response
50
- return response.data; // Ensure 'data' exists or adjust accordingly
50
+ console.log(response);
51
+ return response.data;
51
52
  } catch (error) {
52
53
  console.error("Error fetching messages:", error);
53
- return []; // Return a default empty array on error
54
+ return [];
54
55
  }
55
56
  };
56
57
 
@@ -89,7 +90,7 @@ export const deleteMessage = async ({
89
90
  const response = await apiClient.delete(
90
91
  `${Path.deleteMessage}/${messageId}`,
91
92
  {
92
- data: { userId } // Sending userId in the request body
93
+ data: { userId }
93
94
  }
94
95
  );
95
96
  return response.data;
@@ -9,19 +9,16 @@ export const getAllConversationData = async (userid: string) => {
9
9
  try {
10
10
  const {role} = getChatConfig();
11
11
  const apiClient = getApiClient();
12
- //create common variable and assign the value of role
13
- // check if the user is an admin
14
- //create url for admon and user
12
+
15
13
  const endpoint = role === 'admin'
16
14
  ? `${Path.getConversationListByAdmin}`
17
15
  : `${Path.getconversation}/${userid}`;
18
- //create url for admon and user
16
+
19
17
  const res = await apiClient.get<ApiResponse>(endpoint);
20
18
  if (res.data) {
21
19
  console.log("API Response: ", res.data);
22
20
 
23
21
  }
24
- // Access conversations with participant details from the API response
25
22
  const conversationsWithParticipantDetails = res.data.serviceInfo;
26
23
  console.log("conversationsWithParticipantDetails", res.data.serviceInfo);
27
24
 
@@ -1813,4 +1813,96 @@
1813
1813
  height: 100%;
1814
1814
  background: rgba(0, 0, 0, 0.2);
1815
1815
  /* z-index: 1; */
1816
+ }
1817
+
1818
+ .confirm-button {
1819
+ background: linear-gradient(135deg, #2AA8A6, #1E9C9A);
1820
+ color: white;
1821
+ border: none;
1822
+ padding: 12px 24px;
1823
+ border-radius: 8px;
1824
+ font-weight: 600;
1825
+ font-size: 16px;
1826
+ cursor: pointer;
1827
+ display: flex;
1828
+ align-items: center;
1829
+ justify-content: center;
1830
+ gap: 8px;
1831
+ margin-top: 16px;
1832
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
1833
+ transition: all 0.3s ease;
1834
+ position: relative;
1835
+ overflow: hidden;
1836
+ }
1837
+
1838
+ .confirm-button:hover {
1839
+ background: linear-gradient(135deg, #1E9C9A, #168A88);
1840
+ transform: translateY(-2px);
1841
+ box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);
1842
+ }
1843
+
1844
+ .confirm-button:active {
1845
+ transform: translateY(0);
1846
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
1847
+ }
1848
+
1849
+ .confirm-button::before {
1850
+ content: '';
1851
+ position: absolute;
1852
+ top: 0;
1853
+ left: -100%;
1854
+ width: 100%;
1855
+ height: 100%;
1856
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
1857
+ transition: 0.5s;
1858
+ }
1859
+
1860
+ .confirm-button:hover::before {
1861
+ left: 100%;
1862
+ }
1863
+
1864
+ .confirm-button svg {
1865
+ transition: transform 0.3s ease;
1866
+ }
1867
+
1868
+ .confirm-button:hover svg {
1869
+ transform: scale(1.1);
1870
+ }
1871
+
1872
+
1873
+ .message-option-btn.disabled {
1874
+ opacity: 0.5;
1875
+ cursor: not-allowed;
1876
+ pointer-events: none;
1877
+ }
1878
+
1879
+ .message-option-btn[disabled] {
1880
+ opacity: 0.5;
1881
+ cursor: not-allowed;
1882
+ }
1883
+
1884
+ /* Tooltip styles */
1885
+ .message-option-btn {
1886
+ position: relative;
1887
+ overflow: hidden;
1888
+ }
1889
+
1890
+ .message-option-btn:hover::before {
1891
+ content: attr(title);
1892
+ position: absolute;
1893
+ bottom: 100%;
1894
+ left: 50%;
1895
+ transform: translateX(-50%);
1896
+ background: #333;
1897
+ color: white;
1898
+ padding: 4px 8px;
1899
+ border-radius: 4px;
1900
+ font-size: 12px;
1901
+ white-space: nowrap;
1902
+ z-index: 10;
1903
+ margin-bottom: 5px;
1904
+ }
1905
+
1906
+ .message-option-btn.disabled:hover::before {
1907
+ background: #666;
1816
1908
  }
@@ -1,15 +0,0 @@
1
- // import { ChatWindowProps } from "./types/type";
2
-
3
-
4
- // export const ChatWindow: React.FC<ChatWindowProps> = ({ userId}) => {
5
- // // const { messages, sendMessage, sendFile } = useChat(userId);
6
- // // const [file, setFile] = useState<File | null>(null);
7
-
8
- // return (
9
- // <div className="w-full max-w-lg border p-4 rounded-lg shadow-lg bg-white">
10
- // <MessageList messages={messages} />
11
- // <FileUploader file={file} setFile={setFile} onUpload={sendFile} />
12
- // <MessageInput onSend={sendMessage} />
13
- // </div>
14
- // );
15
- // };