@pubuduth-aplicy/chat-ui 2.1.73 → 2.1.74
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/package.json +2 -1
- package/src/components/common/CollapsibleSection.tsx +40 -0
- package/src/components/common/VirtualizedChatList.tsx +57 -0
- package/src/components/messages/Message.tsx +2 -1
- package/src/components/messages/MessageContainer.tsx +82 -62
- package/src/components/messages/MessageInput.tsx +50 -11
- package/src/components/messages/Messages.tsx +76 -46
- package/src/components/sidebar/Conversation.tsx +122 -163
- package/src/components/sidebar/Conversations.tsx +270 -40
- package/src/components/sidebar/SearchInput.tsx +16 -54
- package/src/hooks/useMessageStatus.ts +97 -0
- package/src/service/messageService.ts +86 -61
- package/src/stores/Zustant.ts +9 -4
- package/src/style/style.css +168 -0
- package/src/types/type.ts +24 -27
|
@@ -2,71 +2,33 @@
|
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import searchicon from "../../assets/icons8-search.svg";
|
|
4
4
|
import useChatUIStore from "../../stores/Zustant";
|
|
5
|
-
import { useGetConversations } from "../../hooks/queries/useChatApi";
|
|
6
|
-
import { useChatContext } from "../../providers/ChatProvider";
|
|
7
5
|
// import { MagnifyingGlass } from "@phosphor-icons/react"
|
|
8
6
|
// import useGetConversations from "../../hooks/useGetConversations";
|
|
9
7
|
// import useConversation from '../../zustand/useConversation'
|
|
10
8
|
// import toast from 'react-hot-toast';
|
|
11
9
|
|
|
12
10
|
const SearchInput = () => {
|
|
13
|
-
const {
|
|
14
|
-
const [search, setSearch] = useState(
|
|
15
|
-
const { setSelectedConversation } = useChatUIStore();
|
|
16
|
-
const { data } = useGetConversations(userId);
|
|
11
|
+
const { searchTerm, setSearchTerm } = useChatUIStore();
|
|
12
|
+
const [search, setSearch] = useState(searchTerm);
|
|
17
13
|
|
|
18
|
-
const
|
|
19
|
-
e.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const conversation = data?.find(
|
|
26
|
-
(c: {
|
|
27
|
-
_id: string;
|
|
28
|
-
participantDetails: {
|
|
29
|
-
username: string;
|
|
30
|
-
firstname?: string;
|
|
31
|
-
idpic?: string;
|
|
32
|
-
};
|
|
33
|
-
}) =>
|
|
34
|
-
c.participantDetails.username
|
|
35
|
-
.toLowerCase()
|
|
36
|
-
.includes(search.toLowerCase())
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
if (conversation) {
|
|
40
|
-
const updatedConversation = {
|
|
41
|
-
...conversation,
|
|
42
|
-
participantDetails: {
|
|
43
|
-
...conversation.participantDetails,
|
|
44
|
-
firstname: conversation.participantDetails.username || "Unknown",
|
|
45
|
-
idpic:
|
|
46
|
-
conversation.participantDetails.profilePic || "default-idpic.png",
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
setSelectedConversation(updatedConversation);
|
|
50
|
-
setSearch("");
|
|
51
|
-
}
|
|
52
|
-
console.error("No such user found!");
|
|
14
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
15
|
+
const term = e.target.value;
|
|
16
|
+
setSearch(term);
|
|
17
|
+
setSearchTerm(term);
|
|
53
18
|
};
|
|
54
|
-
|
|
55
19
|
return (
|
|
56
20
|
<>
|
|
57
|
-
<
|
|
58
|
-
<div className="
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
/>
|
|
67
|
-
</div>
|
|
21
|
+
<div className="chatSidebarSearchbar">
|
|
22
|
+
<div className="chatSidebarSearchbarContainer">
|
|
23
|
+
<img src={searchicon} className="chatSidebarSearchbarImg" />
|
|
24
|
+
<input
|
|
25
|
+
className="chatSidebarInput"
|
|
26
|
+
placeholder="Search…"
|
|
27
|
+
value={search}
|
|
28
|
+
onChange={handleChange}
|
|
29
|
+
/>
|
|
68
30
|
</div>
|
|
69
|
-
</
|
|
31
|
+
</div>
|
|
70
32
|
</>
|
|
71
33
|
);
|
|
72
34
|
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useChatContext } from "../providers/ChatProvider";
|
|
3
|
+
import useChatUIStore from '../stores/Zustant';
|
|
4
|
+
|
|
5
|
+
export const useMessageStatus = () => {
|
|
6
|
+
const { socket,userId } = useChatContext();
|
|
7
|
+
const { messages, setMessages, selectedConversation } = useChatUIStore();
|
|
8
|
+
|
|
9
|
+
// Handle status updates from server
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (!socket) return;
|
|
12
|
+
|
|
13
|
+
const handleStatusUpdate = (event: MessageEvent) => {
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(event.data);
|
|
16
|
+
|
|
17
|
+
if (parsed.event === 'messageStatusUpdated') {
|
|
18
|
+
const { messageId, status, chatId } = parsed.data;
|
|
19
|
+
|
|
20
|
+
setMessages(prev => prev.map(msg => {
|
|
21
|
+
if (msg._id === messageId) {
|
|
22
|
+
// Only update if new status is higher priority
|
|
23
|
+
const statusOrder = ['sent', 'delivered', 'read'];
|
|
24
|
+
const currentIdx = statusOrder.indexOf(msg.status);
|
|
25
|
+
const newIdx = statusOrder.indexOf(status);
|
|
26
|
+
|
|
27
|
+
if (newIdx > currentIdx) {
|
|
28
|
+
return { ...msg, status };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return msg;
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error('Error handling status update:', error);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
socket.addEventListener('message', handleStatusUpdate);
|
|
40
|
+
return () => socket.removeEventListener('message', handleStatusUpdate);
|
|
41
|
+
}, [socket, setMessages]);
|
|
42
|
+
|
|
43
|
+
// Send delivery confirmations for visible messages
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!socket || !selectedConversation) return;
|
|
46
|
+
|
|
47
|
+
const observer = new IntersectionObserver(
|
|
48
|
+
(entries) => {
|
|
49
|
+
entries.forEach(entry => {
|
|
50
|
+
if (entry.isIntersecting) {
|
|
51
|
+
const messageId = entry.target.getAttribute('data-message-id');
|
|
52
|
+
if (messageId) {
|
|
53
|
+
const message = messages.find(m => m._id === messageId);
|
|
54
|
+
if (message && message.status === 'sent') {
|
|
55
|
+
socket.send(JSON.stringify({
|
|
56
|
+
event: 'confirmDelivery',
|
|
57
|
+
data: {
|
|
58
|
+
messageId,
|
|
59
|
+
chatId: selectedConversation._id
|
|
60
|
+
}
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
{ threshold: 0.7 }
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Observe all messages in current chat
|
|
71
|
+
const messageElements = document.querySelectorAll('[data-message-id]');
|
|
72
|
+
messageElements.forEach(el => observer.observe(el));
|
|
73
|
+
|
|
74
|
+
return () => observer.disconnect();
|
|
75
|
+
}, [messages, socket, selectedConversation]);
|
|
76
|
+
|
|
77
|
+
// Mark messages as read when chat is active
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (!socket || !selectedConversation) return;
|
|
80
|
+
|
|
81
|
+
const unreadMessages = messages.filter(
|
|
82
|
+
msg => msg.status !== 'read' &&
|
|
83
|
+
msg.conversationId === selectedConversation._id &&
|
|
84
|
+
msg.senderId !== userId
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (unreadMessages.length > 0) {
|
|
88
|
+
socket.send(JSON.stringify({
|
|
89
|
+
event: 'messageRead',
|
|
90
|
+
data: {
|
|
91
|
+
messageIds: unreadMessages.map(m => m._id),
|
|
92
|
+
chatId: selectedConversation._id
|
|
93
|
+
}
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
}, [messages, socket, selectedConversation, userId]);
|
|
97
|
+
};
|
|
@@ -3,73 +3,98 @@ import { FileType } from "../components/common/FilePreview";
|
|
|
3
3
|
import { getApiClient } from "../lib/api/apiClient";
|
|
4
4
|
import { Path } from "../lib/api/endpoint";
|
|
5
5
|
|
|
6
|
-
export const sendMessage = async (
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
export const sendMessage = async (params: {
|
|
7
|
+
receiverId: string;
|
|
8
|
+
senderId: string;
|
|
9
|
+
message: string;
|
|
10
|
+
attachments: { type: FileType; url: string; name: string; size: number }[];
|
|
11
|
+
bookingId?: string;
|
|
12
|
+
serviceTitle?: string;
|
|
13
|
+
type?: "personal" | "service";
|
|
14
|
+
serviceId?: string;
|
|
15
|
+
}) => {
|
|
16
|
+
const {
|
|
17
|
+
receiverId,
|
|
18
|
+
senderId,
|
|
19
|
+
message,
|
|
20
|
+
attachments,
|
|
21
|
+
bookingId,
|
|
22
|
+
serviceTitle,
|
|
23
|
+
type,
|
|
24
|
+
serviceId,
|
|
25
|
+
} = params;
|
|
26
|
+
const apiClient = getApiClient();
|
|
27
|
+
|
|
28
|
+
const response = await apiClient.post(
|
|
29
|
+
`${Path.sendmessage}/${receiverId}/${senderId}`,
|
|
30
|
+
{
|
|
31
|
+
message,
|
|
32
|
+
attachments,
|
|
33
|
+
bookingId,
|
|
34
|
+
serviceTitle,
|
|
35
|
+
type,
|
|
36
|
+
serviceId,
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
return response.data;
|
|
15
40
|
};
|
|
16
41
|
|
|
17
42
|
|
|
18
|
-
export const fetchMessages = async (chatId: string|undefined, userid: string,pagenum:number) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
43
|
+
export const fetchMessages = async (chatId: string | undefined, userid: string, pagenum: number) => {
|
|
44
|
+
const apiClient = getApiClient();
|
|
45
|
+
try {
|
|
46
|
+
const response = await apiClient.get(`${Path.getmessage}/${chatId}/${userid}`, {
|
|
47
|
+
params: { pagenum, limit: 20 },
|
|
48
|
+
});
|
|
49
|
+
console.log(response); // Check the full response
|
|
50
|
+
return response.data; // Ensure 'data' exists or adjust accordingly
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error("Error fetching messages:", error);
|
|
53
|
+
return []; // Return a default empty array on error
|
|
54
|
+
}
|
|
30
55
|
};
|
|
31
56
|
|
|
32
57
|
export const setEditMessage = async ({
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
messageId,
|
|
59
|
+
userId,
|
|
60
|
+
newMessage
|
|
61
|
+
}: {
|
|
62
|
+
messageId: string;
|
|
63
|
+
userId: string;
|
|
64
|
+
newMessage: string;
|
|
65
|
+
}) => {
|
|
66
|
+
const apiClient = getApiClient();
|
|
67
|
+
try {
|
|
68
|
+
const response = await apiClient.put(`${Path.editMessage}/${messageId}`, {
|
|
69
|
+
userId,
|
|
70
|
+
newMessage
|
|
71
|
+
});
|
|
72
|
+
return response.data;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error("Error editing message:", error);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
53
78
|
|
|
54
79
|
|
|
55
80
|
export const deleteMessage = async ({
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
messageId,
|
|
82
|
+
userId
|
|
83
|
+
}: {
|
|
84
|
+
messageId: string;
|
|
85
|
+
userId: string;
|
|
86
|
+
}) => {
|
|
87
|
+
const apiClient = getApiClient();
|
|
88
|
+
try {
|
|
89
|
+
const response = await apiClient.delete(
|
|
90
|
+
`${Path.deleteMessage}/${messageId}`,
|
|
91
|
+
{
|
|
92
|
+
data: { userId } // Sending userId in the request body
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
return response.data;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error("Error deleting message:", error);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
};
|
package/src/stores/Zustant.ts
CHANGED
|
@@ -14,6 +14,11 @@ interface ChatUIState {
|
|
|
14
14
|
};
|
|
15
15
|
unreadMessageIds?: string[];
|
|
16
16
|
_id: string;
|
|
17
|
+
type?: 'personal' | 'service';
|
|
18
|
+
bookingId?: string;
|
|
19
|
+
title?: string;
|
|
20
|
+
serviceId?: string;
|
|
21
|
+
serviceTitle?: string;
|
|
17
22
|
} | null;
|
|
18
23
|
setSelectedConversation: (
|
|
19
24
|
selectedConversation: ChatUIState["selectedConversation"]
|
|
@@ -39,10 +44,10 @@ interface ChatUIState {
|
|
|
39
44
|
setMessages: (messages: ChatUIState["messages"] | ((prev: ChatUIState["messages"]) => ChatUIState["messages"])) => void;
|
|
40
45
|
updateMessageStatus: (messageId: string, status: string) => void;
|
|
41
46
|
toggleChat: () => void;
|
|
42
|
-
incrementUnreadCount: () => void;
|
|
43
47
|
onlineUsers: string[];
|
|
44
48
|
setOnlineUsers: (users: string[]) => void;
|
|
45
|
-
|
|
49
|
+
searchTerm: string;
|
|
50
|
+
setSearchTerm: (searchTerm: string) => void;
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
const useChatUIStore = create<ChatUIState>((set) => ({
|
|
@@ -64,8 +69,8 @@ const useChatUIStore = create<ChatUIState>((set) => ({
|
|
|
64
69
|
toggleChat: () => set((state) => ({ isChatOpen: !state.isChatOpen })),
|
|
65
70
|
onlineUsers: [],
|
|
66
71
|
setOnlineUsers: (users) => set({ onlineUsers: users }),
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
searchTerm: "",
|
|
73
|
+
setSearchTerm: (searchTerm) => set({ searchTerm }),
|
|
69
74
|
}));
|
|
70
75
|
|
|
71
76
|
export default useChatUIStore;
|
package/src/style/style.css
CHANGED
|
@@ -1645,4 +1645,172 @@
|
|
|
1645
1645
|
margin: 16px auto;
|
|
1646
1646
|
padding: 20px;
|
|
1647
1647
|
}
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
.circular-progress-container {
|
|
1651
|
+
position: relative;
|
|
1652
|
+
width: 100%;
|
|
1653
|
+
height: 120px;
|
|
1654
|
+
border-radius: 12px;
|
|
1655
|
+
overflow: hidden;
|
|
1656
|
+
display: flex;
|
|
1657
|
+
align-items: center;
|
|
1658
|
+
justify-content: center;
|
|
1659
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
.media-preview-background {
|
|
1663
|
+
position: absolute;
|
|
1664
|
+
top: 0;
|
|
1665
|
+
left: 0;
|
|
1666
|
+
width: 100%;
|
|
1667
|
+
height: 100%;
|
|
1668
|
+
/* z-index: 1; */
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
.blurred-preview {
|
|
1672
|
+
width: 100%;
|
|
1673
|
+
object-fit: cover;
|
|
1674
|
+
filter: blur(3px) brightness(0.6);
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
.circular-progress {
|
|
1678
|
+
position: relative;
|
|
1679
|
+
z-index: 2;
|
|
1680
|
+
display: flex;
|
|
1681
|
+
align-items: center;
|
|
1682
|
+
justify-content: center;
|
|
1683
|
+
width: 80px;
|
|
1684
|
+
height: 80px;
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
.circular-progress-svg {
|
|
1688
|
+
width: 100%;
|
|
1689
|
+
height: 100%;
|
|
1690
|
+
transform: rotate(-90deg);
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
.circular-progress-track {
|
|
1694
|
+
fill: none;
|
|
1695
|
+
stroke: rgba(255, 255, 255, 0.3);
|
|
1696
|
+
stroke-width: 3;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
.circular-progress-bar {
|
|
1700
|
+
fill: none;
|
|
1701
|
+
stroke: #ffffff;
|
|
1702
|
+
stroke-width: 3;
|
|
1703
|
+
stroke-linecap: round;
|
|
1704
|
+
transition: stroke-dasharray 0.5s ease-in-out;
|
|
1705
|
+
filter: drop-shadow(0 0 4px rgba(255, 255, 255, 0.5));
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
.circular-progress-text {
|
|
1709
|
+
position: absolute;
|
|
1710
|
+
top: 50%;
|
|
1711
|
+
left: 50%;
|
|
1712
|
+
transform: translate(-50%, -50%);
|
|
1713
|
+
color: #ffffff;
|
|
1714
|
+
font-size: 16px;
|
|
1715
|
+
font-weight: 700;
|
|
1716
|
+
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.8);
|
|
1717
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
/* Smooth animation for progress updates */
|
|
1721
|
+
.circular-progress-bar {
|
|
1722
|
+
animation: progress-smooth 0.3s ease-out;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
@keyframes progress-smooth {
|
|
1726
|
+
from {
|
|
1727
|
+
opacity: 0.7;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
to {
|
|
1731
|
+
opacity: 1;
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
/* Pulse animation while uploading */
|
|
1736
|
+
.circular-progress-container:not([data-complete="true"]) .circular-progress {
|
|
1737
|
+
animation: upload-pulse 2s ease-in-out infinite;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
@keyframes upload-pulse {
|
|
1741
|
+
|
|
1742
|
+
0%,
|
|
1743
|
+
100% {
|
|
1744
|
+
transform: scale(1);
|
|
1745
|
+
opacity: 1;
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
50% {
|
|
1749
|
+
transform: scale(1.05);
|
|
1750
|
+
opacity: 0.9;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
/* Completion state */
|
|
1755
|
+
.circular-progress-container[data-complete="true"] .circular-progress-bar {
|
|
1756
|
+
stroke: #22c55e;
|
|
1757
|
+
animation: completion-flash 0.6s ease-out;
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
.circular-progress-container[data-complete="true"] .circular-progress-text {
|
|
1761
|
+
color: #22c55e;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
@keyframes completion-flash {
|
|
1765
|
+
0% {
|
|
1766
|
+
stroke: #ffffff;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
50% {
|
|
1770
|
+
stroke: #22c55e;
|
|
1771
|
+
filter: drop-shadow(0 0 8px rgba(34, 197, 94, 0.8));
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
100% {
|
|
1775
|
+
stroke: #22c55e;
|
|
1776
|
+
filter: drop-shadow(0 0 4px rgba(34, 197, 94, 0.5));
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
/* Error state */
|
|
1781
|
+
.circular-progress-container[data-error="true"] .circular-progress-bar {
|
|
1782
|
+
stroke: #ef4444;
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
.circular-progress-container[data-error="true"] .circular-progress-text {
|
|
1786
|
+
color: #ef4444;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
/* Responsive adjustments */
|
|
1790
|
+
@media (max-width: 768px) {
|
|
1791
|
+
.circular-progress-container {
|
|
1792
|
+
width: 100px;
|
|
1793
|
+
height: 100px;
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
.circular-progress {
|
|
1797
|
+
width: 70px;
|
|
1798
|
+
height: 70px;
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
.circular-progress-text {
|
|
1802
|
+
font-size: 14px;
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
/* Optional: Add a subtle background overlay */
|
|
1807
|
+
.circular-progress-container::before {
|
|
1808
|
+
content: '';
|
|
1809
|
+
position: absolute;
|
|
1810
|
+
top: 0;
|
|
1811
|
+
left: 0;
|
|
1812
|
+
width: 100%;
|
|
1813
|
+
height: 100%;
|
|
1814
|
+
background: rgba(0, 0, 0, 0.2);
|
|
1815
|
+
/* z-index: 1; */
|
|
1648
1816
|
}
|
package/src/types/type.ts
CHANGED
|
@@ -25,50 +25,47 @@ export interface Conversation {
|
|
|
25
25
|
_id: string;
|
|
26
26
|
senderId: string;
|
|
27
27
|
message: string;
|
|
28
|
+
media: string[];
|
|
29
|
+
type?: 'user' | 'system' | 'system-completion';
|
|
30
|
+
status: MessageStatus;
|
|
28
31
|
chatId: string;
|
|
29
32
|
createdAt: string;
|
|
30
33
|
updatedAt: string;
|
|
31
34
|
__v: number;
|
|
32
|
-
}
|
|
35
|
+
};
|
|
33
36
|
updatedAt: string;
|
|
34
37
|
__v: number;
|
|
35
38
|
participantDetails: ParticipantDetails;
|
|
39
|
+
participants?: string[];
|
|
40
|
+
// readReceipts?: string[] | [];
|
|
41
|
+
unreadMessageIds: string[];
|
|
42
|
+
type?: string | undefined;
|
|
43
|
+
bookingId?: string;
|
|
44
|
+
serviceId?: string;
|
|
45
|
+
unreadMessageCount?: number
|
|
46
|
+
serviceTitle?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ServiceConversationGroup {
|
|
50
|
+
serviceId: string;
|
|
51
|
+
serviceTitle: string;
|
|
52
|
+
conversations: Conversation[];
|
|
36
53
|
}
|
|
37
54
|
|
|
38
|
-
export interface
|
|
39
|
-
|
|
55
|
+
export interface ParticipantGroup {
|
|
56
|
+
participantDetails: ParticipantDetails;
|
|
57
|
+
personalConversation: Conversation | null;
|
|
58
|
+
serviceConversations: ServiceConversationGroup[];
|
|
40
59
|
}
|
|
41
60
|
|
|
42
61
|
export interface ApiResponse {
|
|
43
62
|
success: boolean;
|
|
44
63
|
message: string;
|
|
45
|
-
serviceInfo:
|
|
64
|
+
serviceInfo: ParticipantGroup[];
|
|
46
65
|
}
|
|
47
66
|
|
|
48
67
|
export interface ConversationProps {
|
|
49
|
-
conversation:
|
|
50
|
-
lastMessage: {
|
|
51
|
-
_id: string;
|
|
52
|
-
senderId: string;
|
|
53
|
-
message: string;
|
|
54
|
-
media: string[];
|
|
55
|
-
type?: 'user' | 'system' | 'system-completion';
|
|
56
|
-
status: MessageStatus;
|
|
57
|
-
chatId: string;
|
|
58
|
-
createdAt: string;
|
|
59
|
-
updatedAt: string;
|
|
60
|
-
__v: number;
|
|
61
|
-
},
|
|
62
|
-
participantDetails: {
|
|
63
|
-
_id: string;
|
|
64
|
-
profilePic: string;
|
|
65
|
-
firstname: string;
|
|
66
|
-
idpic: string;
|
|
67
|
-
|
|
68
|
-
},
|
|
69
|
-
unreadMessageIds: string[];
|
|
70
|
-
_id: string;
|
|
71
|
-
};
|
|
68
|
+
conversation: Conversation;
|
|
72
69
|
lastIdx: boolean;
|
|
73
70
|
}
|
|
74
71
|
|