ajaxter-chat 1.0.3 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +96 -204
  2. package/dist/components/ChatScreen/index.d.ts +12 -0
  3. package/dist/components/ChatScreen/index.js +83 -0
  4. package/dist/components/ChatWidget.d.ts +0 -24
  5. package/dist/components/ChatWidget.js +129 -38
  6. package/dist/components/HomeScreen/index.d.ts +9 -0
  7. package/dist/components/HomeScreen/index.js +71 -0
  8. package/dist/components/MaintenanceView/index.d.ts +1 -1
  9. package/dist/components/MaintenanceView/index.js +15 -52
  10. package/dist/components/RecentChatsScreen/index.d.ts +16 -0
  11. package/dist/components/RecentChatsScreen/index.js +38 -0
  12. package/dist/components/Tabs/BottomTabs.d.ts +10 -0
  13. package/dist/components/Tabs/BottomTabs.js +29 -0
  14. package/dist/components/TicketScreen/index.d.ts +9 -0
  15. package/dist/components/TicketScreen/index.js +71 -0
  16. package/dist/components/UserListScreen/index.d.ts +13 -0
  17. package/dist/components/UserListScreen/index.js +64 -0
  18. package/dist/config/index.d.ts +0 -13
  19. package/dist/config/index.js +20 -95
  20. package/dist/hooks/useChat.d.ts +3 -7
  21. package/dist/hooks/useChat.js +8 -30
  22. package/dist/hooks/useUsers.d.ts +3 -10
  23. package/dist/hooks/useUsers.js +5 -11
  24. package/dist/index.d.ts +8 -7
  25. package/dist/index.js +7 -12
  26. package/dist/services/userService.d.ts +0 -5
  27. package/dist/services/userService.js +6 -15
  28. package/dist/src/components/ChatScreen/index.d.ts +12 -0
  29. package/dist/src/components/ChatScreen/index.js +83 -0
  30. package/dist/src/components/ChatWidget.d.ts +4 -0
  31. package/dist/src/components/ChatWidget.js +141 -0
  32. package/dist/src/components/HomeScreen/index.d.ts +9 -0
  33. package/dist/src/components/HomeScreen/index.js +71 -0
  34. package/dist/src/components/MaintenanceView/index.d.ts +7 -0
  35. package/dist/src/components/MaintenanceView/index.js +16 -0
  36. package/dist/src/components/RecentChatsScreen/index.d.ts +16 -0
  37. package/dist/src/components/RecentChatsScreen/index.js +38 -0
  38. package/dist/src/components/Tabs/BottomTabs.d.ts +10 -0
  39. package/dist/src/components/Tabs/BottomTabs.js +29 -0
  40. package/dist/src/components/TicketScreen/index.d.ts +9 -0
  41. package/dist/src/components/TicketScreen/index.js +71 -0
  42. package/dist/src/components/UserListScreen/index.d.ts +13 -0
  43. package/dist/src/components/UserListScreen/index.js +64 -0
  44. package/dist/src/config/index.d.ts +3 -0
  45. package/dist/src/config/index.js +38 -0
  46. package/dist/src/hooks/useChat.d.ts +8 -0
  47. package/dist/src/hooks/useChat.js +26 -0
  48. package/dist/src/hooks/useUsers.d.ts +7 -0
  49. package/dist/src/hooks/useUsers.js +26 -0
  50. package/dist/src/index.d.ts +14 -0
  51. package/dist/src/index.js +13 -0
  52. package/dist/src/services/userService.d.ts +2 -0
  53. package/dist/src/services/userService.js +9 -0
  54. package/dist/src/types/index.d.ts +59 -0
  55. package/dist/src/types/index.js +1 -0
  56. package/dist/src/utils/theme.d.ts +3 -0
  57. package/dist/src/utils/theme.js +13 -0
  58. package/dist/types/index.d.ts +23 -36
  59. package/dist/utils/theme.d.ts +0 -1
  60. package/dist/utils/theme.js +3 -18
  61. package/package.json +10 -20
  62. package/src/components/ChatScreen/index.tsx +205 -0
  63. package/src/components/ChatWidget.tsx +327 -0
  64. package/src/components/HomeScreen/index.tsx +130 -0
  65. package/src/components/MaintenanceView/index.tsx +41 -0
  66. package/src/components/RecentChatsScreen/index.tsx +108 -0
  67. package/src/components/Tabs/BottomTabs.tsx +82 -0
  68. package/src/components/TicketScreen/index.tsx +170 -0
  69. package/src/components/UserListScreen/index.tsx +181 -0
  70. package/src/config/index.ts +46 -0
  71. package/src/hooks/useChat.ts +31 -0
  72. package/src/hooks/useUsers.ts +27 -0
  73. package/src/index.ts +18 -0
  74. package/src/services/userService.ts +9 -0
  75. package/src/types/index.ts +82 -0
  76. package/src/utils/theme.ts +16 -0
  77. package/dist/components/BottomNav/index.d.ts +0 -10
  78. package/dist/components/BottomNav/index.js +0 -32
  79. package/dist/components/ChatBox/index.d.ts +0 -15
  80. package/dist/components/ChatBox/index.js +0 -228
  81. package/dist/components/ChatButton/index.d.ts +0 -9
  82. package/dist/components/ChatButton/index.js +0 -17
  83. package/dist/components/ChatWindow/index.d.ts +0 -10
  84. package/dist/components/ChatWindow/index.js +0 -286
  85. package/dist/components/HomeView/index.d.ts +0 -12
  86. package/dist/components/HomeView/index.js +0 -51
  87. package/dist/components/UserList/index.d.ts +0 -13
  88. package/dist/components/UserList/index.js +0 -136
@@ -1,50 +1,141 @@
1
- 'use client'; // Next.js App Router directive (ignored in React/Pages Router)
1
+ 'use client';
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useEffect } from 'react';
4
- import { loadChatConfig } from '../config';
5
- import { ChatButton } from './ChatButton';
6
- import { ChatWindow } from './ChatWindow';
3
+ import { useState, useEffect, useCallback } from 'react';
4
+ import { loadChatConfig, buildUserListUrl } from '../config';
7
5
  import { mergeTheme } from '../utils/theme';
