@algenium/blocks 1.16.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +466 -164
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -4
- package/dist/index.d.ts +32 -4
- package/dist/index.js +467 -165
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -6737,7 +6737,9 @@ function ChatSidebarProvider({
|
|
|
6737
6737
|
const [isLoading, setIsLoading] = React7.useState(false);
|
|
6738
6738
|
const [activeCaseId, setActiveCaseId] = React7.useState(null);
|
|
6739
6739
|
const [activeRoomId, setActiveRoomId] = React7.useState(null);
|
|
6740
|
+
const [searchQuery, setSearchQuery] = React7.useState("");
|
|
6740
6741
|
const fetchRef = React7.useRef(fetchConversations);
|
|
6742
|
+
const prevOpenRef = React7.useRef(isOpen);
|
|
6741
6743
|
fetchRef.current = fetchConversations;
|
|
6742
6744
|
const persistOpen = React7.useCallback((open2) => {
|
|
6743
6745
|
setIsOpen(open2);
|
|
@@ -6759,6 +6761,32 @@ function ChatSidebarProvider({
|
|
|
6759
6761
|
React7.useEffect(() => {
|
|
6760
6762
|
void loadConversations();
|
|
6761
6763
|
}, [loadConversations]);
|
|
6764
|
+
React7.useEffect(() => {
|
|
6765
|
+
if (isOpen && !prevOpenRef.current) {
|
|
6766
|
+
void loadConversations();
|
|
6767
|
+
}
|
|
6768
|
+
prevOpenRef.current = isOpen;
|
|
6769
|
+
}, [isOpen, loadConversations]);
|
|
6770
|
+
const clearRoomUnread = React7.useCallback((roomId) => {
|
|
6771
|
+
setConversations(
|
|
6772
|
+
(prev) => prev.map((conv) => ({
|
|
6773
|
+
...conv,
|
|
6774
|
+
rooms: conv.rooms.map(
|
|
6775
|
+
(room) => room.id === roomId ? { ...room, unreadCount: 0 } : room
|
|
6776
|
+
)
|
|
6777
|
+
}))
|
|
6778
|
+
);
|
|
6779
|
+
}, []);
|
|
6780
|
+
const totalUnreadCount = React7.useMemo(
|
|
6781
|
+
() => conversations.reduce(
|
|
6782
|
+
(sum, conv) => sum + conv.rooms.reduce(
|
|
6783
|
+
(roomSum, room) => roomSum + (room.unreadCount ?? 0),
|
|
6784
|
+
0
|
|
6785
|
+
),
|
|
6786
|
+
0
|
|
6787
|
+
),
|
|
6788
|
+
[conversations]
|
|
6789
|
+
);
|
|
6762
6790
|
const toggle = React7.useCallback(() => persistOpen(!isOpen), [isOpen, persistOpen]);
|
|
6763
6791
|
const open = React7.useCallback(() => persistOpen(true), [persistOpen]);
|
|
6764
6792
|
const close = React7.useCallback(() => {
|
|
@@ -6766,6 +6794,7 @@ function ChatSidebarProvider({
|
|
|
6766
6794
|
setView("list");
|
|
6767
6795
|
setActiveCaseId(null);
|
|
6768
6796
|
setActiveRoomId(null);
|
|
6797
|
+
setSearchQuery("");
|
|
6769
6798
|
}, [persistOpen]);
|
|
6770
6799
|
const openChat = React7.useCallback(
|
|
6771
6800
|
(caseId, roomId) => {
|
|
@@ -6807,31 +6836,40 @@ function ChatSidebarProvider({
|
|
|
6807
6836
|
isLoading,
|
|
6808
6837
|
activeCaseId,
|
|
6809
6838
|
activeRoomId,
|
|
6839
|
+
searchQuery,
|
|
6840
|
+
setSearchQuery,
|
|
6841
|
+
totalUnreadCount,
|
|
6810
6842
|
toggle,
|
|
6811
6843
|
open,
|
|
6812
6844
|
close,
|
|
6813
6845
|
openChat,
|
|
6814
6846
|
selectRoom,
|
|
6815
6847
|
back,
|
|
6816
|
-
refetch: loadConversations
|
|
6848
|
+
refetch: loadConversations,
|
|
6849
|
+
clearRoomUnread
|
|
6817
6850
|
};
|
|
6818
6851
|
return /* @__PURE__ */ jsxRuntime.jsx(ChatSidebarContext.Provider, { value, children });
|
|
6819
6852
|
}
|
|
6820
6853
|
var PING_INTERVAL = 3e4;
|
|
6821
6854
|
var MIN_RECONNECT_DELAY = 1e3;
|
|
6822
6855
|
var MAX_RECONNECT_DELAY = 3e4;
|
|
6856
|
+
var MARK_READ_DEBOUNCE_MS = 1e3;
|
|
6823
6857
|
function useChatRoom(roomId, config, options = {}) {
|
|
6824
6858
|
const {
|
|
6825
6859
|
minReconnectDelay = MIN_RECONNECT_DELAY,
|
|
6826
6860
|
maxReconnectDelay = MAX_RECONNECT_DELAY,
|
|
6827
|
-
pingInterval = PING_INTERVAL
|
|
6861
|
+
pingInterval = PING_INTERVAL,
|
|
6862
|
+
onMarkedRead
|
|
6828
6863
|
} = options;
|
|
6829
6864
|
const [messages, setMessages] = React7.useState([]);
|
|
6830
6865
|
const [isConnected, setIsConnected] = React7.useState(false);
|
|
6831
6866
|
const [error, setError] = React7.useState(null);
|
|
6832
6867
|
const [typingUsers, setTypingUsers] = React7.useState([]);
|
|
6868
|
+
const [onlineUserIds, setOnlineUserIds] = React7.useState([]);
|
|
6869
|
+
const [readReceipts, setReadReceipts] = React7.useState({});
|
|
6833
6870
|
const [hasMoreHistory, setHasMoreHistory] = React7.useState(true);
|
|
6834
6871
|
const [isLoadingHistory, setIsLoadingHistory] = React7.useState(false);
|
|
6872
|
+
const [isInitialLoading, setIsInitialLoading] = React7.useState(false);
|
|
6835
6873
|
const wsRef = React7.useRef(null);
|
|
6836
6874
|
const mountedRef = React7.useRef(true);
|
|
6837
6875
|
const reconnectDelayRef = React7.useRef(minReconnectDelay);
|
|
@@ -6844,8 +6882,13 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6844
6882
|
() => Promise.resolve()
|
|
6845
6883
|
);
|
|
6846
6884
|
const configRef = React7.useRef(config);
|
|
6885
|
+
const onMarkedReadRef = React7.useRef(onMarkedRead);
|
|
6886
|
+
const lastSentWatermarkRef = React7.useRef(null);
|
|
6887
|
+
const pendingMarkRef = React7.useRef(null);
|
|
6888
|
+
const markReadTimerRef = React7.useRef(null);
|
|
6847
6889
|
roomIdRef.current = roomId;
|
|
6848
6890
|
configRef.current = config;
|
|
6891
|
+
onMarkedReadRef.current = onMarkedRead;
|
|
6849
6892
|
const clearTimers = React7.useCallback(() => {
|
|
6850
6893
|
if (reconnectTimerRef.current) {
|
|
6851
6894
|
clearTimeout(reconnectTimerRef.current);
|
|
@@ -6855,7 +6898,74 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6855
6898
|
clearInterval(pingTimerRef.current);
|
|
6856
6899
|
pingTimerRef.current = null;
|
|
6857
6900
|
}
|
|
6901
|
+
if (markReadTimerRef.current) {
|
|
6902
|
+
clearTimeout(markReadTimerRef.current);
|
|
6903
|
+
markReadTimerRef.current = null;
|
|
6904
|
+
}
|
|
6858
6905
|
}, []);
|
|
6906
|
+
const flushMarkRead = React7.useCallback(async () => {
|
|
6907
|
+
const pending = pendingMarkRef.current;
|
|
6908
|
+
const currentRoomId = roomIdRef.current;
|
|
6909
|
+
if (!pending || !currentRoomId) return;
|
|
6910
|
+
const { lastMessageId, lastReadAt } = pending;
|
|
6911
|
+
pendingMarkRef.current = null;
|
|
6912
|
+
if (lastSentWatermarkRef.current && lastSentWatermarkRef.current >= lastReadAt) {
|
|
6913
|
+
return;
|
|
6914
|
+
}
|
|
6915
|
+
const ws = wsRef.current;
|
|
6916
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
6917
|
+
ws.send(
|
|
6918
|
+
JSON.stringify({
|
|
6919
|
+
type: "mark_read",
|
|
6920
|
+
lastMessageId,
|
|
6921
|
+
lastReadAt
|
|
6922
|
+
})
|
|
6923
|
+
);
|
|
6924
|
+
lastSentWatermarkRef.current = lastReadAt;
|
|
6925
|
+
onMarkedReadRef.current?.(currentRoomId);
|
|
6926
|
+
return;
|
|
6927
|
+
}
|
|
6928
|
+
try {
|
|
6929
|
+
const jwt = await configRef.current.getToken();
|
|
6930
|
+
if (!jwt) return;
|
|
6931
|
+
const res = await fetch(
|
|
6932
|
+
`${configRef.current.baseUrl}/rooms/${currentRoomId}/read`,
|
|
6933
|
+
{
|
|
6934
|
+
method: "POST",
|
|
6935
|
+
headers: {
|
|
6936
|
+
Authorization: `Bearer ${jwt}`,
|
|
6937
|
+
"Content-Type": "application/json"
|
|
6938
|
+
},
|
|
6939
|
+
body: JSON.stringify({ lastMessageId, lastReadAt })
|
|
6940
|
+
}
|
|
6941
|
+
);
|
|
6942
|
+
if (res.ok) {
|
|
6943
|
+
lastSentWatermarkRef.current = lastReadAt;
|
|
6944
|
+
onMarkedReadRef.current?.(currentRoomId);
|
|
6945
|
+
}
|
|
6946
|
+
} catch {
|
|
6947
|
+
}
|
|
6948
|
+
}, []);
|
|
6949
|
+
const markRead = React7.useCallback(
|
|
6950
|
+
(lastMessageId, lastReadAt) => {
|
|
6951
|
+
if (lastSentWatermarkRef.current && lastSentWatermarkRef.current >= lastReadAt) {
|
|
6952
|
+
return;
|
|
6953
|
+
}
|
|
6954
|
+
const pending = pendingMarkRef.current;
|
|
6955
|
+
if (pending && pending.lastReadAt >= lastReadAt) {
|
|
6956
|
+
return;
|
|
6957
|
+
}
|
|
6958
|
+
pendingMarkRef.current = { lastMessageId, lastReadAt };
|
|
6959
|
+
if (markReadTimerRef.current) {
|
|
6960
|
+
clearTimeout(markReadTimerRef.current);
|
|
6961
|
+
}
|
|
6962
|
+
markReadTimerRef.current = setTimeout(() => {
|
|
6963
|
+
markReadTimerRef.current = null;
|
|
6964
|
+
void flushMarkRead();
|
|
6965
|
+
}, MARK_READ_DEBOUNCE_MS);
|
|
6966
|
+
},
|
|
6967
|
+
[flushMarkRead]
|
|
6968
|
+
);
|
|
6859
6969
|
const loadMoreHistory = React7.useCallback(async () => {
|
|
6860
6970
|
if (!roomIdRef.current || isLoadingHistory || !hasMoreHistory) return;
|
|
6861
6971
|
setIsLoadingHistory(true);
|
|
@@ -6888,6 +6998,7 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6888
6998
|
} catch {
|
|
6889
6999
|
} finally {
|
|
6890
7000
|
setIsLoadingHistory(false);
|
|
7001
|
+
setIsInitialLoading(false);
|
|
6891
7002
|
}
|
|
6892
7003
|
}, [isLoadingHistory, hasMoreHistory]);
|
|
6893
7004
|
const sendMessage = React7.useCallback(
|
|
@@ -6921,6 +7032,7 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6921
7032
|
} catch {
|
|
6922
7033
|
if (mountedRef.current) {
|
|
6923
7034
|
setError("Failed to get authentication token");
|
|
7035
|
+
setIsInitialLoading(false);
|
|
6924
7036
|
}
|
|
6925
7037
|
return;
|
|
6926
7038
|
}
|
|
@@ -6936,6 +7048,7 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6936
7048
|
setError(
|
|
6937
7049
|
err instanceof Error ? err.message : "WebSocket creation failed"
|
|
6938
7050
|
);
|
|
7051
|
+
setIsInitialLoading(false);
|
|
6939
7052
|
}
|
|
6940
7053
|
return;
|
|
6941
7054
|
}
|
|
@@ -6960,9 +7073,23 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6960
7073
|
try {
|
|
6961
7074
|
const msg = JSON.parse(event.data);
|
|
6962
7075
|
switch (msg.type) {
|
|
6963
|
-
case "server_hello":
|
|
7076
|
+
case "server_hello": {
|
|
7077
|
+
const readState = msg.readState;
|
|
7078
|
+
if (readState) {
|
|
7079
|
+
setReadReceipts((prev) => {
|
|
7080
|
+
const next = { ...prev };
|
|
7081
|
+
for (const entry of readState) {
|
|
7082
|
+
const existing = next[entry.userId];
|
|
7083
|
+
if (!existing || existing < entry.lastReadAt) {
|
|
7084
|
+
next[entry.userId] = entry.lastReadAt;
|
|
7085
|
+
}
|
|
7086
|
+
}
|
|
7087
|
+
return next;
|
|
7088
|
+
});
|
|
7089
|
+
}
|
|
6964
7090
|
void loadMoreHistoryRef.current();
|
|
6965
7091
|
break;
|
|
7092
|
+
}
|
|
6966
7093
|
case "new_message": {
|
|
6967
7094
|
const message = msg.message;
|
|
6968
7095
|
if (!messageIdsRef.current.has(message.id)) {
|
|
@@ -6987,6 +7114,25 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6987
7114
|
});
|
|
6988
7115
|
break;
|
|
6989
7116
|
}
|
|
7117
|
+
case "presence_update": {
|
|
7118
|
+
const ids = msg.onlineUserIds;
|
|
7119
|
+
if (Array.isArray(ids)) {
|
|
7120
|
+
setOnlineUserIds(ids);
|
|
7121
|
+
}
|
|
7122
|
+
break;
|
|
7123
|
+
}
|
|
7124
|
+
case "read_receipt": {
|
|
7125
|
+
const userId = msg.userId;
|
|
7126
|
+
const lastReadAt = msg.lastReadAt;
|
|
7127
|
+
if (userId && lastReadAt) {
|
|
7128
|
+
setReadReceipts((prev) => {
|
|
7129
|
+
const existing = prev[userId];
|
|
7130
|
+
if (existing && existing >= lastReadAt) return prev;
|
|
7131
|
+
return { ...prev, [userId]: lastReadAt };
|
|
7132
|
+
});
|
|
7133
|
+
}
|
|
7134
|
+
break;
|
|
7135
|
+
}
|
|
6990
7136
|
case "pong":
|
|
6991
7137
|
break;
|
|
6992
7138
|
case "error":
|
|
@@ -7023,16 +7169,27 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
7023
7169
|
setIsConnected(false);
|
|
7024
7170
|
setError(null);
|
|
7025
7171
|
setTypingUsers([]);
|
|
7172
|
+
setOnlineUserIds([]);
|
|
7173
|
+
setReadReceipts({});
|
|
7026
7174
|
setHasMoreHistory(true);
|
|
7175
|
+
setIsLoadingHistory(false);
|
|
7176
|
+
setIsInitialLoading(false);
|
|
7027
7177
|
cursorRef.current = null;
|
|
7028
7178
|
messageIdsRef.current = /* @__PURE__ */ new Set();
|
|
7179
|
+
lastSentWatermarkRef.current = null;
|
|
7180
|
+
pendingMarkRef.current = null;
|
|
7029
7181
|
return;
|
|
7030
7182
|
}
|
|
7031
7183
|
setMessages([]);
|
|
7032
7184
|
setTypingUsers([]);
|
|
7185
|
+
setOnlineUserIds([]);
|
|
7186
|
+
setReadReceipts({});
|
|
7033
7187
|
setHasMoreHistory(true);
|
|
7188
|
+
setIsInitialLoading(true);
|
|
7034
7189
|
cursorRef.current = null;
|
|
7035
7190
|
messageIdsRef.current = /* @__PURE__ */ new Set();
|
|
7191
|
+
lastSentWatermarkRef.current = null;
|
|
7192
|
+
pendingMarkRef.current = null;
|
|
7036
7193
|
reconnectDelayRef.current = minReconnectDelay;
|
|
7037
7194
|
void connect();
|
|
7038
7195
|
return () => {
|
|
@@ -7048,12 +7205,16 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
7048
7205
|
messages,
|
|
7049
7206
|
sendMessage,
|
|
7050
7207
|
sendTyping,
|
|
7208
|
+
markRead,
|
|
7051
7209
|
isConnected,
|
|
7052
7210
|
error,
|
|
7053
7211
|
typingUsers,
|
|
7212
|
+
onlineUserIds,
|
|
7213
|
+
readReceipts,
|
|
7054
7214
|
loadMoreHistory,
|
|
7055
7215
|
hasMoreHistory,
|
|
7056
|
-
isLoadingHistory
|
|
7216
|
+
isLoadingHistory,
|
|
7217
|
+
isInitialLoading
|
|
7057
7218
|
};
|
|
7058
7219
|
}
|
|
7059
7220
|
var roleColors = {
|
|
@@ -7068,21 +7229,27 @@ function ChatRoomView({
|
|
|
7068
7229
|
currentUserId,
|
|
7069
7230
|
config,
|
|
7070
7231
|
roomLabel,
|
|
7232
|
+
caseNumber,
|
|
7071
7233
|
labels = {},
|
|
7072
7234
|
roleLabel,
|
|
7235
|
+
onMarkedRead,
|
|
7073
7236
|
className
|
|
7074
7237
|
}) {
|
|
7075
7238
|
const {
|
|
7076
7239
|
messages,
|
|
7077
7240
|
sendMessage,
|
|
7078
7241
|
sendTyping,
|
|
7242
|
+
markRead,
|
|
7079
7243
|
isConnected,
|
|
7080
7244
|
error,
|
|
7081
7245
|
typingUsers,
|
|
7246
|
+
onlineUserIds,
|
|
7247
|
+
readReceipts,
|
|
7082
7248
|
loadMoreHistory,
|
|
7083
7249
|
hasMoreHistory,
|
|
7084
|
-
isLoadingHistory
|
|
7085
|
-
|
|
7250
|
+
isLoadingHistory,
|
|
7251
|
+
isInitialLoading
|
|
7252
|
+
} = useChatRoom(roomId, config, { onMarkedRead });
|
|
7086
7253
|
const [newMessage, setNewMessage] = React7.useState("");
|
|
7087
7254
|
const [pendingAttachments, setPendingAttachments] = React7.useState([]);
|
|
7088
7255
|
const scrollRef = React7.useRef(null);
|
|
@@ -7090,6 +7257,16 @@ function ChatRoomView({
|
|
|
7090
7257
|
const fileInputRef = React7.useRef(null);
|
|
7091
7258
|
const typingTimeoutRef = React7.useRef(null);
|
|
7092
7259
|
const prevMessageCountRef = React7.useRef(0);
|
|
7260
|
+
const counterpartOnline = React7.useMemo(
|
|
7261
|
+
() => onlineUserIds.some((id) => id !== currentUserId),
|
|
7262
|
+
[onlineUserIds, currentUserId]
|
|
7263
|
+
);
|
|
7264
|
+
React7.useEffect(() => {
|
|
7265
|
+
if (isInitialLoading || messages.length === 0) return;
|
|
7266
|
+
const latest = messages[messages.length - 1];
|
|
7267
|
+
if (!latest) return;
|
|
7268
|
+
markRead(latest.id, latest.createdAt);
|
|
7269
|
+
}, [messages, isInitialLoading, markRead]);
|
|
7093
7270
|
React7.useEffect(() => {
|
|
7094
7271
|
const el = scrollRef.current;
|
|
7095
7272
|
if (!el) return;
|
|
@@ -7202,27 +7379,50 @@ function ChatRoomView({
|
|
|
7202
7379
|
};
|
|
7203
7380
|
const getAttachmentUrl = (attachmentId) => `${config.baseUrl}/rooms/${roomId}/attachments/${attachmentId}`;
|
|
7204
7381
|
const getRoleLabel = (role) => roleLabel ? roleLabel(role) : role;
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7382
|
+
const isMessageSeen = (message) => {
|
|
7383
|
+
if (message.senderId !== currentUserId) return false;
|
|
7384
|
+
return Object.entries(readReceipts).some(
|
|
7385
|
+
([userId, lastReadAt]) => userId !== currentUserId && lastReadAt >= message.createdAt
|
|
7386
|
+
);
|
|
7387
|
+
};
|
|
7388
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex h-full min-h-0 flex-col", className), children: [
|
|
7389
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-shrink-0 flex items-center justify-between border-b px-4 py-2.5", children: [
|
|
7390
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
7391
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
7392
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium truncate", children: roomLabel ?? "Chat" }),
|
|
7393
|
+
caseNumber && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground truncate", children: caseNumber })
|
|
7394
|
+
] }),
|
|
7395
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-0.5 flex items-center gap-1.5", children: [
|
|
7396
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7397
|
+
lucideReact.Circle,
|
|
7398
|
+
{
|
|
7399
|
+
className: cn(
|
|
7400
|
+
"h-2 w-2 fill-current",
|
|
7401
|
+
counterpartOnline ? "text-emerald-500" : "text-muted-foreground/40"
|
|
7402
|
+
)
|
|
7403
|
+
}
|
|
7404
|
+
),
|
|
7405
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-muted-foreground", children: counterpartOnline ? labels.online ?? "Online" : labels.offline ?? "Offline" })
|
|
7406
|
+
] })
|
|
7210
7407
|
] }),
|
|
7211
7408
|
/* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
7212
|
-
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5", children: isConnected ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Wifi, { className: "h-3.5 w-3.5 text-
|
|
7409
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5", children: isConnected ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Wifi, { className: "h-3.5 w-3.5 text-emerald-500" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.WifiOff, { className: "h-3.5 w-3.5 text-destructive" }) }) }),
|
|
7213
7410
|
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: isConnected ? "Connected" : error ?? "Reconnecting..." })
|
|
7214
7411
|
] }) })
|
|
7215
7412
|
] }),
|
|
7216
7413
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
|
|
7217
|
-
/* @__PURE__ */ jsxRuntime.
|
|
7414
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7218
7415
|
"div",
|
|
7219
7416
|
{
|
|
7220
7417
|
ref: scrollRef,
|
|
7221
7418
|
onScroll: handleScroll,
|
|
7222
7419
|
className: "flex-1 overflow-y-auto p-3",
|
|
7223
|
-
children: [
|
|
7420
|
+
children: isInitialLoading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full flex-col items-center justify-center py-8", children: [
|
|
7421
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }),
|
|
7422
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: labels.loading ?? "Loading messages..." })
|
|
7423
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7224
7424
|
isLoadingHistory && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) }),
|
|
7225
|
-
messages.length === 0
|
|
7425
|
+
messages.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full flex-col items-center justify-center py-8 text-center", children: [
|
|
7226
7426
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7227
7427
|
lucideReact.MessageSquare,
|
|
7228
7428
|
{
|
|
@@ -7231,112 +7431,132 @@ function ChatRoomView({
|
|
|
7231
7431
|
}
|
|
7232
7432
|
),
|
|
7233
7433
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: labels.noMessages ?? "No messages yet" })
|
|
7234
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: Object.entries(groupedMessages).map(
|
|
7235
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
7236
|
-
/* @__PURE__ */ jsxRuntime.
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7434
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: Object.entries(groupedMessages).map(
|
|
7435
|
+
([date, dateMessages]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
7436
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "my-3 flex items-center gap-3", children: [
|
|
7437
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px flex-1 bg-border" }),
|
|
7438
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-2 text-xs text-muted-foreground", children: date }),
|
|
7439
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px flex-1 bg-border" })
|
|
7440
|
+
] }),
|
|
7441
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: dateMessages.map((message) => {
|
|
7442
|
+
if (message.senderRole === "system") {
|
|
7443
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7444
|
+
"div",
|
|
7445
|
+
{
|
|
7446
|
+
className: "flex justify-center py-1",
|
|
7447
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex max-w-[90%] items-center gap-1.5 rounded-full border bg-muted/60 px-3 py-1 text-[11px] text-muted-foreground", children: [
|
|
7448
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { className: "h-3 w-3 flex-shrink-0" }),
|
|
7449
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-center", children: message.content })
|
|
7450
|
+
] })
|
|
7451
|
+
},
|
|
7452
|
+
message.id
|
|
7453
|
+
);
|
|
7454
|
+
}
|
|
7455
|
+
const isCurrentUser = message.senderId === currentUserId;
|
|
7456
|
+
const seen = isMessageSeen(message);
|
|
7457
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7458
|
+
"div",
|
|
7459
|
+
{
|
|
7460
|
+
className: cn(
|
|
7461
|
+
"flex gap-2",
|
|
7462
|
+
isCurrentUser ? "flex-row-reverse" : "flex-row"
|
|
7259
7463
|
),
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7464
|
+
children: [
|
|
7465
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7466
|
+
"div",
|
|
7467
|
+
{
|
|
7468
|
+
className: cn(
|
|
7469
|
+
"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full text-xs font-medium",
|
|
7470
|
+
roleColors[message.senderRole] ?? roleColors.client
|
|
7471
|
+
),
|
|
7472
|
+
children: (message.senderName ?? message.senderId).split(" ").map((n) => n[0]).join("").slice(0, 2).toUpperCase()
|
|
7473
|
+
}
|
|
7474
|
+
),
|
|
7475
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7476
|
+
"div",
|
|
7477
|
+
{
|
|
7478
|
+
className: cn(
|
|
7479
|
+
"flex max-w-[80%] flex-col",
|
|
7480
|
+
isCurrentUser ? "items-end" : "items-start"
|
|
7481
|
+
),
|
|
7482
|
+
children: [
|
|
7483
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-0.5 flex items-center gap-1.5", children: [
|
|
7484
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-foreground", children: message.senderName ?? message.senderId.slice(0, 8) }),
|
|
7485
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7486
|
+
"span",
|
|
7487
|
+
{
|
|
7488
|
+
className: cn(
|
|
7489
|
+
"rounded px-1 py-0.5 text-[10px] font-medium",
|
|
7490
|
+
roleColors[message.senderRole] ?? ""
|
|
7491
|
+
),
|
|
7492
|
+
children: getRoleLabel(message.senderRole)
|
|
7493
|
+
}
|
|
7494
|
+
),
|
|
7495
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children: formatTime2(message.createdAt) })
|
|
7496
|
+
] }),
|
|
7497
|
+
message.content && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7498
|
+
"div",
|
|
7272
7499
|
{
|
|
7273
7500
|
className: cn(
|
|
7274
|
-
"rounded px-
|
|
7275
|
-
|
|
7501
|
+
"rounded-2xl px-3 py-2 text-sm shadow-sm",
|
|
7502
|
+
isCurrentUser ? "rounded-br-md bg-primary text-primary-foreground" : "rounded-bl-md bg-muted text-foreground"
|
|
7276
7503
|
),
|
|
7277
|
-
children:
|
|
7504
|
+
children: message.content
|
|
7278
7505
|
}
|
|
7279
7506
|
),
|
|
7280
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
]
|
|
7334
|
-
},
|
|
7335
|
-
message.id
|
|
7336
|
-
);
|
|
7337
|
-
}) })
|
|
7338
|
-
] }, date)) })
|
|
7339
|
-
]
|
|
7507
|
+
message.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 flex flex-col gap-1", children: message.attachments.map(
|
|
7508
|
+
(att) => att.mimeType.startsWith("image/") ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7509
|
+
"a",
|
|
7510
|
+
{
|
|
7511
|
+
href: getAttachmentUrl(att.id),
|
|
7512
|
+
target: "_blank",
|
|
7513
|
+
rel: "noopener noreferrer",
|
|
7514
|
+
className: "block max-w-[200px] overflow-hidden rounded-lg border",
|
|
7515
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7516
|
+
"img",
|
|
7517
|
+
{
|
|
7518
|
+
src: getAttachmentUrl(att.id),
|
|
7519
|
+
alt: att.fileName,
|
|
7520
|
+
className: "h-auto w-full object-cover",
|
|
7521
|
+
loading: "lazy"
|
|
7522
|
+
}
|
|
7523
|
+
)
|
|
7524
|
+
},
|
|
7525
|
+
att.id
|
|
7526
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7527
|
+
"a",
|
|
7528
|
+
{
|
|
7529
|
+
href: getAttachmentUrl(att.id),
|
|
7530
|
+
target: "_blank",
|
|
7531
|
+
rel: "noopener noreferrer",
|
|
7532
|
+
className: "flex items-center gap-2 rounded-lg border bg-muted/50 px-2 py-1.5 text-xs transition-colors hover:bg-muted",
|
|
7533
|
+
children: [
|
|
7534
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileIcon, { className: "h-3.5 w-3.5 flex-shrink-0 text-muted-foreground" }),
|
|
7535
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
7536
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate font-medium", children: att.fileName }),
|
|
7537
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] text-muted-foreground", children: formatFileSize(att.fileSize) })
|
|
7538
|
+
] }),
|
|
7539
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "h-3.5 w-3.5 flex-shrink-0 text-muted-foreground" })
|
|
7540
|
+
]
|
|
7541
|
+
},
|
|
7542
|
+
att.id
|
|
7543
|
+
)
|
|
7544
|
+
) }),
|
|
7545
|
+
isCurrentUser && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 flex items-center gap-0.5 text-[10px] text-muted-foreground", children: seen ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7546
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCheck, { className: "h-3 w-3 text-primary" }),
|
|
7547
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: labels.seen ?? "Seen" })
|
|
7548
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-3 w-3" }) })
|
|
7549
|
+
]
|
|
7550
|
+
}
|
|
7551
|
+
)
|
|
7552
|
+
]
|
|
7553
|
+
},
|
|
7554
|
+
message.id
|
|
7555
|
+
);
|
|
7556
|
+
}) })
|
|
7557
|
+
] }, date)
|
|
7558
|
+
) })
|
|
7559
|
+
] })
|
|
7340
7560
|
}
|
|
7341
7561
|
),
|
|
7342
7562
|
typingUsers.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 px-3 py-1", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground italic", children: [
|
|
@@ -7431,16 +7651,41 @@ function ChatRoomView({
|
|
|
7431
7651
|
] })
|
|
7432
7652
|
] });
|
|
7433
7653
|
}
|
|
7654
|
+
function Input({ className, type, ...props }) {
|
|
7655
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7656
|
+
"input",
|
|
7657
|
+
{
|
|
7658
|
+
type,
|
|
7659
|
+
"data-slot": "input",
|
|
7660
|
+
className: cn(
|
|
7661
|
+
"h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
|
|
7662
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
7663
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
|
7664
|
+
className
|
|
7665
|
+
),
|
|
7666
|
+
...props
|
|
7667
|
+
}
|
|
7668
|
+
);
|
|
7669
|
+
}
|
|
7434
7670
|
var roomTypeLabels = {
|
|
7435
7671
|
client_agent: "Client \u2194 Agent",
|
|
7436
7672
|
dealer_agent: "Dealer \u2194 Agent"
|
|
7437
7673
|
};
|
|
7674
|
+
var statusToneClasses = {
|
|
7675
|
+
success: "bg-emerald-500/10 text-emerald-700 dark:text-emerald-400 border-emerald-500/20",
|
|
7676
|
+
warning: "bg-amber-500/10 text-amber-700 dark:text-amber-400 border-amber-500/20",
|
|
7677
|
+
destructive: "bg-destructive/10 text-destructive border-destructive/20",
|
|
7678
|
+
info: "bg-primary/10 text-primary border-primary/20",
|
|
7679
|
+
neutral: "bg-muted text-muted-foreground border-border"
|
|
7680
|
+
};
|
|
7438
7681
|
function ChatSidebar({
|
|
7439
7682
|
currentUserId,
|
|
7440
7683
|
config,
|
|
7441
7684
|
labels = {},
|
|
7442
7685
|
roleLabel,
|
|
7443
7686
|
roomTypeLabel,
|
|
7687
|
+
statusLabel,
|
|
7688
|
+
statusTone,
|
|
7444
7689
|
mobileTopOffset,
|
|
7445
7690
|
className
|
|
7446
7691
|
}) {
|
|
@@ -7451,9 +7696,12 @@ function ChatSidebar({
|
|
|
7451
7696
|
isLoading,
|
|
7452
7697
|
activeCaseId,
|
|
7453
7698
|
activeRoomId,
|
|
7699
|
+
searchQuery,
|
|
7700
|
+
setSearchQuery,
|
|
7454
7701
|
close,
|
|
7455
7702
|
selectRoom,
|
|
7456
|
-
back
|
|
7703
|
+
back,
|
|
7704
|
+
clearRoomUnread
|
|
7457
7705
|
} = useChatSidebar();
|
|
7458
7706
|
React7.useEffect(() => {
|
|
7459
7707
|
if (!isOpen || typeof window === "undefined") return;
|
|
@@ -7469,12 +7717,24 @@ function ChatSidebar({
|
|
|
7469
7717
|
document.body.style.overflow = "";
|
|
7470
7718
|
};
|
|
7471
7719
|
}, [isOpen]);
|
|
7720
|
+
const filteredConversations = React7.useMemo(() => {
|
|
7721
|
+
const q = searchQuery.trim().toLowerCase();
|
|
7722
|
+
if (!q) return conversations;
|
|
7723
|
+
return conversations.filter(
|
|
7724
|
+
(c) => c.caseNumber.toLowerCase().includes(q) || (c.clientName?.toLowerCase().includes(q) ?? false)
|
|
7725
|
+
);
|
|
7726
|
+
}, [conversations, searchQuery]);
|
|
7472
7727
|
if (!isOpen) return null;
|
|
7473
7728
|
const getRoomTypeLabel = (type) => {
|
|
7474
7729
|
if (roomTypeLabel) return roomTypeLabel(type);
|
|
7475
7730
|
return roomTypeLabels[type] ?? type;
|
|
7476
7731
|
};
|
|
7732
|
+
const getStatusLabel = (status) => statusLabel ? statusLabel(status) : status;
|
|
7733
|
+
const getStatusTone = (status) => statusTone?.(status) ?? "neutral";
|
|
7477
7734
|
const activeConversation = activeCaseId ? conversations.find((c) => c.caseId === activeCaseId) : null;
|
|
7735
|
+
const activeRoom = activeConversation?.rooms.find(
|
|
7736
|
+
(r2) => r2.id === activeRoomId
|
|
7737
|
+
);
|
|
7478
7738
|
const cssVars = {
|
|
7479
7739
|
"--sidebar-top": mobileTopOffset ?? "0px"
|
|
7480
7740
|
};
|
|
@@ -7505,40 +7765,88 @@ function ChatSidebar({
|
|
|
7505
7765
|
}
|
|
7506
7766
|
)
|
|
7507
7767
|
] }),
|
|
7768
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b px-3 py-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
7769
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }),
|
|
7770
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7771
|
+
Input,
|
|
7772
|
+
{
|
|
7773
|
+
value: searchQuery,
|
|
7774
|
+
onChange: (e) => setSearchQuery(e.target.value),
|
|
7775
|
+
placeholder: labels.searchPlaceholder ?? "Search by case or client...",
|
|
7776
|
+
className: "h-8 pl-8 text-sm"
|
|
7777
|
+
}
|
|
7778
|
+
)
|
|
7779
|
+
] }) }),
|
|
7508
7780
|
/* @__PURE__ */ jsxRuntime.jsx(ScrollArea, { className: "flex-1", children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-5 w-5 animate-spin text-muted-foreground" }) }) : conversations.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center px-4", children: [
|
|
7509
7781
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "mb-3 h-8 w-8 text-muted-foreground/50" }),
|
|
7510
7782
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: labels.noConversations ?? "No conversations yet" })
|
|
7511
|
-
] }) : /* @__PURE__ */ jsxRuntime.
|
|
7512
|
-
/* @__PURE__ */ jsxRuntime.
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
"
|
|
7783
|
+
] }) : filteredConversations.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center px-4", children: [
|
|
7784
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "mb-3 h-8 w-8 text-muted-foreground/50" }),
|
|
7785
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: labels.noResults ?? "No matching conversations" })
|
|
7786
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 p-3", children: filteredConversations.map((conv) => {
|
|
7787
|
+
const caseUnread = conv.rooms.reduce(
|
|
7788
|
+
(sum, r2) => sum + (r2.unreadCount ?? 0),
|
|
7789
|
+
0
|
|
7790
|
+
);
|
|
7791
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7792
|
+
"div",
|
|
7521
7793
|
{
|
|
7522
|
-
|
|
7523
|
-
onClick: () => selectRoom(room.id, conv.caseId),
|
|
7524
|
-
className: cn(
|
|
7525
|
-
"flex w-full items-center justify-between rounded-md px-2 py-2 text-left transition-colors hover:bg-accent",
|
|
7526
|
-
activeRoomId === room.id && "bg-accent"
|
|
7527
|
-
),
|
|
7794
|
+
className: "rounded-lg border bg-card shadow-sm",
|
|
7528
7795
|
children: [
|
|
7529
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-
|
|
7530
|
-
/* @__PURE__ */ jsxRuntime.
|
|
7531
|
-
|
|
7796
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2 border-b px-3 py-2.5", children: [
|
|
7797
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
7798
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
7799
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-foreground truncate", children: conv.caseNumber }),
|
|
7800
|
+
caseUnread > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7801
|
+
"span",
|
|
7802
|
+
{
|
|
7803
|
+
className: "h-2 w-2 flex-shrink-0 rounded-full bg-primary",
|
|
7804
|
+
"aria-label": `${caseUnread} unread`
|
|
7805
|
+
}
|
|
7806
|
+
)
|
|
7807
|
+
] }),
|
|
7808
|
+
conv.clientName && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground truncate", children: conv.clientName })
|
|
7809
|
+
] }),
|
|
7810
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7811
|
+
"span",
|
|
7812
|
+
{
|
|
7813
|
+
className: cn(
|
|
7814
|
+
"flex-shrink-0 rounded-full border px-2 py-0.5 text-[10px] font-medium",
|
|
7815
|
+
statusToneClasses[getStatusTone(conv.caseStatus)]
|
|
7816
|
+
),
|
|
7817
|
+
children: getStatusLabel(conv.caseStatus)
|
|
7818
|
+
}
|
|
7819
|
+
)
|
|
7532
7820
|
] }),
|
|
7533
|
-
/* @__PURE__ */ jsxRuntime.
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7821
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: conv.rooms.map((room) => {
|
|
7822
|
+
const RoomIcon = room.type === "dealer_agent" ? lucideReact.Store : lucideReact.User;
|
|
7823
|
+
const unread = room.unreadCount ?? 0;
|
|
7824
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7825
|
+
"button",
|
|
7826
|
+
{
|
|
7827
|
+
type: "button",
|
|
7828
|
+
onClick: () => selectRoom(room.id, conv.caseId),
|
|
7829
|
+
className: cn(
|
|
7830
|
+
"flex w-full items-center gap-2.5 px-3 py-2.5 text-left transition-colors hover:bg-accent/60",
|
|
7831
|
+
activeRoomId === room.id && "bg-accent"
|
|
7832
|
+
),
|
|
7833
|
+
children: [
|
|
7834
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-muted", children: /* @__PURE__ */ jsxRuntime.jsx(RoomIcon, { className: "h-3.5 w-3.5 text-muted-foreground" }) }),
|
|
7835
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
7836
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium truncate", children: getRoomTypeLabel(room.type) }),
|
|
7837
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] text-muted-foreground", children: room.lastMessageAt ? formatRelativeTime(room.lastMessageAt) : labels.noMessagesShort ?? "No messages" })
|
|
7838
|
+
] }),
|
|
7839
|
+
unread > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-5 min-w-5 flex-shrink-0 items-center justify-center rounded-full bg-primary px-1.5 text-[10px] font-semibold text-primary-foreground", children: unread > 99 ? "99+" : unread })
|
|
7840
|
+
]
|
|
7841
|
+
},
|
|
7842
|
+
room.id
|
|
7843
|
+
);
|
|
7844
|
+
}) })
|
|
7537
7845
|
]
|
|
7538
7846
|
},
|
|
7539
|
-
|
|
7540
|
-
)
|
|
7541
|
-
|
|
7847
|
+
conv.caseId
|
|
7848
|
+
);
|
|
7849
|
+
}) }) })
|
|
7542
7850
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7543
7851
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border-b px-3 py-2", children: [
|
|
7544
7852
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -7552,8 +7860,11 @@ function ChatSidebar({
|
|
|
7552
7860
|
}
|
|
7553
7861
|
),
|
|
7554
7862
|
activeConversation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
7555
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium truncate", children: activeConversation.caseNumber }),
|
|
7556
|
-
|
|
7863
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium truncate", children: activeRoom ? getRoomTypeLabel(activeRoom.type) : activeConversation.caseNumber }),
|
|
7864
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground truncate", children: [
|
|
7865
|
+
activeConversation.caseNumber,
|
|
7866
|
+
activeConversation.clientName ? ` \xB7 ${activeConversation.clientName}` : ""
|
|
7867
|
+
] })
|
|
7557
7868
|
] }),
|
|
7558
7869
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7559
7870
|
Button,
|
|
@@ -7572,8 +7883,11 @@ function ChatSidebar({
|
|
|
7572
7883
|
roomId: activeRoomId,
|
|
7573
7884
|
currentUserId,
|
|
7574
7885
|
config,
|
|
7886
|
+
roomLabel: activeRoom ? getRoomTypeLabel(activeRoom.type) : void 0,
|
|
7887
|
+
caseNumber: activeConversation?.caseNumber,
|
|
7575
7888
|
labels,
|
|
7576
7889
|
roleLabel,
|
|
7890
|
+
onMarkedRead: clearRoomUnread,
|
|
7577
7891
|
className: "flex-1"
|
|
7578
7892
|
}
|
|
7579
7893
|
)
|
|
@@ -10293,29 +10607,14 @@ function USAddressInput({
|
|
|
10293
10607
|
] })
|
|
10294
10608
|
] });
|
|
10295
10609
|
}
|
|
10296
|
-
|
|
10297
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10298
|
-
"input",
|
|
10299
|
-
{
|
|
10300
|
-
type,
|
|
10301
|
-
"data-slot": "input",
|
|
10302
|
-
className: cn(
|
|
10303
|
-
"h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
|
|
10304
|
-
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
10305
|
-
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
|
10306
|
-
className
|
|
10307
|
-
),
|
|
10308
|
-
...props
|
|
10309
|
-
}
|
|
10310
|
-
);
|
|
10311
|
-
}
|
|
10610
|
+
var phoneInputGroupClass = "flex h-9 w-full gap-0 overflow-hidden rounded-md border border-input bg-transparent shadow-xs transition-[color,box-shadow] [--PhoneInputCountrySelect-marginRight:0] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 dark:bg-input/30 [&_.PhoneInputCountry]:mr-0";
|
|
10312
10611
|
var PhoneInput = React7__namespace.forwardRef(
|
|
10313
10612
|
({ className, onChange, value, defaultCountry = "MX", ...props }, ref) => {
|
|
10314
10613
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10315
10614
|
RPNInput__namespace.default,
|
|
10316
10615
|
{
|
|
10317
10616
|
ref,
|
|
10318
|
-
className: cn(
|
|
10617
|
+
className: cn(phoneInputGroupClass, className),
|
|
10319
10618
|
flagComponent: FlagComponent,
|
|
10320
10619
|
countrySelectComponent: CountrySelect,
|
|
10321
10620
|
inputComponent: InputComponent,
|
|
@@ -10332,7 +10631,10 @@ PhoneInput.displayName = "PhoneInput";
|
|
|
10332
10631
|
var InputComponent = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
10333
10632
|
Input,
|
|
10334
10633
|
{
|
|
10335
|
-
className: cn(
|
|
10634
|
+
className: cn(
|
|
10635
|
+
"h-9 min-h-0 rounded-none border-0 bg-transparent shadow-none focus-visible:border-transparent focus-visible:ring-0 dark:bg-transparent",
|
|
10636
|
+
className
|
|
10637
|
+
),
|
|
10336
10638
|
...props,
|
|
10337
10639
|
ref
|
|
10338
10640
|
}
|
|
@@ -10361,8 +10663,8 @@ var CountrySelect = ({
|
|
|
10361
10663
|
Button,
|
|
10362
10664
|
{
|
|
10363
10665
|
type: "button",
|
|
10364
|
-
variant: "
|
|
10365
|
-
className: "
|
|
10666
|
+
variant: "ghost",
|
|
10667
|
+
className: "h-9 shrink-0 gap-1 rounded-none border-0 border-r border-input px-2.5 shadow-none hover:bg-muted/50 focus:z-10",
|
|
10366
10668
|
disabled,
|
|
10367
10669
|
children: [
|
|
10368
10670
|
/* @__PURE__ */ jsxRuntime.jsx(
|