@algenium/blocks 1.16.1 → 1.18.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 +473 -160
- 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 +474 -161
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import * as React7 from 'react';
|
|
3
3
|
import { createContext, useContext, useState, useCallback, useEffect, useRef, useMemo, useId } from 'react';
|
|
4
|
-
import { ChevronsUpDown, CheckIcon, SearchIcon, Loader2, AlertTriangle, Inbox, CircleIcon, ChevronRightIcon, Monitor, Sun, Moon, Building2, Check, Plus, Languages, FlaskConical, X, Upload, Move, ZoomOut, ZoomIn, RotateCcw, RotateCw, Grid3X3, RefreshCw, XIcon, User, Pencil, Bell, CheckCheck, XCircle, CheckCircle, Info, Trash2, Clock, MapPin, Link, CalendarDays, ExternalLink, ChevronLeft, ChevronRight, HelpCircle,
|
|
4
|
+
import { ChevronsUpDown, CheckIcon, SearchIcon, Loader2, AlertTriangle, Inbox, CircleIcon, ChevronRightIcon, Monitor, Sun, Moon, Building2, Check, Plus, Languages, FlaskConical, X, Upload, Move, ZoomOut, ZoomIn, RotateCcw, RotateCw, Grid3X3, RefreshCw, XIcon, User, Pencil, Bell, CheckCheck, XCircle, CheckCircle, Info, Trash2, Clock, MapPin, Link, CalendarDays, ExternalLink, ChevronLeft, ChevronRight, HelpCircle, Circle, Wifi, WifiOff, MessageSquare, FileIcon, Download, Paperclip, Send, Search, Store, ArrowLeft, Flag, Hash, Lightbulb, Trophy, Plane, Apple, Leaf, Hand, Smile, Play, AlertCircle, VolumeX, Pause, Volume1, Volume2, Settings, PictureInPicture2, Minimize, Maximize, CreditCard, CheckCircle2, BadgeCheck } from 'lucide-react';
|
|
5
5
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
6
6
|
import { useTheme } from 'next-themes';
|
|
7
7
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
@@ -6704,7 +6704,9 @@ function ChatSidebarProvider({
|
|
|
6704
6704
|
const [isLoading, setIsLoading] = useState(false);
|
|
6705
6705
|
const [activeCaseId, setActiveCaseId] = useState(null);
|
|
6706
6706
|
const [activeRoomId, setActiveRoomId] = useState(null);
|
|
6707
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
6707
6708
|
const fetchRef = useRef(fetchConversations);
|
|
6709
|
+
const prevOpenRef = useRef(isOpen);
|
|
6708
6710
|
fetchRef.current = fetchConversations;
|
|
6709
6711
|
const persistOpen = useCallback((open2) => {
|
|
6710
6712
|
setIsOpen(open2);
|
|
@@ -6726,6 +6728,32 @@ function ChatSidebarProvider({
|
|
|
6726
6728
|
useEffect(() => {
|
|
6727
6729
|
void loadConversations();
|
|
6728
6730
|
}, [loadConversations]);
|
|
6731
|
+
useEffect(() => {
|
|
6732
|
+
if (isOpen && !prevOpenRef.current) {
|
|
6733
|
+
void loadConversations();
|
|
6734
|
+
}
|
|
6735
|
+
prevOpenRef.current = isOpen;
|
|
6736
|
+
}, [isOpen, loadConversations]);
|
|
6737
|
+
const clearRoomUnread = useCallback((roomId) => {
|
|
6738
|
+
setConversations(
|
|
6739
|
+
(prev) => prev.map((conv) => ({
|
|
6740
|
+
...conv,
|
|
6741
|
+
rooms: conv.rooms.map(
|
|
6742
|
+
(room) => room.id === roomId ? { ...room, unreadCount: 0 } : room
|
|
6743
|
+
)
|
|
6744
|
+
}))
|
|
6745
|
+
);
|
|
6746
|
+
}, []);
|
|
6747
|
+
const totalUnreadCount = useMemo(
|
|
6748
|
+
() => conversations.reduce(
|
|
6749
|
+
(sum, conv) => sum + conv.rooms.reduce(
|
|
6750
|
+
(roomSum, room) => roomSum + (room.unreadCount ?? 0),
|
|
6751
|
+
0
|
|
6752
|
+
),
|
|
6753
|
+
0
|
|
6754
|
+
),
|
|
6755
|
+
[conversations]
|
|
6756
|
+
);
|
|
6729
6757
|
const toggle = useCallback(() => persistOpen(!isOpen), [isOpen, persistOpen]);
|
|
6730
6758
|
const open = useCallback(() => persistOpen(true), [persistOpen]);
|
|
6731
6759
|
const close = useCallback(() => {
|
|
@@ -6733,6 +6761,7 @@ function ChatSidebarProvider({
|
|
|
6733
6761
|
setView("list");
|
|
6734
6762
|
setActiveCaseId(null);
|
|
6735
6763
|
setActiveRoomId(null);
|
|
6764
|
+
setSearchQuery("");
|
|
6736
6765
|
}, [persistOpen]);
|
|
6737
6766
|
const openChat = useCallback(
|
|
6738
6767
|
(caseId, roomId) => {
|
|
@@ -6774,31 +6803,40 @@ function ChatSidebarProvider({
|
|
|
6774
6803
|
isLoading,
|
|
6775
6804
|
activeCaseId,
|
|
6776
6805
|
activeRoomId,
|
|
6806
|
+
searchQuery,
|
|
6807
|
+
setSearchQuery,
|
|
6808
|
+
totalUnreadCount,
|
|
6777
6809
|
toggle,
|
|
6778
6810
|
open,
|
|
6779
6811
|
close,
|
|
6780
6812
|
openChat,
|
|
6781
6813
|
selectRoom,
|
|
6782
6814
|
back,
|
|
6783
|
-
refetch: loadConversations
|
|
6815
|
+
refetch: loadConversations,
|
|
6816
|
+
clearRoomUnread
|
|
6784
6817
|
};
|
|
6785
6818
|
return /* @__PURE__ */ jsx(ChatSidebarContext.Provider, { value, children });
|
|
6786
6819
|
}
|
|
6787
6820
|
var PING_INTERVAL = 3e4;
|
|
6788
6821
|
var MIN_RECONNECT_DELAY = 1e3;
|
|
6789
6822
|
var MAX_RECONNECT_DELAY = 3e4;
|
|
6823
|
+
var MARK_READ_DEBOUNCE_MS = 1e3;
|
|
6790
6824
|
function useChatRoom(roomId, config, options = {}) {
|
|
6791
6825
|
const {
|
|
6792
6826
|
minReconnectDelay = MIN_RECONNECT_DELAY,
|
|
6793
6827
|
maxReconnectDelay = MAX_RECONNECT_DELAY,
|
|
6794
|
-
pingInterval = PING_INTERVAL
|
|
6828
|
+
pingInterval = PING_INTERVAL,
|
|
6829
|
+
onMarkedRead
|
|
6795
6830
|
} = options;
|
|
6796
6831
|
const [messages, setMessages] = useState([]);
|
|
6797
6832
|
const [isConnected, setIsConnected] = useState(false);
|
|
6798
6833
|
const [error, setError] = useState(null);
|
|
6799
6834
|
const [typingUsers, setTypingUsers] = useState([]);
|
|
6835
|
+
const [onlineUserIds, setOnlineUserIds] = useState([]);
|
|
6836
|
+
const [readReceipts, setReadReceipts] = useState({});
|
|
6800
6837
|
const [hasMoreHistory, setHasMoreHistory] = useState(true);
|
|
6801
6838
|
const [isLoadingHistory, setIsLoadingHistory] = useState(false);
|
|
6839
|
+
const [isInitialLoading, setIsInitialLoading] = useState(false);
|
|
6802
6840
|
const wsRef = useRef(null);
|
|
6803
6841
|
const mountedRef = useRef(true);
|
|
6804
6842
|
const reconnectDelayRef = useRef(minReconnectDelay);
|
|
@@ -6811,8 +6849,13 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6811
6849
|
() => Promise.resolve()
|
|
6812
6850
|
);
|
|
6813
6851
|
const configRef = useRef(config);
|
|
6852
|
+
const onMarkedReadRef = useRef(onMarkedRead);
|
|
6853
|
+
const lastSentWatermarkRef = useRef(null);
|
|
6854
|
+
const pendingMarkRef = useRef(null);
|
|
6855
|
+
const markReadTimerRef = useRef(null);
|
|
6814
6856
|
roomIdRef.current = roomId;
|
|
6815
6857
|
configRef.current = config;
|
|
6858
|
+
onMarkedReadRef.current = onMarkedRead;
|
|
6816
6859
|
const clearTimers = useCallback(() => {
|
|
6817
6860
|
if (reconnectTimerRef.current) {
|
|
6818
6861
|
clearTimeout(reconnectTimerRef.current);
|
|
@@ -6822,7 +6865,74 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6822
6865
|
clearInterval(pingTimerRef.current);
|
|
6823
6866
|
pingTimerRef.current = null;
|
|
6824
6867
|
}
|
|
6868
|
+
if (markReadTimerRef.current) {
|
|
6869
|
+
clearTimeout(markReadTimerRef.current);
|
|
6870
|
+
markReadTimerRef.current = null;
|
|
6871
|
+
}
|
|
6872
|
+
}, []);
|
|
6873
|
+
const flushMarkRead = useCallback(async () => {
|
|
6874
|
+
const pending = pendingMarkRef.current;
|
|
6875
|
+
const currentRoomId = roomIdRef.current;
|
|
6876
|
+
if (!pending || !currentRoomId) return;
|
|
6877
|
+
const { lastMessageId, lastReadAt } = pending;
|
|
6878
|
+
pendingMarkRef.current = null;
|
|
6879
|
+
if (lastSentWatermarkRef.current && lastSentWatermarkRef.current >= lastReadAt) {
|
|
6880
|
+
return;
|
|
6881
|
+
}
|
|
6882
|
+
const ws = wsRef.current;
|
|
6883
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
6884
|
+
ws.send(
|
|
6885
|
+
JSON.stringify({
|
|
6886
|
+
type: "mark_read",
|
|
6887
|
+
lastMessageId,
|
|
6888
|
+
lastReadAt
|
|
6889
|
+
})
|
|
6890
|
+
);
|
|
6891
|
+
lastSentWatermarkRef.current = lastReadAt;
|
|
6892
|
+
onMarkedReadRef.current?.(currentRoomId);
|
|
6893
|
+
return;
|
|
6894
|
+
}
|
|
6895
|
+
try {
|
|
6896
|
+
const jwt = await configRef.current.getToken();
|
|
6897
|
+
if (!jwt) return;
|
|
6898
|
+
const res = await fetch(
|
|
6899
|
+
`${configRef.current.baseUrl}/rooms/${currentRoomId}/read`,
|
|
6900
|
+
{
|
|
6901
|
+
method: "POST",
|
|
6902
|
+
headers: {
|
|
6903
|
+
Authorization: `Bearer ${jwt}`,
|
|
6904
|
+
"Content-Type": "application/json"
|
|
6905
|
+
},
|
|
6906
|
+
body: JSON.stringify({ lastMessageId, lastReadAt })
|
|
6907
|
+
}
|
|
6908
|
+
);
|
|
6909
|
+
if (res.ok) {
|
|
6910
|
+
lastSentWatermarkRef.current = lastReadAt;
|
|
6911
|
+
onMarkedReadRef.current?.(currentRoomId);
|
|
6912
|
+
}
|
|
6913
|
+
} catch {
|
|
6914
|
+
}
|
|
6825
6915
|
}, []);
|
|
6916
|
+
const markRead = useCallback(
|
|
6917
|
+
(lastMessageId, lastReadAt) => {
|
|
6918
|
+
if (lastSentWatermarkRef.current && lastSentWatermarkRef.current >= lastReadAt) {
|
|
6919
|
+
return;
|
|
6920
|
+
}
|
|
6921
|
+
const pending = pendingMarkRef.current;
|
|
6922
|
+
if (pending && pending.lastReadAt >= lastReadAt) {
|
|
6923
|
+
return;
|
|
6924
|
+
}
|
|
6925
|
+
pendingMarkRef.current = { lastMessageId, lastReadAt };
|
|
6926
|
+
if (markReadTimerRef.current) {
|
|
6927
|
+
clearTimeout(markReadTimerRef.current);
|
|
6928
|
+
}
|
|
6929
|
+
markReadTimerRef.current = setTimeout(() => {
|
|
6930
|
+
markReadTimerRef.current = null;
|
|
6931
|
+
void flushMarkRead();
|
|
6932
|
+
}, MARK_READ_DEBOUNCE_MS);
|
|
6933
|
+
},
|
|
6934
|
+
[flushMarkRead]
|
|
6935
|
+
);
|
|
6826
6936
|
const loadMoreHistory = useCallback(async () => {
|
|
6827
6937
|
if (!roomIdRef.current || isLoadingHistory || !hasMoreHistory) return;
|
|
6828
6938
|
setIsLoadingHistory(true);
|
|
@@ -6855,6 +6965,7 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6855
6965
|
} catch {
|
|
6856
6966
|
} finally {
|
|
6857
6967
|
setIsLoadingHistory(false);
|
|
6968
|
+
setIsInitialLoading(false);
|
|
6858
6969
|
}
|
|
6859
6970
|
}, [isLoadingHistory, hasMoreHistory]);
|
|
6860
6971
|
const sendMessage = useCallback(
|
|
@@ -6888,6 +6999,7 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6888
6999
|
} catch {
|
|
6889
7000
|
if (mountedRef.current) {
|
|
6890
7001
|
setError("Failed to get authentication token");
|
|
7002
|
+
setIsInitialLoading(false);
|
|
6891
7003
|
}
|
|
6892
7004
|
return;
|
|
6893
7005
|
}
|
|
@@ -6903,6 +7015,7 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6903
7015
|
setError(
|
|
6904
7016
|
err instanceof Error ? err.message : "WebSocket creation failed"
|
|
6905
7017
|
);
|
|
7018
|
+
setIsInitialLoading(false);
|
|
6906
7019
|
}
|
|
6907
7020
|
return;
|
|
6908
7021
|
}
|
|
@@ -6927,9 +7040,23 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6927
7040
|
try {
|
|
6928
7041
|
const msg = JSON.parse(event.data);
|
|
6929
7042
|
switch (msg.type) {
|
|
6930
|
-
case "server_hello":
|
|
7043
|
+
case "server_hello": {
|
|
7044
|
+
const readState = msg.readState;
|
|
7045
|
+
if (readState) {
|
|
7046
|
+
setReadReceipts((prev) => {
|
|
7047
|
+
const next = { ...prev };
|
|
7048
|
+
for (const entry of readState) {
|
|
7049
|
+
const existing = next[entry.userId];
|
|
7050
|
+
if (!existing || existing < entry.lastReadAt) {
|
|
7051
|
+
next[entry.userId] = entry.lastReadAt;
|
|
7052
|
+
}
|
|
7053
|
+
}
|
|
7054
|
+
return next;
|
|
7055
|
+
});
|
|
7056
|
+
}
|
|
6931
7057
|
void loadMoreHistoryRef.current();
|
|
6932
7058
|
break;
|
|
7059
|
+
}
|
|
6933
7060
|
case "new_message": {
|
|
6934
7061
|
const message = msg.message;
|
|
6935
7062
|
if (!messageIdsRef.current.has(message.id)) {
|
|
@@ -6954,6 +7081,25 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6954
7081
|
});
|
|
6955
7082
|
break;
|
|
6956
7083
|
}
|
|
7084
|
+
case "presence_update": {
|
|
7085
|
+
const ids = msg.onlineUserIds;
|
|
7086
|
+
if (Array.isArray(ids)) {
|
|
7087
|
+
setOnlineUserIds(ids);
|
|
7088
|
+
}
|
|
7089
|
+
break;
|
|
7090
|
+
}
|
|
7091
|
+
case "read_receipt": {
|
|
7092
|
+
const userId = msg.userId;
|
|
7093
|
+
const lastReadAt = msg.lastReadAt;
|
|
7094
|
+
if (userId && lastReadAt) {
|
|
7095
|
+
setReadReceipts((prev) => {
|
|
7096
|
+
const existing = prev[userId];
|
|
7097
|
+
if (existing && existing >= lastReadAt) return prev;
|
|
7098
|
+
return { ...prev, [userId]: lastReadAt };
|
|
7099
|
+
});
|
|
7100
|
+
}
|
|
7101
|
+
break;
|
|
7102
|
+
}
|
|
6957
7103
|
case "pong":
|
|
6958
7104
|
break;
|
|
6959
7105
|
case "error":
|
|
@@ -6990,16 +7136,27 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
6990
7136
|
setIsConnected(false);
|
|
6991
7137
|
setError(null);
|
|
6992
7138
|
setTypingUsers([]);
|
|
7139
|
+
setOnlineUserIds([]);
|
|
7140
|
+
setReadReceipts({});
|
|
6993
7141
|
setHasMoreHistory(true);
|
|
7142
|
+
setIsLoadingHistory(false);
|
|
7143
|
+
setIsInitialLoading(false);
|
|
6994
7144
|
cursorRef.current = null;
|
|
6995
7145
|
messageIdsRef.current = /* @__PURE__ */ new Set();
|
|
7146
|
+
lastSentWatermarkRef.current = null;
|
|
7147
|
+
pendingMarkRef.current = null;
|
|
6996
7148
|
return;
|
|
6997
7149
|
}
|
|
6998
7150
|
setMessages([]);
|
|
6999
7151
|
setTypingUsers([]);
|
|
7152
|
+
setOnlineUserIds([]);
|
|
7153
|
+
setReadReceipts({});
|
|
7000
7154
|
setHasMoreHistory(true);
|
|
7155
|
+
setIsInitialLoading(true);
|
|
7001
7156
|
cursorRef.current = null;
|
|
7002
7157
|
messageIdsRef.current = /* @__PURE__ */ new Set();
|
|
7158
|
+
lastSentWatermarkRef.current = null;
|
|
7159
|
+
pendingMarkRef.current = null;
|
|
7003
7160
|
reconnectDelayRef.current = minReconnectDelay;
|
|
7004
7161
|
void connect();
|
|
7005
7162
|
return () => {
|
|
@@ -7015,12 +7172,16 @@ function useChatRoom(roomId, config, options = {}) {
|
|
|
7015
7172
|
messages,
|
|
7016
7173
|
sendMessage,
|
|
7017
7174
|
sendTyping,
|
|
7175
|
+
markRead,
|
|
7018
7176
|
isConnected,
|
|
7019
7177
|
error,
|
|
7020
7178
|
typingUsers,
|
|
7179
|
+
onlineUserIds,
|
|
7180
|
+
readReceipts,
|
|
7021
7181
|
loadMoreHistory,
|
|
7022
7182
|
hasMoreHistory,
|
|
7023
|
-
isLoadingHistory
|
|
7183
|
+
isLoadingHistory,
|
|
7184
|
+
isInitialLoading
|
|
7024
7185
|
};
|
|
7025
7186
|
}
|
|
7026
7187
|
var roleColors = {
|
|
@@ -7035,21 +7196,27 @@ function ChatRoomView({
|
|
|
7035
7196
|
currentUserId,
|
|
7036
7197
|
config,
|
|
7037
7198
|
roomLabel,
|
|
7199
|
+
caseNumber,
|
|
7038
7200
|
labels = {},
|
|
7039
7201
|
roleLabel,
|
|
7202
|
+
onMarkedRead,
|
|
7040
7203
|
className
|
|
7041
7204
|
}) {
|
|
7042
7205
|
const {
|
|
7043
7206
|
messages,
|
|
7044
7207
|
sendMessage,
|
|
7045
7208
|
sendTyping,
|
|
7209
|
+
markRead,
|
|
7046
7210
|
isConnected,
|
|
7047
7211
|
error,
|
|
7048
7212
|
typingUsers,
|
|
7213
|
+
onlineUserIds,
|
|
7214
|
+
readReceipts,
|
|
7049
7215
|
loadMoreHistory,
|
|
7050
7216
|
hasMoreHistory,
|
|
7051
|
-
isLoadingHistory
|
|
7052
|
-
|
|
7217
|
+
isLoadingHistory,
|
|
7218
|
+
isInitialLoading
|
|
7219
|
+
} = useChatRoom(roomId, config, { onMarkedRead });
|
|
7053
7220
|
const [newMessage, setNewMessage] = useState("");
|
|
7054
7221
|
const [pendingAttachments, setPendingAttachments] = useState([]);
|
|
7055
7222
|
const scrollRef = useRef(null);
|
|
@@ -7057,6 +7224,16 @@ function ChatRoomView({
|
|
|
7057
7224
|
const fileInputRef = useRef(null);
|
|
7058
7225
|
const typingTimeoutRef = useRef(null);
|
|
7059
7226
|
const prevMessageCountRef = useRef(0);
|
|
7227
|
+
const counterpartOnline = useMemo(
|
|
7228
|
+
() => onlineUserIds.some((id) => id !== currentUserId),
|
|
7229
|
+
[onlineUserIds, currentUserId]
|
|
7230
|
+
);
|
|
7231
|
+
useEffect(() => {
|
|
7232
|
+
if (isInitialLoading || messages.length === 0) return;
|
|
7233
|
+
const latest = messages[messages.length - 1];
|
|
7234
|
+
if (!latest) return;
|
|
7235
|
+
markRead(latest.id, latest.createdAt);
|
|
7236
|
+
}, [messages, isInitialLoading, markRead]);
|
|
7060
7237
|
useEffect(() => {
|
|
7061
7238
|
const el = scrollRef.current;
|
|
7062
7239
|
if (!el) return;
|
|
@@ -7169,27 +7346,50 @@ function ChatRoomView({
|
|
|
7169
7346
|
};
|
|
7170
7347
|
const getAttachmentUrl = (attachmentId) => `${config.baseUrl}/rooms/${roomId}/attachments/${attachmentId}`;
|
|
7171
7348
|
const getRoleLabel = (role) => roleLabel ? roleLabel(role) : role;
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7349
|
+
const isMessageSeen = (message) => {
|
|
7350
|
+
if (message.senderId !== currentUserId) return false;
|
|
7351
|
+
return Object.entries(readReceipts).some(
|
|
7352
|
+
([userId, lastReadAt]) => userId !== currentUserId && lastReadAt >= message.createdAt
|
|
7353
|
+
);
|
|
7354
|
+
};
|
|
7355
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex h-full min-h-0 flex-col", className), children: [
|
|
7356
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-shrink-0 flex items-center justify-between border-b px-4 py-2.5", children: [
|
|
7357
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
7358
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
7359
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium truncate", children: roomLabel ?? "Chat" }),
|
|
7360
|
+
caseNumber && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground truncate", children: caseNumber })
|
|
7361
|
+
] }),
|
|
7362
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex items-center gap-1.5", children: [
|
|
7363
|
+
/* @__PURE__ */ jsx(
|
|
7364
|
+
Circle,
|
|
7365
|
+
{
|
|
7366
|
+
className: cn(
|
|
7367
|
+
"h-2 w-2 fill-current",
|
|
7368
|
+
counterpartOnline ? "text-emerald-500" : "text-muted-foreground/40"
|
|
7369
|
+
)
|
|
7370
|
+
}
|
|
7371
|
+
),
|
|
7372
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: counterpartOnline ? labels.online ?? "Online" : labels.offline ?? "Offline" })
|
|
7373
|
+
] })
|
|
7177
7374
|
] }),
|
|
7178
7375
|
/* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
7179
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5", children: isConnected ? /* @__PURE__ */ jsx(Wifi, { className: "h-3.5 w-3.5 text-
|
|
7376
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5", children: isConnected ? /* @__PURE__ */ jsx(Wifi, { className: "h-3.5 w-3.5 text-emerald-500" }) : /* @__PURE__ */ jsx(WifiOff, { className: "h-3.5 w-3.5 text-destructive" }) }) }),
|
|
7180
7377
|
/* @__PURE__ */ jsx(TooltipContent, { children: isConnected ? "Connected" : error ?? "Reconnecting..." })
|
|
7181
7378
|
] }) })
|
|
7182
7379
|
] }),
|
|
7183
7380
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
|
|
7184
|
-
/* @__PURE__ */
|
|
7381
|
+
/* @__PURE__ */ jsx(
|
|
7185
7382
|
"div",
|
|
7186
7383
|
{
|
|
7187
7384
|
ref: scrollRef,
|
|
7188
7385
|
onScroll: handleScroll,
|
|
7189
7386
|
className: "flex-1 overflow-y-auto p-3",
|
|
7190
|
-
children: [
|
|
7387
|
+
children: isInitialLoading ? /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col items-center justify-center py-8", children: [
|
|
7388
|
+
/* @__PURE__ */ jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }),
|
|
7389
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: labels.loading ?? "Loading messages..." })
|
|
7390
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7191
7391
|
isLoadingHistory && /* @__PURE__ */ jsx("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) }),
|
|
7192
|
-
messages.length === 0
|
|
7392
|
+
messages.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col items-center justify-center py-8 text-center", children: [
|
|
7193
7393
|
/* @__PURE__ */ jsx(
|
|
7194
7394
|
MessageSquare,
|
|
7195
7395
|
{
|
|
@@ -7198,112 +7398,132 @@ function ChatRoomView({
|
|
|
7198
7398
|
}
|
|
7199
7399
|
),
|
|
7200
7400
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: labels.noMessages ?? "No messages yet" })
|
|
7201
|
-
] }) : /* @__PURE__ */ jsx("div", { className: "space-y-4", children: Object.entries(groupedMessages).map(
|
|
7202
|
-
/* @__PURE__ */ jsxs("div", {
|
|
7203
|
-
/* @__PURE__ */
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7401
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "space-y-4", children: Object.entries(groupedMessages).map(
|
|
7402
|
+
([date, dateMessages]) => /* @__PURE__ */ jsxs("div", { children: [
|
|
7403
|
+
/* @__PURE__ */ jsxs("div", { className: "my-3 flex items-center gap-3", children: [
|
|
7404
|
+
/* @__PURE__ */ jsx("div", { className: "h-px flex-1 bg-border" }),
|
|
7405
|
+
/* @__PURE__ */ jsx("span", { className: "px-2 text-xs text-muted-foreground", children: date }),
|
|
7406
|
+
/* @__PURE__ */ jsx("div", { className: "h-px flex-1 bg-border" })
|
|
7407
|
+
] }),
|
|
7408
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-3", children: dateMessages.map((message) => {
|
|
7409
|
+
if (message.senderRole === "system") {
|
|
7410
|
+
return /* @__PURE__ */ jsx(
|
|
7411
|
+
"div",
|
|
7412
|
+
{
|
|
7413
|
+
className: "flex justify-center py-1",
|
|
7414
|
+
children: /* @__PURE__ */ 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: [
|
|
7415
|
+
/* @__PURE__ */ jsx(Info, { className: "h-3 w-3 flex-shrink-0" }),
|
|
7416
|
+
/* @__PURE__ */ jsx("span", { className: "text-center", children: message.content })
|
|
7417
|
+
] })
|
|
7418
|
+
},
|
|
7419
|
+
message.id
|
|
7420
|
+
);
|
|
7421
|
+
}
|
|
7422
|
+
const isCurrentUser = message.senderId === currentUserId;
|
|
7423
|
+
const seen = isMessageSeen(message);
|
|
7424
|
+
return /* @__PURE__ */ jsxs(
|
|
7425
|
+
"div",
|
|
7426
|
+
{
|
|
7427
|
+
className: cn(
|
|
7428
|
+
"flex gap-2",
|
|
7429
|
+
isCurrentUser ? "flex-row-reverse" : "flex-row"
|
|
7226
7430
|
),
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7431
|
+
children: [
|
|
7432
|
+
/* @__PURE__ */ jsx(
|
|
7433
|
+
"div",
|
|
7434
|
+
{
|
|
7435
|
+
className: cn(
|
|
7436
|
+
"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full text-xs font-medium",
|
|
7437
|
+
roleColors[message.senderRole] ?? roleColors.client
|
|
7438
|
+
),
|
|
7439
|
+
children: (message.senderName ?? message.senderId).split(" ").map((n) => n[0]).join("").slice(0, 2).toUpperCase()
|
|
7440
|
+
}
|
|
7441
|
+
),
|
|
7442
|
+
/* @__PURE__ */ jsxs(
|
|
7443
|
+
"div",
|
|
7444
|
+
{
|
|
7445
|
+
className: cn(
|
|
7446
|
+
"flex max-w-[80%] flex-col",
|
|
7447
|
+
isCurrentUser ? "items-end" : "items-start"
|
|
7448
|
+
),
|
|
7449
|
+
children: [
|
|
7450
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-0.5 flex items-center gap-1.5", children: [
|
|
7451
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground", children: message.senderName ?? message.senderId.slice(0, 8) }),
|
|
7452
|
+
/* @__PURE__ */ jsx(
|
|
7453
|
+
"span",
|
|
7454
|
+
{
|
|
7455
|
+
className: cn(
|
|
7456
|
+
"rounded px-1 py-0.5 text-[10px] font-medium",
|
|
7457
|
+
roleColors[message.senderRole] ?? ""
|
|
7458
|
+
),
|
|
7459
|
+
children: getRoleLabel(message.senderRole)
|
|
7460
|
+
}
|
|
7461
|
+
),
|
|
7462
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: formatTime2(message.createdAt) })
|
|
7463
|
+
] }),
|
|
7464
|
+
message.content && /* @__PURE__ */ jsx(
|
|
7465
|
+
"div",
|
|
7239
7466
|
{
|
|
7240
7467
|
className: cn(
|
|
7241
|
-
"rounded px-
|
|
7242
|
-
|
|
7468
|
+
"rounded-2xl px-3 py-2 text-sm shadow-sm",
|
|
7469
|
+
isCurrentUser ? "rounded-br-md bg-primary text-primary-foreground" : "rounded-bl-md bg-muted text-foreground"
|
|
7243
7470
|
),
|
|
7244
|
-
children:
|
|
7471
|
+
children: message.content
|
|
7245
7472
|
}
|
|
7246
7473
|
),
|
|
7247
|
-
/* @__PURE__ */ jsx("
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
]
|
|
7301
|
-
},
|
|
7302
|
-
message.id
|
|
7303
|
-
);
|
|
7304
|
-
}) })
|
|
7305
|
-
] }, date)) })
|
|
7306
|
-
]
|
|
7474
|
+
message.attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-1 flex flex-col gap-1", children: message.attachments.map(
|
|
7475
|
+
(att) => att.mimeType.startsWith("image/") ? /* @__PURE__ */ jsx(
|
|
7476
|
+
"a",
|
|
7477
|
+
{
|
|
7478
|
+
href: getAttachmentUrl(att.id),
|
|
7479
|
+
target: "_blank",
|
|
7480
|
+
rel: "noopener noreferrer",
|
|
7481
|
+
className: "block max-w-[200px] overflow-hidden rounded-lg border",
|
|
7482
|
+
children: /* @__PURE__ */ jsx(
|
|
7483
|
+
"img",
|
|
7484
|
+
{
|
|
7485
|
+
src: getAttachmentUrl(att.id),
|
|
7486
|
+
alt: att.fileName,
|
|
7487
|
+
className: "h-auto w-full object-cover",
|
|
7488
|
+
loading: "lazy"
|
|
7489
|
+
}
|
|
7490
|
+
)
|
|
7491
|
+
},
|
|
7492
|
+
att.id
|
|
7493
|
+
) : /* @__PURE__ */ jsxs(
|
|
7494
|
+
"a",
|
|
7495
|
+
{
|
|
7496
|
+
href: getAttachmentUrl(att.id),
|
|
7497
|
+
target: "_blank",
|
|
7498
|
+
rel: "noopener noreferrer",
|
|
7499
|
+
className: "flex items-center gap-2 rounded-lg border bg-muted/50 px-2 py-1.5 text-xs transition-colors hover:bg-muted",
|
|
7500
|
+
children: [
|
|
7501
|
+
/* @__PURE__ */ jsx(FileIcon, { className: "h-3.5 w-3.5 flex-shrink-0 text-muted-foreground" }),
|
|
7502
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
7503
|
+
/* @__PURE__ */ jsx("p", { className: "truncate font-medium", children: att.fileName }),
|
|
7504
|
+
/* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground", children: formatFileSize(att.fileSize) })
|
|
7505
|
+
] }),
|
|
7506
|
+
/* @__PURE__ */ jsx(Download, { className: "h-3.5 w-3.5 flex-shrink-0 text-muted-foreground" })
|
|
7507
|
+
]
|
|
7508
|
+
},
|
|
7509
|
+
att.id
|
|
7510
|
+
)
|
|
7511
|
+
) }),
|
|
7512
|
+
isCurrentUser && /* @__PURE__ */ jsx("div", { className: "mt-0.5 flex items-center gap-0.5 text-[10px] text-muted-foreground", children: seen ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7513
|
+
/* @__PURE__ */ jsx(CheckCheck, { className: "h-3 w-3 text-primary" }),
|
|
7514
|
+
/* @__PURE__ */ jsx("span", { children: labels.seen ?? "Seen" })
|
|
7515
|
+
] }) : /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }) })
|
|
7516
|
+
]
|
|
7517
|
+
}
|
|
7518
|
+
)
|
|
7519
|
+
]
|
|
7520
|
+
},
|
|
7521
|
+
message.id
|
|
7522
|
+
);
|
|
7523
|
+
}) })
|
|
7524
|
+
] }, date)
|
|
7525
|
+
) })
|
|
7526
|
+
] })
|
|
7307
7527
|
}
|
|
7308
7528
|
),
|
|
7309
7529
|
typingUsers.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 px-3 py-1", children: /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground italic", children: [
|
|
@@ -7398,16 +7618,45 @@ function ChatRoomView({
|
|
|
7398
7618
|
] })
|
|
7399
7619
|
] });
|
|
7400
7620
|
}
|
|
7621
|
+
function Input({ className, type, ...props }) {
|
|
7622
|
+
return /* @__PURE__ */ jsx(
|
|
7623
|
+
"input",
|
|
7624
|
+
{
|
|
7625
|
+
type,
|
|
7626
|
+
"data-slot": "input",
|
|
7627
|
+
className: cn(
|
|
7628
|
+
"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",
|
|
7629
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
7630
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
|
7631
|
+
className
|
|
7632
|
+
),
|
|
7633
|
+
...props
|
|
7634
|
+
}
|
|
7635
|
+
);
|
|
7636
|
+
}
|
|
7401
7637
|
var roomTypeLabels = {
|
|
7402
7638
|
client_agent: "Client \u2194 Agent",
|
|
7403
7639
|
dealer_agent: "Dealer \u2194 Agent"
|
|
7404
7640
|
};
|
|
7641
|
+
var statusToneClasses = {
|
|
7642
|
+
success: "bg-emerald-500/10 text-emerald-700 dark:text-emerald-400 border-emerald-500/20",
|
|
7643
|
+
warning: "bg-amber-500/10 text-amber-700 dark:text-amber-400 border-amber-500/20",
|
|
7644
|
+
destructive: "bg-destructive/10 text-destructive border-destructive/20",
|
|
7645
|
+
info: "bg-primary/10 text-primary border-primary/20",
|
|
7646
|
+
neutral: "bg-muted text-muted-foreground border-border"
|
|
7647
|
+
};
|
|
7648
|
+
var roomTypeIconClasses = {
|
|
7649
|
+
client_agent: "bg-blue-500/10 text-blue-600 dark:text-blue-400",
|
|
7650
|
+
dealer_agent: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400"
|
|
7651
|
+
};
|
|
7405
7652
|
function ChatSidebar({
|
|
7406
7653
|
currentUserId,
|
|
7407
7654
|
config,
|
|
7408
7655
|
labels = {},
|
|
7409
7656
|
roleLabel,
|
|
7410
7657
|
roomTypeLabel,
|
|
7658
|
+
statusLabel,
|
|
7659
|
+
statusTone,
|
|
7411
7660
|
mobileTopOffset,
|
|
7412
7661
|
className
|
|
7413
7662
|
}) {
|
|
@@ -7418,9 +7667,12 @@ function ChatSidebar({
|
|
|
7418
7667
|
isLoading,
|
|
7419
7668
|
activeCaseId,
|
|
7420
7669
|
activeRoomId,
|
|
7670
|
+
searchQuery,
|
|
7671
|
+
setSearchQuery,
|
|
7421
7672
|
close,
|
|
7422
7673
|
selectRoom,
|
|
7423
|
-
back
|
|
7674
|
+
back,
|
|
7675
|
+
clearRoomUnread
|
|
7424
7676
|
} = useChatSidebar();
|
|
7425
7677
|
useEffect(() => {
|
|
7426
7678
|
if (!isOpen || typeof window === "undefined") return;
|
|
@@ -7436,12 +7688,24 @@ function ChatSidebar({
|
|
|
7436
7688
|
document.body.style.overflow = "";
|
|
7437
7689
|
};
|
|
7438
7690
|
}, [isOpen]);
|
|
7691
|
+
const filteredConversations = useMemo(() => {
|
|
7692
|
+
const q = searchQuery.trim().toLowerCase();
|
|
7693
|
+
if (!q) return conversations;
|
|
7694
|
+
return conversations.filter(
|
|
7695
|
+
(c) => c.caseNumber.toLowerCase().includes(q) || (c.clientName?.toLowerCase().includes(q) ?? false)
|
|
7696
|
+
);
|
|
7697
|
+
}, [conversations, searchQuery]);
|
|
7439
7698
|
if (!isOpen) return null;
|
|
7440
7699
|
const getRoomTypeLabel = (type) => {
|
|
7441
7700
|
if (roomTypeLabel) return roomTypeLabel(type);
|
|
7442
7701
|
return roomTypeLabels[type] ?? type;
|
|
7443
7702
|
};
|
|
7703
|
+
const getStatusLabel = (status) => statusLabel ? statusLabel(status) : status;
|
|
7704
|
+
const getStatusTone = (status) => statusTone?.(status) ?? "neutral";
|
|
7444
7705
|
const activeConversation = activeCaseId ? conversations.find((c) => c.caseId === activeCaseId) : null;
|
|
7706
|
+
const activeRoom = activeConversation?.rooms.find(
|
|
7707
|
+
(r2) => r2.id === activeRoomId
|
|
7708
|
+
);
|
|
7445
7709
|
const cssVars = {
|
|
7446
7710
|
"--sidebar-top": mobileTopOffset ?? "0px"
|
|
7447
7711
|
};
|
|
@@ -7472,40 +7736,99 @@ function ChatSidebar({
|
|
|
7472
7736
|
}
|
|
7473
7737
|
)
|
|
7474
7738
|
] }),
|
|
7739
|
+
/* @__PURE__ */ jsx("div", { className: "border-b px-3 py-2", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
7740
|
+
/* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }),
|
|
7741
|
+
/* @__PURE__ */ jsx(
|
|
7742
|
+
Input,
|
|
7743
|
+
{
|
|
7744
|
+
value: searchQuery,
|
|
7745
|
+
onChange: (e) => setSearchQuery(e.target.value),
|
|
7746
|
+
placeholder: labels.searchPlaceholder ?? "Search by case or client...",
|
|
7747
|
+
className: "h-8 pl-8 text-sm"
|
|
7748
|
+
}
|
|
7749
|
+
)
|
|
7750
|
+
] }) }),
|
|
7475
7751
|
/* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", children: isLoading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx(Loader2, { className: "h-5 w-5 animate-spin text-muted-foreground" }) }) : conversations.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center px-4", children: [
|
|
7476
7752
|
/* @__PURE__ */ jsx(MessageSquare, { className: "mb-3 h-8 w-8 text-muted-foreground/50" }),
|
|
7477
7753
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: labels.noConversations ?? "No conversations yet" })
|
|
7478
|
-
] }) : /* @__PURE__ */
|
|
7479
|
-
/* @__PURE__ */
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
"
|
|
7754
|
+
] }) : filteredConversations.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center px-4", children: [
|
|
7755
|
+
/* @__PURE__ */ jsx(Search, { className: "mb-3 h-8 w-8 text-muted-foreground/50" }),
|
|
7756
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: labels.noResults ?? "No matching conversations" })
|
|
7757
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "space-y-2 p-3", children: filteredConversations.map((conv) => {
|
|
7758
|
+
const caseUnread = conv.rooms.reduce(
|
|
7759
|
+
(sum, r2) => sum + (r2.unreadCount ?? 0),
|
|
7760
|
+
0
|
|
7761
|
+
);
|
|
7762
|
+
return /* @__PURE__ */ jsxs(
|
|
7763
|
+
"div",
|
|
7488
7764
|
{
|
|
7489
|
-
|
|
7490
|
-
onClick: () => selectRoom(room.id, conv.caseId),
|
|
7491
|
-
className: cn(
|
|
7492
|
-
"flex w-full items-center justify-between rounded-md px-2 py-2 text-left transition-colors hover:bg-accent",
|
|
7493
|
-
activeRoomId === room.id && "bg-accent"
|
|
7494
|
-
),
|
|
7765
|
+
className: "rounded-lg border bg-card shadow-sm",
|
|
7495
7766
|
children: [
|
|
7496
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-
|
|
7497
|
-
/* @__PURE__ */
|
|
7498
|
-
|
|
7767
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2 border-b bg-muted/40 px-3 py-2.5", children: [
|
|
7768
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
7769
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
7770
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground truncate", children: conv.caseNumber }),
|
|
7771
|
+
caseUnread > 0 && /* @__PURE__ */ jsx(
|
|
7772
|
+
"span",
|
|
7773
|
+
{
|
|
7774
|
+
className: "h-2 w-2 flex-shrink-0 rounded-full bg-primary",
|
|
7775
|
+
"aria-label": `${caseUnread} unread`
|
|
7776
|
+
}
|
|
7777
|
+
)
|
|
7778
|
+
] }),
|
|
7779
|
+
conv.clientName && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground truncate", children: conv.clientName })
|
|
7780
|
+
] }),
|
|
7781
|
+
/* @__PURE__ */ jsx(
|
|
7782
|
+
"span",
|
|
7783
|
+
{
|
|
7784
|
+
className: cn(
|
|
7785
|
+
"flex-shrink-0 rounded-full border px-2 py-0.5 text-[10px] font-medium",
|
|
7786
|
+
statusToneClasses[getStatusTone(conv.caseStatus)]
|
|
7787
|
+
),
|
|
7788
|
+
children: getStatusLabel(conv.caseStatus)
|
|
7789
|
+
}
|
|
7790
|
+
)
|
|
7499
7791
|
] }),
|
|
7500
|
-
/* @__PURE__ */
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7792
|
+
/* @__PURE__ */ jsx("div", { className: "ml-1 divide-y", children: conv.rooms.map((room) => {
|
|
7793
|
+
const RoomIcon = room.type === "dealer_agent" ? Store : User;
|
|
7794
|
+
const unread = room.unreadCount ?? 0;
|
|
7795
|
+
const iconClasses = roomTypeIconClasses[room.type] ?? "bg-muted text-muted-foreground";
|
|
7796
|
+
return /* @__PURE__ */ jsxs(
|
|
7797
|
+
"button",
|
|
7798
|
+
{
|
|
7799
|
+
type: "button",
|
|
7800
|
+
onClick: () => selectRoom(room.id, conv.caseId),
|
|
7801
|
+
className: cn(
|
|
7802
|
+
"flex w-full items-center gap-2.5 py-2.5 pl-4 pr-3 text-left transition-colors hover:bg-accent/60",
|
|
7803
|
+
activeRoomId === room.id && "bg-accent"
|
|
7804
|
+
),
|
|
7805
|
+
children: [
|
|
7806
|
+
/* @__PURE__ */ jsx(
|
|
7807
|
+
"div",
|
|
7808
|
+
{
|
|
7809
|
+
className: cn(
|
|
7810
|
+
"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full",
|
|
7811
|
+
iconClasses
|
|
7812
|
+
),
|
|
7813
|
+
children: /* @__PURE__ */ jsx(RoomIcon, { className: "h-3.5 w-3.5" })
|
|
7814
|
+
}
|
|
7815
|
+
),
|
|
7816
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
7817
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: getRoomTypeLabel(room.type) }),
|
|
7818
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: room.lastMessageAt ? formatRelativeTime(room.lastMessageAt) : labels.noMessagesShort ?? "No messages" })
|
|
7819
|
+
] }),
|
|
7820
|
+
unread > 0 && /* @__PURE__ */ 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 }),
|
|
7821
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 flex-shrink-0 text-muted-foreground" })
|
|
7822
|
+
]
|
|
7823
|
+
},
|
|
7824
|
+
room.id
|
|
7825
|
+
);
|
|
7826
|
+
}) })
|
|
7504
7827
|
]
|
|
7505
7828
|
},
|
|
7506
|
-
|
|
7507
|
-
)
|
|
7508
|
-
|
|
7829
|
+
conv.caseId
|
|
7830
|
+
);
|
|
7831
|
+
}) }) })
|
|
7509
7832
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7510
7833
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b px-3 py-2", children: [
|
|
7511
7834
|
/* @__PURE__ */ jsx(
|
|
@@ -7519,8 +7842,11 @@ function ChatSidebar({
|
|
|
7519
7842
|
}
|
|
7520
7843
|
),
|
|
7521
7844
|
activeConversation && /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
7522
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: activeConversation.caseNumber }),
|
|
7523
|
-
|
|
7845
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: activeRoom ? getRoomTypeLabel(activeRoom.type) : activeConversation.caseNumber }),
|
|
7846
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground truncate", children: [
|
|
7847
|
+
activeConversation.caseNumber,
|
|
7848
|
+
activeConversation.clientName ? ` \xB7 ${activeConversation.clientName}` : ""
|
|
7849
|
+
] })
|
|
7524
7850
|
] }),
|
|
7525
7851
|
/* @__PURE__ */ jsx(
|
|
7526
7852
|
Button,
|
|
@@ -7539,8 +7865,11 @@ function ChatSidebar({
|
|
|
7539
7865
|
roomId: activeRoomId,
|
|
7540
7866
|
currentUserId,
|
|
7541
7867
|
config,
|
|
7868
|
+
roomLabel: activeRoom ? getRoomTypeLabel(activeRoom.type) : void 0,
|
|
7869
|
+
caseNumber: activeConversation?.caseNumber,
|
|
7542
7870
|
labels,
|
|
7543
7871
|
roleLabel,
|
|
7872
|
+
onMarkedRead: clearRoomUnread,
|
|
7544
7873
|
className: "flex-1"
|
|
7545
7874
|
}
|
|
7546
7875
|
)
|
|
@@ -10260,22 +10589,6 @@ function USAddressInput({
|
|
|
10260
10589
|
] })
|
|
10261
10590
|
] });
|
|
10262
10591
|
}
|
|
10263
|
-
function Input({ className, type, ...props }) {
|
|
10264
|
-
return /* @__PURE__ */ jsx(
|
|
10265
|
-
"input",
|
|
10266
|
-
{
|
|
10267
|
-
type,
|
|
10268
|
-
"data-slot": "input",
|
|
10269
|
-
className: cn(
|
|
10270
|
-
"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",
|
|
10271
|
-
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
10272
|
-
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
|
10273
|
-
className
|
|
10274
|
-
),
|
|
10275
|
-
...props
|
|
10276
|
-
}
|
|
10277
|
-
);
|
|
10278
|
-
}
|
|
10279
10592
|
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";
|
|
10280
10593
|
var PhoneInput = React7.forwardRef(
|
|
10281
10594
|
({ className, onChange, value, defaultCountry = "MX", ...props }, ref) => {
|