8
- /**
9
- * ChatWidget
10
- *
11
- * Drop-in chat widget for React.js and Next.js apps.
12
- * All behavior is configured via environment variables.
13
- * All UI styling is configured via the `theme` prop.
14
- *
15
- * @example
16
- * // Basic usage
17
- * <ChatWidget />
18
- *
19
- * @example
20
- * // With custom theme
21
- * <ChatWidget
22
- * theme={{
23
- * primaryColor: '#FF6B6B',
24
- * buttonColor: '#FF6B6B',
25
- * buttonLabel: 'Need Help?',
26
- * buttonPosition: 'bottom-left',
27
- * fontFamily: "'Inter', sans-serif",
28
- * borderRadius: '12px',
29
- * }}
30
- * />
31
- */
6
+ import { useUsers } from '../hooks/useUsers';
7
+ import { useChat } from '../hooks/useChat';
8
+ // Screens
9
+ import { HomeScreen } from './HomeScreen';
10
+ import { UserListScreen } from './UserListScreen';
11
+ import { ChatScreen } from './ChatScreen';
12
+ import { RecentChatsScreen } from './RecentChatsScreen';
13
+ import { TicketScreen } from './TicketScreen';
14
+ import { MaintenanceView } from './MaintenanceView';
15
+ import { BottomTabs } from './Tabs/BottomTabs';
32
16
  export const ChatWidget = ({ theme }) => {
33
- const [isOpen, setIsOpen] = useState(false);
34
17
  const [isMounted, setIsMounted] = useState(false);
35
- // SSR Safety: Only render on the client
36
- useEffect(() => {
37
- setIsMounted(true);
38
- }, []);
39
- // Load config from env (safe on both client and server)
18
+ const [isOpen, setIsOpen] = useState(false);
19
+ const [isMaximized, setIsMaximized] = useState(false);
20
+ const [activeTab, setActiveTab] = useState('home');
21
+ const [screen, setScreen] = useState('home');
22
+ const [userListCtx, setUserListCtx] = useState('support');
23
+ const [tickets, setTickets] = useState([]);
24
+ // SSR guard
25
+ useEffect(() => { setIsMounted(true); }, []);
40
26
  const config = loadChatConfig();
41
27
  const t = mergeTheme(theme);
42
- // Don't render anything server-side
28
+ const apiUrl = buildUserListUrl(config);
29
+ // Determine filter based on context
30
+ const filterType = userListCtx === 'support' ? 'developer' : 'user';
31
+ const { users, loading, error } = useUsers(apiUrl, filterType, config.status === 'ACTIVE' && screen === 'user-list');
32
+ const { messages, activeUser, selectUser, sendMessage, clearChat } = useChat();
33
+ // ── Navigation helpers ─────────────────────────────────────────────────────
34
+ const goHome = useCallback(() => { setScreen('home'); setActiveTab('home'); }, []);
35
+ const handleTabChange = useCallback((tab) => {
36
+ setActiveTab(tab);
37
+ if (tab === 'home') {
38
+ setScreen('home');
39
+ }
40
+ if (tab === 'chats') {
41
+ setScreen('recent-chats');
42
+ }
43
+ if (tab === 'tickets') {
44
+ setScreen('tickets');
45
+ }
46
+ }, []);
47
+ const handleCardClick = useCallback((ctx) => {
48
+ if (ctx === 'ticket') {
49
+ setActiveTab('tickets');
50
+ setScreen('tickets');
51
+ }
52
+ else {
53
+ setUserListCtx(ctx);
54
+ setScreen('user-list');
55
+ }
56
+ }, []);
57
+ const handleSelectUser = useCallback((user) => {
58
+ selectUser(user);
59
+ setScreen('chat');
60
+ }, [selectUser]);
61
+ const handleBackFromUserList = useCallback(() => {
62
+ setScreen('home');
63
+ }, []);
64
+ const handleBackFromChat = useCallback(() => {
65
+ clearChat();
66
+ setScreen('user-list');
67
+ }, [clearChat]);
68
+ const handleRaiseTicket = useCallback((title, description) => {
69
+ const t = {
70
+ id: `ticket_${Date.now()}`,
71
+ title,
72
+ description,
73
+ status: 'open',
74
+ priority: 'medium',
75
+ createdAt: new Date(),
76
+ updatedAt: new Date(),
77
+ };
78
+ setTickets(prev => [t, ...prev]);
79
+ }, []);
80
+ // ── Sizing ─────────────────────────────────────────────────────────────────
81
+ const normalW = 380;
82
+ const normalH = 560;
83
+ const maxW = 480;
84
+ const maxH = 720;
85
+ const width = isMaximized ? maxW : normalW;
86
+ const height = isMaximized ? maxH : normalH;
87
+ const posStyle = t.buttonPosition === 'bottom-left'
88
+ ? { left: 24, right: 'auto' }
89
+ : { right: 24, left: 'auto' };
43
90
  if (!isMounted)
44
91
  return null;
45
- // DISABLE: render nothing at all
46
92
  if (config.status === 'DISABLE')
47
93
  return null;
48
- return (_jsxs(_Fragment, { children: [isOpen && (_jsx(ChatWindow, { config: config, theme: theme, buttonPosition: t.buttonPosition, onClose: () => setIsOpen(false) })), _jsx(ChatButton, { isOpen: isOpen, onClick: () => setIsOpen((prev) => !prev), theme: theme })] }));
94
+ return (_jsxs(_Fragment, { children: [_jsx("style", { children: `
95
+ @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700;800&display=swap');
96
+ @keyframes cw-fadeUp {
97
+ from { opacity:0; transform:translateY(10px); }
98
+ to { opacity:1; transform:translateY(0); }
99
+ }
100
+ @keyframes cw-slideInRight {
101
+ from { opacity:0; transform:translateX(18px); }
102
+ to { opacity:1; transform:translateX(0); }
103
+ }
104
+ @keyframes cw-popIn {
105
+ from { opacity:0; transform:scale(0.88) translateY(16px); }
106
+ to { opacity:1; transform:scale(1) translateY(0); }
107
+ }
108
+ .cw-scrollbar::-webkit-scrollbar { width:4px; }
109
+ .cw-scrollbar::-webkit-scrollbar-track { background:transparent; }
110
+ .cw-scrollbar::-webkit-scrollbar-thumb { background:#e0e0e0; border-radius:4px; }
111
+ ` }), _jsx("button", { onClick: () => setIsOpen(o => !o), "aria-label": isOpen ? 'Close chat' : t.buttonLabel, style: Object.assign(Object.assign({ position: 'fixed', bottom: 24 }, posStyle), { zIndex: 9999, display: 'flex', alignItems: 'center', gap: 10, padding: isOpen ? '14px' : '13px 22px', backgroundColor: t.buttonColor, color: t.buttonTextColor, border: 'none', borderRadius: 50, cursor: 'pointer', fontFamily: t.fontFamily, fontSize: '15px', fontWeight: 700, boxShadow: `0 8px 28px ${t.buttonColor}55`, transition: 'all 0.3s cubic-bezier(0.34,1.56,0.64,1)', transform: isOpen ? 'scale(0.94)' : 'scale(1)', minWidth: isOpen ? 50 : 'auto', justifyContent: 'center' }), onMouseEnter: e => {
112
+ if (!isOpen) {
113
+ e.currentTarget.style.transform = 'scale(1.06) translateY(-2px)';
114
+ e.currentTarget.style.boxShadow = `0 12px 36px ${t.buttonColor}77`;
115
+ }
116
+ }, onMouseLeave: e => {
117
+ e.currentTarget.style.transform = isOpen ? 'scale(0.94)' : 'scale(1)';
118
+ e.currentTarget.style.boxShadow = `0 8px 28px ${t.buttonColor}55`;
119
+ }, children: isOpen
120
+ ? _jsx(CloseIcon, { color: t.buttonTextColor })
121
+ : _jsxs(_Fragment, { children: [_jsx(ChatBubbleIcon, { color: t.buttonTextColor }), _jsx("span", { children: t.buttonLabel })] }) }), isOpen && (_jsxs("div", { style: Object.assign(Object.assign({ position: 'fixed', bottom: 86 }, posStyle), { zIndex: 9998, width,
122
+ height, maxWidth: 'calc(100vw - 32px)', maxHeight: 'calc(100vh - 110px)', backgroundColor: '#fff', borderRadius: t.borderRadius, boxShadow: '0 20px 70px rgba(0,0,0,0.2), 0 6px 20px rgba(0,0,0,0.08)', display: 'flex', flexDirection: 'column', overflow: 'hidden', fontFamily: t.fontFamily, animation: 'cw-popIn 0.3s cubic-bezier(0.34,1.56,0.64,1)', transition: 'width 0.3s ease, height 0.3s ease' }), children: [screen !== 'chat' && (_jsx("button", { onClick: () => setIsMaximized(m => !m), title: isMaximized ? 'Minimize' : 'Maximize', style: {
123
+ position: 'absolute', top: 12, right: 48, zIndex: 10,
124
+ background: 'rgba(255,255,255,0.22)', border: 'none', borderRadius: '50%',
125
+ width: 28, height: 28, display: 'flex', alignItems: 'center', justifyContent: 'center',
126
+ cursor: 'pointer', transition: 'background 0.15s',
127
+ }, onMouseEnter: e => e.currentTarget.style.background = 'rgba(255,255,255,0.38)', onMouseLeave: e => e.currentTarget.style.background = 'rgba(255,255,255,0.22)', children: isMaximized
128
+ ? _jsx(MinimizeIcon, {})
129
+ : _jsx(MaximizeIcon, {}) })), screen !== 'chat' && (_jsx("button", { onClick: () => setIsOpen(false), title: "Close", style: {
130
+ position: 'absolute', top: 12, right: 12, zIndex: 10,
131
+ background: 'rgba(255,255,255,0.22)', border: 'none', borderRadius: '50%',
132
+ width: 28, height: 28, display: 'flex', alignItems: 'center', justifyContent: 'center',
133
+ cursor: 'pointer',
134
+ }, children: _jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: "#fff", strokeWidth: "2.5", strokeLinecap: "round" }) }) })), _jsxs("div", { className: "cw-scrollbar", style: { flex: 1, overflow: 'hidden', display: 'flex', flexDirection: 'column' }, children: [config.status === 'MAINTENANCE' && (_jsx(MaintenanceView, { primaryColor: t.primaryColor, fontFamily: t.fontFamily })), config.status === 'ACTIVE' && (_jsxs(_Fragment, { children: [screen === 'home' && (_jsx(HomeScreen, { config: config, theme: theme, onNavigate: handleCardClick })), screen === 'user-list' && (_jsx(UserListScreen, { context: userListCtx, users: users, loading: loading, error: error, theme: theme, onBack: handleBackFromUserList, onSelectUser: handleSelectUser })), screen === 'chat' && activeUser && (_jsx(ChatScreen, { activeUser: activeUser, messages: messages, onSend: sendMessage, onBack: handleBackFromChat, onClose: () => setIsOpen(false), theme: theme })), screen === 'recent-chats' && (_jsx(RecentChatsScreen, { chats: [], theme: theme, onSelectChat: handleSelectUser })), screen === 'tickets' && (_jsx(TicketScreen, { tickets: tickets, theme: theme, onRaiseTicket: handleRaiseTicket }))] }))] }), screen !== 'chat' && screen !== 'user-list' && config.status !== 'MAINTENANCE' && (_jsx(BottomTabs, { active: activeTab, onChange: handleTabChange, primaryColor: t.primaryColor, fontFamily: t.fontFamily }))] }))] }));
49
135
  };
50
136
  export default ChatWidget;
137
+ // ── Tiny SVG icons ─────────────────────────────────────────────────────────────
138
+ const ChatBubbleIcon = ({ color }) => (_jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
139
+ const CloseIcon = ({ color }) => (_jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: color, strokeWidth: "2.5", strokeLinecap: "round" }) }));
140
+ const MaximizeIcon = () => (_jsx("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M8 3H5a2 2 0 00-2 2v3M21 8V5a2 2 0 00-2-2h-3M3 16v3a2 2 0 002 2h3M16 21h3a2 2 0 002-2v-3", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round" }) }));
141
+ const MinimizeIcon = () => (_jsx("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M8 3v5H3M21 8h-5V3M3 16h5v5M16 21v-5h5", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round" }) }));
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { ChatConfig, ChatWidgetTheme, UserListContext } from '../../types';
3
+ interface HomeScreenProps {
4
+ config: ChatConfig;
5
+ theme?: ChatWidgetTheme;
6
+ onNavigate: (ctx: UserListContext | 'ticket') => void;
7
+ }
8
+ export declare const HomeScreen: React.FC<HomeScreenProps>;
9
+ export {};
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { mergeTheme } from '../../utils/theme';
3
+ export const HomeScreen = ({ config, theme, onNavigate }) => {
4
+ const t = mergeTheme(theme);
5
+ const showSupport = config.chatType === 'SUPPORT' || config.chatType === 'BOTH';
6
+ const showConversation = config.chatType === 'CHAT' || config.chatType === 'BOTH';
7
+ const cards = [
8
+ showSupport && {
9
+ key: 'support',
10
+ title: 'Need Support',
11
+ subtitle: 'We typically reply in a few minutes',
12
+ onClick: () => onNavigate('support'),
13
+ },
14
+ showConversation && {
15
+ key: 'conversation',
16
+ title: 'New Conversation',
17
+ subtitle: 'With your colleague',
18
+ onClick: () => onNavigate('conversation'),
19
+ },
20
+ // Raise Ticket is always shown
21
+ {
22
+ key: 'ticket',
23
+ title: 'Raise Ticket',
24
+ subtitle: 'For major changes',
25
+ onClick: () => onNavigate('ticket'),
26
+ },
27
+ ].filter(Boolean);
28
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [_jsxs("div", { style: {
29
+ backgroundColor: t.primaryColor,
30
+ padding: '32px 24px 40px',
31
+ flexShrink: 0,
32
+ }, children: [_jsx("h1", { style: {
33
+ margin: '0 0 8px',
34
+ fontSize: '28px',
35
+ fontWeight: 800,
36
+ color: '#fff',
37
+ letterSpacing: '-0.03em',
38
+ fontFamily: t.fontFamily,
39
+ }, children: "Hi there \uD83D\uDC4B" }), _jsx("p", { style: { margin: 0, fontSize: '15px', color: 'rgba(255,255,255,0.85)', fontFamily: t.fontFamily }, children: "Need help? start a conversation:" })] }), _jsx("div", { style: {
40
+ flex: 1,
41
+ overflowY: 'auto',
42
+ padding: '0 16px 16px',
43
+ marginTop: '-24px',
44
+ display: 'flex',
45
+ flexDirection: 'column',
46
+ gap: '10px',
47
+ }, children: cards.map((card, i) => (_jsxs("button", { onClick: card.onClick, style: {
48
+ width: '100%',
49
+ background: '#fff',
50
+ border: 'none',
51
+ borderRadius: '14px',
52
+ padding: '18px 20px',
53
+ display: 'flex',
54
+ alignItems: 'center',
55
+ justifyContent: 'space-between',
56
+ cursor: 'pointer',
57
+ textAlign: 'left',
58
+ boxShadow: '0 2px 12px rgba(0,0,0,0.09)',
59
+ transition: 'transform 0.15s ease, box-shadow 0.15s ease',
60
+ animation: `cw-fadeUp 0.35s ease both`,
61
+ animationDelay: `${i * 0.08}s`,
62
+ fontFamily: t.fontFamily,
63
+ }, onMouseEnter: e => {
64
+ e.currentTarget.style.transform = 'translateY(-2px)';
65
+ e.currentTarget.style.boxShadow = '0 6px 20px rgba(0,0,0,0.13)';
66
+ }, onMouseLeave: e => {
67
+ e.currentTarget.style.transform = 'translateY(0)';
68
+ e.currentTarget.style.boxShadow = '0 2px 12px rgba(0,0,0,0.09)';
69
+ }, children: [_jsxs("div", { children: [_jsx("div", { style: { fontWeight: 700, fontSize: '15px', color: '#1a2332', marginBottom: '3px' }, children: card.title }), _jsx("div", { style: { fontSize: '13px', color: '#7b8fa1' }, children: card.subtitle })] }), _jsx(SendArrow, { color: t.primaryColor })] }, card.key))) })] }));
70
+ };
71
+ const SendArrow = ({ color }) => (_jsx("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: _jsx("path", { d: "M22 2L11 13M22 2L15 22L11 13L2 9L22 2Z", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  interface MaintenanceViewProps {
3
- fontFamily: string;
4
3
  primaryColor: string;
4
+ fontFamily: string;
5
5
  }
6
6
  export declare const MaintenanceView: React.FC<MaintenanceViewProps>;
7
7
  export {};
@@ -1,53 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- export const MaintenanceView = ({ fontFamily, primaryColor, }) => {
3
- return (_jsxs("div", { style: {
4
- display: 'flex',
5
- flexDirection: 'column',
6
- alignItems: 'center',
7
- justifyContent: 'center',
8
- height: '100%',
9
- padding: '32px',
10
- fontFamily,
11
- textAlign: 'center',
12
- gap: '16px',
13
- }, children: [_jsx("div", { style: {
14
- width: '72px',
15
- height: '72px',
16
- borderRadius: '50%',
17
- backgroundColor: `${primaryColor}15`,
18
- display: 'flex',
19
- alignItems: 'center',
20
- justifyContent: 'center',
21
- marginBottom: '8px',
22
- }, children: _jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M12 9v4M12 17h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z", stroke: primaryColor, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsx("h3", { style: {
23
- margin: 0,
24
- fontSize: '17px',
25
- fontWeight: 700,
26
- color: '#1a1a2e',
27
- letterSpacing: '-0.02em',
28
- }, children: "Under Maintenance" }), _jsx("p", { style: {
29
- margin: 0,
30
- fontSize: '14px',
31
- color: '#888',
32
- lineHeight: 1.6,
33
- maxWidth: '220px',
34
- }, children: "Chat is under maintenance. We'll be back shortly. Thank you for your patience!" }), _jsxs("span", { style: {
35
- display: 'inline-flex',
36
- alignItems: 'center',
37
- gap: '6px',
38
- padding: '6px 14px',
39
- borderRadius: '20px',
40
- backgroundColor: '#fff3cd',
41
- color: '#856404',
42
- fontSize: '12px',
43
- fontWeight: 600,
44
- marginTop: '8px',
45
- border: '1px solid #ffc10730',
46
- }, children: [_jsx("span", { style: {
47
- width: '6px',
48
- height: '6px',
49
- borderRadius: '50%',
50
- backgroundColor: '#ffc107',
51
- display: 'inline-block',
52
- } }), "Temporarily Unavailable"] })] }));
53
- };
2
+ export const MaintenanceView = ({ primaryColor, fontFamily }) => (_jsxs("div", { style: {
3
+ display: 'flex', flexDirection: 'column', alignItems: 'center',
4
+ justifyContent: 'center', height: '100%', padding: '32px',
5
+ fontFamily, textAlign: 'center', gap: 16,
6
+ }, children: [_jsx("div", { style: {
7
+ width: 72, height: 72, borderRadius: '50%',
8
+ backgroundColor: `${primaryColor}15`,
9
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
10
+ }, children: _jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M12 9v4M12 17h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z", stroke: primaryColor, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsx("h3", { style: { margin: 0, fontSize: '17px', fontWeight: 800, color: '#1a2332', letterSpacing: '-0.02em' }, children: "Under Maintenance" }), _jsx("p", { style: { margin: 0, fontSize: '14px', color: '#7b8fa1', lineHeight: 1.6, maxWidth: 220 }, children: "Chat is under maintenance. We'll be back shortly!" }), _jsxs("span", { style: {
11
+ display: 'inline-flex', alignItems: 'center', gap: 6,
12
+ padding: '6px 14px', borderRadius: 20,
13
+ backgroundColor: '#fff3cd', color: '#856404',
14
+ fontSize: '12px', fontWeight: 700,
15
+ border: '1px solid #ffc10730',
16
+ }, children: [_jsx("span", { style: { width: 6, height: 6, borderRadius: '50%', backgroundColor: '#ffc107', display: 'inline-block' } }), "Temporarily Unavailable"] })] }));
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { ChatUser, ChatWidgetTheme } from '../../types';
3
+ interface RecentChat {
4
+ id: string;
5
+ user: ChatUser;
6
+ lastMessage: string;
7
+ lastTime: Date;
8
+ unread: number;
9
+ }
10
+ interface RecentChatsScreenProps {
11
+ chats: RecentChat[];
12
+ theme?: ChatWidgetTheme;
13
+ onSelectChat: (user: ChatUser) => void;
14
+ }
15
+ export declare const RecentChatsScreen: React.FC<RecentChatsScreenProps>;
16
+ export {};
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { mergeTheme } from '../../utils/theme';
3
+ export const RecentChatsScreen = ({ chats, theme, onSelectChat }) => {
4
+ const t = mergeTheme(theme);
5
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [_jsxs("div", { style: {
6
+ backgroundColor: t.primaryColor,
7
+ padding: '20px 20px 24px',
8
+ flexShrink: 0,
9
+ }, children: [_jsx("h2", { style: { margin: 0, fontSize: '20px', fontWeight: 800, color: '#fff', fontFamily: t.fontFamily, letterSpacing: '-0.02em' }, children: "Recent Chats" }), _jsx("p", { style: { margin: '4px 0 0', fontSize: '13px', color: 'rgba(255,255,255,0.8)', fontFamily: t.fontFamily }, children: "Your conversation history" })] }), _jsx("div", { style: { flex: 1, overflowY: 'auto' }, children: chats.length === 0 ? (_jsxs("div", { style: { padding: '50px 24px', textAlign: 'center', fontFamily: t.fontFamily }, children: [_jsx("div", { style: { fontSize: '36px', marginBottom: 12 }, children: "\uD83D\uDCAC" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: 6 }, children: "No chats yet" }), _jsx("div", { style: { fontSize: '13px', color: '#7b8fa1' }, children: "Start a conversation from the home tab" })] })) : (chats.map((chat, i) => {
10
+ const initials = chat.user.name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
11
+ const avatarColors = ['#1aaa96', '#2563EB', '#7C3AED', '#D97706', '#DC2626'];
12
+ const bg = avatarColors[chat.user.name.charCodeAt(0) % avatarColors.length];
13
+ const timeStr = new Date(chat.lastTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
14
+ return (_jsxs("button", { onClick: () => onSelectChat(chat.user), style: {
15
+ width: '100%', padding: '14px 20px',
16
+ display: 'flex', alignItems: 'center', gap: '14px',
17
+ background: 'transparent', border: 'none',
18
+ borderBottom: '1px solid #f3f4f6',
19
+ cursor: 'pointer', textAlign: 'left',
20
+ fontFamily: t.fontFamily,
21
+ animation: `cw-fadeUp 0.3s ease both`,
22
+ animationDelay: `${i * 0.05}s`,
23
+ transition: 'background 0.15s',
24
+ }, onMouseEnter: e => e.currentTarget.style.background = '#f8fdfc', onMouseLeave: e => e.currentTarget.style.background = 'transparent', children: [_jsxs("div", { style: {
25
+ width: 46, height: 46, borderRadius: '50%', backgroundColor: bg,
26
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
27
+ color: '#fff', fontWeight: 700, fontSize: '15px', flexShrink: 0,
28
+ position: 'relative',
29
+ }, children: [initials, chat.unread > 0 && (_jsx("span", { style: {
30
+ position: 'absolute', top: -2, right: -2,
31
+ width: 18, height: 18, borderRadius: '50%',
32
+ backgroundColor: '#ff4757', color: '#fff',
33
+ fontSize: '10px', fontWeight: 700,
34
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
35
+ border: '2px solid #fff',
36
+ }, children: chat.unread }))] }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', marginBottom: 3 }, children: [_jsx("span", { style: { fontWeight: 700, fontSize: '14px', color: '#1a2332' }, children: chat.user.name }), _jsx("span", { style: { fontSize: '11px', color: '#b0bec5' }, children: timeStr })] }), _jsx("div", { style: { fontSize: '13px', color: '#7b8fa1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: chat.lastMessage })] })] }, chat.id));
37
+ })) })] }));
38
+ };
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { BottomTab } from '../../types';
3
+ interface BottomTabsProps {
4
+ active: BottomTab;
5
+ onChange: (tab: BottomTab) => void;
6
+ primaryColor: string;
7
+ fontFamily: string;
8
+ }
9
+ export declare const BottomTabs: React.FC<BottomTabsProps>;
10
+ export {};
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export const BottomTabs = ({ active, onChange, primaryColor, fontFamily }) => {
3
+ const tabs = [
4
+ { key: 'home', label: 'Home', Icon: HomeIcon },
5
+ { key: 'chats', label: 'Chats', Icon: ChatsIcon },
6
+ { key: 'tickets', label: 'Tickets', Icon: TicketsIcon },
7
+ ];
8
+ return (_jsx("div", { style: {
9
+ display: 'flex',
10
+ borderTop: '1px solid #eef0f5',
11
+ backgroundColor: '#fff',
12
+ flexShrink: 0,
13
+ }, children: tabs.map(tab => {
14
+ const isActive = active === tab.key;
15
+ return (_jsxs("button", { onClick: () => onChange(tab.key), style: {
16
+ flex: 1, padding: '10px 0 8px',
17
+ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3,
18
+ background: 'transparent', border: 'none', cursor: 'pointer',
19
+ fontFamily, fontSize: '10px', fontWeight: isActive ? 700 : 500,
20
+ color: isActive ? primaryColor : '#9aa3af',
21
+ transition: 'color 0.15s',
22
+ borderTop: isActive ? `2px solid ${primaryColor}` : '2px solid transparent',
23
+ }, children: [_jsx(tab.Icon, { active: isActive, color: isActive ? primaryColor : '#b0bec5' }), tab.label] }, tab.key));
24
+ }) }));
25
+ };
26
+ // ── Icons ─────────────────────────────────────────────────────────────────────
27
+ const HomeIcon = ({ active, color }) => (_jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M3 9.5L12 3l9 6.5V20a1 1 0 01-1 1H4a1 1 0 01-1-1V9.5z", stroke: color, strokeWidth: active ? 2.2 : 1.8, strokeLinecap: "round", strokeLinejoin: "round", fill: active ? `${color}20` : 'none' }), _jsx("path", { d: "M9 21V12h6v9", stroke: color, strokeWidth: active ? 2.2 : 1.8, strokeLinecap: "round", strokeLinejoin: "round" })] }));
28
+ const ChatsIcon = ({ active, color }) => (_jsx("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z", stroke: color, strokeWidth: active ? 2.2 : 1.8, strokeLinecap: "round", strokeLinejoin: "round", fill: active ? `${color}20` : 'none' }) }));
29
+ const TicketsIcon = ({ active, color }) => (_jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M15 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V9l-4-4z", stroke: color, strokeWidth: active ? 2.2 : 1.8, strokeLinecap: "round", strokeLinejoin: "round", fill: active ? `${color}20` : 'none' }), _jsx("path", { d: "M15 5v4h4M9 13h6M9 17h4", stroke: color, strokeWidth: active ? 2.2 : 1.8, strokeLinecap: "round" })] }));
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { ChatWidgetTheme, Ticket } from '../../types';
3
+ interface TicketScreenProps {
4
+ tickets: Ticket[];
5
+ theme?: ChatWidgetTheme;
6
+ onRaiseTicket: (title: string, description: string) => void;
7
+ }
8
+ export declare const TicketScreen: React.FC<TicketScreenProps>;
9
+ export {};
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { mergeTheme } from '../../utils/theme';
4
+ export const TicketScreen = ({ tickets, theme, onRaiseTicket }) => {
5
+ const t = mergeTheme(theme);
6
+ const [showForm, setShowForm] = useState(false);
7
+ const [title, setTitle] = useState('');
8
+ const [desc, setDesc] = useState('');
9
+ const handleSubmit = () => {
10
+ if (!title.trim())
11
+ return;
12
+ onRaiseTicket(title.trim(), desc.trim());
13
+ setTitle('');
14
+ setDesc('');
15
+ setShowForm(false);
16
+ };
17
+ const statusMeta = {
18
+ 'open': { label: 'Open', bg: '#e6faf8', color: t.primaryColor },
19
+ 'in-progress': { label: 'In Progress', bg: '#fffbeb', color: '#d97706' },
20
+ 'resolved': { label: 'Resolved', bg: '#f0fdf4', color: '#16a34a' },
21
+ 'closed': { label: 'Closed', bg: '#f3f4f6', color: '#6b7280' },
22
+ };
23
+ const priorityMeta = {
24
+ low: { label: '↓ Low', color: '#6b7280' },
25
+ medium: { label: '→ Medium', color: '#d97706' },
26
+ high: { label: '↑ High', color: '#dc2626' },
27
+ };
28
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [_jsx("div", { style: { backgroundColor: t.primaryColor, padding: '20px 20px 24px', flexShrink: 0 }, children: _jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs("div", { children: [_jsx("h2", { style: { margin: 0, fontSize: '20px', fontWeight: 800, color: '#fff', fontFamily: t.fontFamily, letterSpacing: '-0.02em' }, children: "Tickets" }), _jsxs("p", { style: { margin: '4px 0 0', fontSize: '13px', color: 'rgba(255,255,255,0.8)', fontFamily: t.fontFamily }, children: [tickets.length, " ticket", tickets.length !== 1 ? 's' : '', " raised"] })] }), _jsx("button", { onClick: () => setShowForm(v => !v), style: {
29
+ background: 'rgba(255,255,255,0.25)', border: 'none', borderRadius: '20px',
30
+ padding: '8px 16px', color: '#fff', fontWeight: 700, fontSize: '13px',
31
+ cursor: 'pointer', fontFamily: t.fontFamily, display: 'flex', alignItems: 'center', gap: 5,
32
+ }, children: showForm ? '✕ Cancel' : '+ New Ticket' })] }) }), showForm && (_jsxs("div", { style: {
33
+ padding: '16px 20px',
34
+ borderBottom: '1px solid #eef0f5',
35
+ backgroundColor: '#fafcff',
36
+ animation: 'cw-fadeUp 0.2s ease',
37
+ flexShrink: 0,
38
+ }, children: [_jsx("input", { placeholder: "Ticket title *", value: title, onChange: e => setTitle(e.target.value), style: {
39
+ width: '100%', padding: '10px 14px', borderRadius: 10,
40
+ border: '1.5px solid #e2e8f0', outline: 'none',
41
+ fontFamily: t.fontFamily, fontSize: '14px', color: '#1a2332',
42
+ marginBottom: 10, boxSizing: 'border-box',
43
+ }, onFocus: e => (e.target.style.borderColor = t.primaryColor), onBlur: e => (e.target.style.borderColor = '#e2e8f0') }), _jsx("textarea", { placeholder: "Describe the issue (optional)", value: desc, onChange: e => setDesc(e.target.value), rows: 3, style: {
44
+ width: '100%', padding: '10px 14px', borderRadius: 10,
45
+ border: '1.5px solid #e2e8f0', outline: 'none', resize: 'none',
46
+ fontFamily: t.fontFamily, fontSize: '14px', color: '#1a2332',
47
+ marginBottom: 10, boxSizing: 'border-box',
48
+ }, onFocus: e => (e.target.style.borderColor = t.primaryColor), onBlur: e => (e.target.style.borderColor = '#e2e8f0') }), _jsx("button", { onClick: handleSubmit, disabled: !title.trim(), style: {
49
+ width: '100%', padding: '11px', borderRadius: 10,
50
+ backgroundColor: title.trim() ? t.primaryColor : '#e2e8f0',
51
+ color: title.trim() ? '#fff' : '#9aa3af',
52
+ border: 'none', cursor: title.trim() ? 'pointer' : 'not-allowed',
53
+ fontWeight: 700, fontSize: '14px', fontFamily: t.fontFamily,
54
+ transition: 'background 0.2s',
55
+ }, children: "Submit Ticket" })] })), _jsx("div", { style: { flex: 1, overflowY: 'auto' }, children: tickets.length === 0 ? (_jsxs("div", { style: { padding: '50px 24px', textAlign: 'center', fontFamily: t.fontFamily }, children: [_jsx("div", { style: { fontSize: '36px', marginBottom: 12 }, children: "\uD83C\uDFAB" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: 6 }, children: "No tickets yet" }), _jsx("div", { style: { fontSize: '13px', color: '#7b8fa1' }, children: "Raise a ticket for major issues or changes" })] })) : (tickets.map((ticket, i) => {
56
+ const sm = statusMeta[ticket.status];
57
+ const pm = priorityMeta[ticket.priority];
58
+ const date = new Date(ticket.createdAt).toLocaleDateString([], { month: 'short', day: 'numeric' });
59
+ return (_jsxs("div", { style: {
60
+ padding: '14px 20px',
61
+ borderBottom: '1px solid #f3f4f6',
62
+ fontFamily: t.fontFamily,
63
+ animation: `cw-fadeUp 0.3s ease both`,
64
+ animationDelay: `${i * 0.05}s`,
65
+ }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: 6 }, children: [_jsx("span", { style: { fontWeight: 700, fontSize: '14px', color: '#1a2332', flex: 1, paddingRight: 12 }, children: ticket.title }), _jsx("span", { style: {
66
+ fontSize: '10px', fontWeight: 700, padding: '3px 9px', borderRadius: 20,
67
+ backgroundColor: sm.bg, color: sm.color, whiteSpace: 'nowrap',
68
+ textTransform: 'uppercase', letterSpacing: '0.04em',
69
+ }, children: sm.label })] }), ticket.description && (_jsx("div", { style: { fontSize: '13px', color: '#7b8fa1', marginBottom: 8, lineHeight: 1.5 }, children: ticket.description })), _jsxs("div", { style: { display: 'flex', gap: 12, fontSize: '11px', color: '#b0bec5' }, children: [_jsx("span", { style: { color: pm.color, fontWeight: 600 }, children: pm.label }), _jsxs("span", { children: ["#", ticket.id.slice(-6).toUpperCase()] }), _jsx("span", { children: date })] })] }, ticket.id));
70
+ })) })] }));
71
+ };
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { ChatUser, ChatWidgetTheme, UserListContext } from '../../types';
3
+ interface UserListScreenProps {
4
+ context: UserListContext;
5
+ users: ChatUser[];
6
+ loading: boolean;
7
+ error: string | null;
8
+ theme?: ChatWidgetTheme;
9
+ onBack: () => void;
10
+ onSelectUser: (user: ChatUser) => void;
11
+ }
12
+ export declare const UserListScreen: React.FC<UserListScreenProps>;
13
+ export {};
@@ -0,0 +1,64 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { mergeTheme } from '../../utils/theme';
3
+ export const UserListScreen = ({ context, users, loading, error, theme, onBack, onSelectUser, }) => {
4
+ const t = mergeTheme(theme);
5
+ const title = context === 'support' ? 'Need Support' : 'New Conversation';
6
+ const subtitle = context === 'support'
7
+ ? 'Choose a support agent'
8
+ : 'Choose a colleague to chat with';
9
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', animation: 'cw-slideInRight 0.25s ease' }, children: [_jsxs("div", { style: {
10
+ backgroundColor: t.primaryColor,
11
+ padding: '16px 20px',
12
+ display: 'flex',
13
+ alignItems: 'center',
14
+ gap: '12px',
15
+ flexShrink: 0,
16
+ }, children: [_jsx("button", { onClick: onBack, style: {
17
+ background: 'rgba(255,255,255,0.2)',
18
+ border: 'none',
19
+ borderRadius: '50%',
20
+ width: '34px',
21
+ height: '34px',
22
+ display: 'flex',
23
+ alignItems: 'center',
24
+ justifyContent: 'center',
25
+ cursor: 'pointer',
26
+ flexShrink: 0,
27
+ }, children: _jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M19 12H5M5 12L12 19M5 12L12 5", stroke: "#fff", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsxs("div", { children: [_jsx("div", { style: { fontWeight: 700, fontSize: '16px', color: '#fff', fontFamily: t.fontFamily }, children: title }), _jsx("div", { style: { fontSize: '12px', color: 'rgba(255,255,255,0.8)', fontFamily: t.fontFamily }, children: subtitle })] })] }), _jsxs("div", { style: { flex: 1, overflowY: 'auto', padding: '8px 0' }, children: [loading && _jsx(UserListSkeleton, {}), error && _jsx(ErrorState, { message: error, color: t.primaryColor, font: t.fontFamily }), !loading && !error && users.length === 0 && (_jsx(EmptyState, { color: t.primaryColor, font: t.fontFamily })), !loading && !error && users.map((user, i) => (_jsx(UserRow, { user: user, index: i, primaryColor: t.primaryColor, fontFamily: t.fontFamily, onClick: () => onSelectUser(user) }, user.uid)))] })] }));
28
+ };
29
+ // ── Sub-components ─────────────────────────────────────────────────────────────
30
+ const UserRow = ({ user, index, primaryColor, fontFamily, onClick }) => {
31
+ const initials = user.name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
32
+ const avatarColors = ['#1aaa96', '#2563EB', '#7C3AED', '#D97706', '#DC2626', '#059669'];
33
+ const bg = avatarColors[user.name.charCodeAt(0) % avatarColors.length];
34
+ return (_jsxs("button", { onClick: onClick, style: {
35
+ width: '100%',
36
+ padding: '14px 20px',
37
+ display: 'flex',
38
+ alignItems: 'center',
39
+ gap: '14px',
40
+ background: 'transparent',
41
+ border: 'none',
42
+ borderBottom: '1px solid #f3f4f6',
43
+ cursor: 'pointer',
44
+ textAlign: 'left',
45
+ fontFamily,
46
+ animation: 'cw-fadeUp 0.3s ease both',
47
+ animationDelay: `${index * 0.05}s`,
48
+ transition: 'background 0.15s',
49
+ }, onMouseEnter: e => e.currentTarget.style.background = '#f8fdfc', onMouseLeave: e => e.currentTarget.style.background = 'transparent', children: [_jsx("div", { style: {
50
+ width: '46px', height: '46px', borderRadius: '50%',
51
+ backgroundColor: bg, flexShrink: 0,
52
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
53
+ color: '#fff', fontWeight: 700, fontSize: '15px',
54
+ }, children: initials }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: { fontWeight: 700, fontSize: '14px', color: '#1a2332', marginBottom: '2px' }, children: user.name }), _jsx("div", { style: { fontSize: '12px', color: '#7b8fa1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: user.project || user.email })] }), _jsx("span", { style: {
55
+ fontSize: '10px', fontWeight: 700, padding: '3px 9px',
56
+ borderRadius: '20px', textTransform: 'uppercase', letterSpacing: '0.05em',
57
+ backgroundColor: user.type === 'developer' ? '#e6faf8' : '#eff6ff',
58
+ color: user.type === 'developer' ? primaryColor : '#2563EB',
59
+ border: `1px solid ${user.type === 'developer' ? primaryColor + '30' : '#2563eb30'}`,
60
+ }, children: user.type === 'developer' ? 'Dev' : 'User' })] }));
61
+ };
62
+ const UserListSkeleton = () => (_jsx(_Fragment, { children: [1, 2, 3, 4].map(i => (_jsxs("div", { style: { padding: '14px 20px', display: 'flex', gap: '14px', alignItems: 'center' }, children: [_jsx("div", { style: { width: 46, height: 46, borderRadius: '50%', background: '#f0f0f0', flexShrink: 0 } }), _jsxs("div", { style: { flex: 1 }, children: [_jsx("div", { style: { height: 13, width: '55%', background: '#f0f0f0', borderRadius: 6, marginBottom: 7 } }), _jsx("div", { style: { height: 11, width: '38%', background: '#f0f0f0', borderRadius: 6 } })] })] }, i))) }));
63
+ const ErrorState = ({ message, color, font }) => (_jsxs("div", { style: { padding: '40px 24px', textAlign: 'center', fontFamily: font }, children: [_jsx("div", { style: { fontSize: '32px', marginBottom: '10px' }, children: "\u26A0\uFE0F" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: '6px' }, children: "Could not load users" }), _jsx("div", { style: { fontSize: '13px', color: '#7b8fa1' }, children: message })] }));
64
+ const EmptyState = ({ color, font }) => (_jsxs("div", { style: { padding: '40px 24px', textAlign: 'center', fontFamily: font }, children: [_jsx("div", { style: { fontSize: '32px', marginBottom: '10px' }, children: "\uD83D\uDC65" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: '6px' }, children: "No users available" }), _jsx("div", { style: { fontSize: '13px', color: '#7b8fa1' }, children: "Check back later" })] }));