vg-x07df 0.1.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.
Files changed (140) hide show
  1. package/.azure-pipelines/publish-public.yml +37 -0
  2. package/.azure-pipelines/publish.yml +39 -0
  3. package/.changeset/README.md +8 -0
  4. package/.changeset/config.json +11 -0
  5. package/AUTO_JOIN_GUIDE.md +411 -0
  6. package/README.md +215 -0
  7. package/Screenshot 2025-09-24 at 14.34.48.png +0 -0
  8. package/Screenshot 2025-10-04 at 12.58.54.png +0 -0
  9. package/biome.json +48 -0
  10. package/examples/demo/.env.example +19 -0
  11. package/examples/demo/CHANGELOG.md +22 -0
  12. package/examples/demo/README.md +72 -0
  13. package/examples/demo/eslint.config.js +23 -0
  14. package/examples/demo/index.html +13 -0
  15. package/examples/demo/package.json +34 -0
  16. package/examples/demo/pnpm-lock.yaml +2098 -0
  17. package/examples/demo/pnpm-workspace.yaml +1 -0
  18. package/examples/demo/public/vite.svg +1 -0
  19. package/examples/demo/src/App.css +52 -0
  20. package/examples/demo/src/App.tsx +176 -0
  21. package/examples/demo/src/assets/react.svg +1 -0
  22. package/examples/demo/src/components/auth/LoginForm.css +144 -0
  23. package/examples/demo/src/components/auth/LoginForm.tsx +80 -0
  24. package/examples/demo/src/components/calling/AutoJoinSettings.tsx +213 -0
  25. package/examples/demo/src/components/calling/AutoJoinStatus.tsx +72 -0
  26. package/examples/demo/src/components/calling/CallInitiator.css +258 -0
  27. package/examples/demo/src/components/calling/CallInitiator.tsx +142 -0
  28. package/examples/demo/src/components/calling/CallNotifications.css +119 -0
  29. package/examples/demo/src/components/calling/CallNotifications.tsx +108 -0
  30. package/examples/demo/src/components/calling/IncomingCallModal.css +192 -0
  31. package/examples/demo/src/components/calling/IncomingCallModal.tsx +78 -0
  32. package/examples/demo/src/components/calling/MinimizedCall.css +156 -0
  33. package/examples/demo/src/components/calling/MinimizedCall.tsx +78 -0
  34. package/examples/demo/src/components/conference/ConferenceHeader.css +265 -0
  35. package/examples/demo/src/components/conference/ConferenceHeader.tsx +78 -0
  36. package/examples/demo/src/components/conference/EnhancedControlBar.css +356 -0
  37. package/examples/demo/src/components/conference/EnhancedControlBar.tsx +262 -0
  38. package/examples/demo/src/components/conference/PaginationControls.css +67 -0
  39. package/examples/demo/src/components/conference/PaginationControls.tsx +64 -0
  40. package/examples/demo/src/components/conference/ParticipantGrid.css +153 -0
  41. package/examples/demo/src/components/conference/ParticipantGrid.tsx +87 -0
  42. package/examples/demo/src/components/conference/ParticipantTile.css +210 -0
  43. package/examples/demo/src/components/conference/ParticipantTile.tsx +114 -0
  44. package/examples/demo/src/components/conference/VideoConference.css +214 -0
  45. package/examples/demo/src/components/conference/VideoConference.tsx +93 -0
  46. package/examples/demo/src/contexts/AuthContext.tsx +105 -0
  47. package/examples/demo/src/hooks/useAuth.ts +5 -0
  48. package/examples/demo/src/hooks/useCallTimer.ts +42 -0
  49. package/examples/demo/src/index.css +68 -0
  50. package/examples/demo/src/main.tsx +10 -0
  51. package/examples/demo/src/services/auth.service.ts +153 -0
  52. package/examples/demo/src/types/auth.types.ts +31 -0
  53. package/examples/demo/tsconfig.app.json +28 -0
  54. package/examples/demo/tsconfig.json +7 -0
  55. package/examples/demo/tsconfig.node.json +26 -0
  56. package/examples/demo/vite.config.ts +15 -0
  57. package/images/callpad-without-ai.png +0 -0
  58. package/package.json +28 -0
  59. package/packages/sdk/CHANGELOG.md +33 -0
  60. package/packages/sdk/LICENSE +21 -0
  61. package/packages/sdk/README.md +97 -0
  62. package/packages/sdk/documentation.md +1132 -0
  63. package/packages/sdk/openapi-ts.config.ts +7 -0
  64. package/packages/sdk/package.json +88 -0
  65. package/packages/sdk/src/core/auth.manager.ts +52 -0
  66. package/packages/sdk/src/core/events/event-bus.ts +301 -0
  67. package/packages/sdk/src/core/events/index.ts +8 -0
  68. package/packages/sdk/src/core/events/types.ts +165 -0
  69. package/packages/sdk/src/core/index.ts +3 -0
  70. package/packages/sdk/src/core/signal/api.config.ts +49 -0
  71. package/packages/sdk/src/core/signal/index.ts +16 -0
  72. package/packages/sdk/src/core/signal/signal.client.ts +101 -0
  73. package/packages/sdk/src/core/signal/types.ts +110 -0
  74. package/packages/sdk/src/core/socketio/handlers/base.handler.ts +212 -0
  75. package/packages/sdk/src/core/socketio/handlers/call-accepted.handler.ts +34 -0
  76. package/packages/sdk/src/core/socketio/handlers/call-canceled.handler.ts +34 -0
  77. package/packages/sdk/src/core/socketio/handlers/call-declined.handler.ts +29 -0
  78. package/packages/sdk/src/core/socketio/handlers/call-ended.handler.ts +40 -0
  79. package/packages/sdk/src/core/socketio/handlers/call-incoming.handler.ts +72 -0
  80. package/packages/sdk/src/core/socketio/handlers/call-join-info.handler.ts +181 -0
  81. package/packages/sdk/src/core/socketio/handlers/call-participant-joined.handler.ts +42 -0
  82. package/packages/sdk/src/core/socketio/handlers/call-participant-joining.handler.ts +42 -0
  83. package/packages/sdk/src/core/socketio/handlers/call-timeout.handler.ts +31 -0
  84. package/packages/sdk/src/core/socketio/handlers/handler.registry.ts +62 -0
  85. package/packages/sdk/src/core/socketio/handlers/index.ts +21 -0
  86. package/packages/sdk/src/core/socketio/handlers/participant-left.handler.ts +37 -0
  87. package/packages/sdk/src/core/socketio/handlers/schema.ts +130 -0
  88. package/packages/sdk/src/core/socketio/index.ts +5 -0
  89. package/packages/sdk/src/core/socketio/socket.manager.ts +187 -0
  90. package/packages/sdk/src/core/socketio/types.ts +14 -0
  91. package/packages/sdk/src/core/types.ts +23 -0
  92. package/packages/sdk/src/generated/api/core/ApiError.ts +21 -0
  93. package/packages/sdk/src/generated/api/core/ApiRequestOptions.ts +13 -0
  94. package/packages/sdk/src/generated/api/core/ApiResult.ts +7 -0
  95. package/packages/sdk/src/generated/api/core/CancelablePromise.ts +126 -0
  96. package/packages/sdk/src/generated/api/core/OpenAPI.ts +55 -0
  97. package/packages/sdk/src/generated/api/core/request.ts +339 -0
  98. package/packages/sdk/src/generated/api/index.ts +5 -0
  99. package/packages/sdk/src/generated/api/models.ts +219 -0
  100. package/packages/sdk/src/generated/api/services.ts +225 -0
  101. package/packages/sdk/src/hooks/index.ts +21 -0
  102. package/packages/sdk/src/hooks/useAutoJoin.ts +66 -0
  103. package/packages/sdk/src/hooks/useCallActions.ts +28 -0
  104. package/packages/sdk/src/hooks/useCallQuality.ts +416 -0
  105. package/packages/sdk/src/hooks/useCallState.ts +23 -0
  106. package/packages/sdk/src/hooks/useConnection.ts +15 -0
  107. package/packages/sdk/src/hooks/useDevices.ts +296 -0
  108. package/packages/sdk/src/hooks/useErrorRecovery.ts +299 -0
  109. package/packages/sdk/src/hooks/useErrors.ts +84 -0
  110. package/packages/sdk/src/hooks/useEvent.ts +188 -0
  111. package/packages/sdk/src/hooks/useMediaControls.ts +215 -0
  112. package/packages/sdk/src/hooks/useParticipantStatus.ts +318 -0
  113. package/packages/sdk/src/hooks/useParticipants.ts +111 -0
  114. package/packages/sdk/src/index.ts +66 -0
  115. package/packages/sdk/src/livekit/constants.ts +76 -0
  116. package/packages/sdk/src/livekit/device.manager.ts +172 -0
  117. package/packages/sdk/src/livekit/error-classifier.ts +155 -0
  118. package/packages/sdk/src/livekit/events/eventBridge.ts +371 -0
  119. package/packages/sdk/src/livekit/events/trackRegistry.ts +114 -0
  120. package/packages/sdk/src/livekit/index.ts +49 -0
  121. package/packages/sdk/src/livekit/livekit.service.ts +110 -0
  122. package/packages/sdk/src/livekit/media.controls.ts +315 -0
  123. package/packages/sdk/src/livekit/room.manager.ts +79 -0
  124. package/packages/sdk/src/livekit/track.utils.ts +230 -0
  125. package/packages/sdk/src/livekit/types.ts +135 -0
  126. package/packages/sdk/src/provider/RtcProvider.tsx +78 -0
  127. package/packages/sdk/src/services/call-actions.ts +260 -0
  128. package/packages/sdk/src/services/error-recovery.ts +461 -0
  129. package/packages/sdk/src/services/index.ts +2 -0
  130. package/packages/sdk/src/services/sdk-builder.ts +104 -0
  131. package/packages/sdk/src/state/errors.ts +163 -0
  132. package/packages/sdk/src/state/selectors.ts +28 -0
  133. package/packages/sdk/src/state/store.ts +36 -0
  134. package/packages/sdk/src/state/types.ts +151 -0
  135. package/packages/sdk/src/utils/logger.ts +183 -0
  136. package/packages/sdk/tsconfig.json +49 -0
  137. package/packages/sdk/tsup.config.ts +51 -0
  138. package/pnpm-workspace.yaml +4 -0
  139. package/tsconfig.base.json +19 -0
  140. package/turbo.json +34 -0
@@ -0,0 +1 @@
1
+ packages: []
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
@@ -0,0 +1,52 @@
1
+ /* Global styles */
2
+ * {
3
+ margin: 0;
4
+ padding: 0;
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ #root {
9
+ width: 100vw;
10
+ height: 100vh;
11
+ overflow: hidden;
12
+ }
13
+
14
+ .app {
15
+ width: 100%;
16
+ height: 100%;
17
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
18
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
19
+ sans-serif;
20
+ -webkit-font-smoothing: antialiased;
21
+ -moz-osx-font-smoothing: grayscale;
22
+ }
23
+
24
+ .app-loading {
25
+ display: flex;
26
+ flex-direction: column;
27
+ align-items: center;
28
+ justify-content: center;
29
+ height: 100vh;
30
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
31
+ color: white;
32
+ }
33
+
34
+ .loading-spinner {
35
+ width: 40px;
36
+ height: 40px;
37
+ border: 4px solid rgba(255, 255, 255, 0.3);
38
+ border-top: 4px solid white;
39
+ border-radius: 50%;
40
+ animation: spin 1s linear infinite;
41
+ margin-bottom: 16px;
42
+ }
43
+
44
+ @keyframes spin {
45
+ 0% { transform: rotate(0deg); }
46
+ 100% { transform: rotate(360deg); }
47
+ }
48
+
49
+ .app-loading p {
50
+ font-size: 16px;
51
+ font-weight: 500;
52
+ }
@@ -0,0 +1,176 @@
1
+ import './App.css'
2
+ import React, { useState } from 'react'
3
+ import { RtcProvider } from 'vg-x07df'
4
+ import { AuthProvider } from './contexts/AuthContext'
5
+ import { useAuth } from './hooks/useAuth'
6
+ import { AuthService } from './services/auth.service'
7
+ import { LoginForm } from './components/auth/LoginForm'
8
+ import { CallInitiator } from './components/calling/CallInitiator'
9
+ import { VideoConference } from './components/conference/VideoConference'
10
+ import { IncomingCallModal } from './components/calling/IncomingCallModal'
11
+ import { CallNotifications } from './components/calling/CallNotifications'
12
+ import { MinimizedCall } from './components/calling/MinimizedCall'
13
+ import { AutoJoinStatus } from './components/calling/AutoJoinStatus'
14
+ import { AutoJoinSettings } from './components/calling/AutoJoinSettings'
15
+ import { useCallState, useSdk } from 'vg-x07df'
16
+
17
+ // Debug environment variables
18
+ console.log('🔍 Environment Debug:');
19
+ console.log(' VITE_SIGNAL_HOST:', import.meta.env.VITE_SIGNAL_HOST);
20
+ console.log(' VITE_LIVEKIT_URL:', import.meta.env.VITE_LIVEKIT_URL);
21
+ console.log(' VITE_DEBUG:', import.meta.env.VITE_DEBUG);
22
+
23
+ const signalHost = import.meta.env.VITE_SIGNAL_HOST || 'http://localhost:3001';
24
+ console.log('🚀 Final signalHost:', signalHost);
25
+
26
+ const rtcOptions = {
27
+ appId: 'callpad-demo',
28
+ signalHost,
29
+ livekitUrl: import.meta.env.VITE_LIVEKIT_URL || 'ws://localhost:7880',
30
+ authProvider: () => AuthService.getToken(),
31
+ // Use new logging system
32
+ logLevel: import.meta.env.VITE_DEBUG ? 'debug' as const : 'info' as const,
33
+ enableDebug: !!import.meta.env.VITE_DEBUG,
34
+ // Auto-join configuration - industry standard defaults
35
+ autoJoin: {
36
+ caller: {
37
+ enabled: true,
38
+ trigger: 'first-accept' as const,
39
+ },
40
+ callee: {
41
+ enabled: true,
42
+ trigger: 'immediate' as const,
43
+ },
44
+ fallback: {
45
+ onFailure: 'manual' as const,
46
+ retryAttempts: 2,
47
+ },
48
+ },
49
+ }
50
+
51
+ // Component to initialize app state with user ID
52
+ function AppInitializer() {
53
+ const sdk = useSdk()
54
+ const { isAuthenticated } = useAuth()
55
+
56
+ React.useEffect(() => {
57
+ if (isAuthenticated) {
58
+ // User identity is now automatically managed by the SDK
59
+ // via auth token + call action context - no manual setup needed
60
+ const user = AuthService.getUser()
61
+ if (user?.id) {
62
+ console.log('🆔 Authenticated user:', user.id)
63
+ }
64
+ }
65
+ }, [sdk, isAuthenticated])
66
+
67
+ return null
68
+ }
69
+
70
+ function AppContent() {
71
+ const { status } = useCallState()
72
+ const [isCallMinimized, setIsCallMinimized] = useState(false)
73
+ const [isSettingsOpen, setIsSettingsOpen] = useState(false)
74
+
75
+ const handleCallInitiated = () => {
76
+ // The call will automatically progress through states via the SDK
77
+ console.log('Call initiated successfully')
78
+ }
79
+
80
+ const handleLeaveCall = () => {
81
+ // Return to call initiator after leaving
82
+ setIsCallMinimized(false)
83
+ console.log('Left call, returning to initiator')
84
+ }
85
+
86
+ const handleMinimizeCall = () => {
87
+ setIsCallMinimized(true)
88
+ }
89
+
90
+ const handleRestoreCall = () => {
91
+ setIsCallMinimized(false)
92
+ }
93
+
94
+ const inCall = status !== 'IDLE' && status !== 'ENDED'
95
+
96
+ return (
97
+ <>
98
+ {/* Settings button - fixed position top-left */}
99
+ <button
100
+ onClick={() => setIsSettingsOpen(true)}
101
+ className="fixed top-4 left-4 z-40 bg-gray-800 text-white p-2 rounded-lg hover:bg-gray-700 transition-colors"
102
+ title="Auto-join Settings"
103
+ >
104
+ ⚙️
105
+ </button>
106
+
107
+ {/* Show video conference when call is active/connecting and not minimized */}
108
+ {inCall && !isCallMinimized && (
109
+ <VideoConference
110
+ onLeaveCall={handleLeaveCall}
111
+ onMinimize={handleMinimizeCall}
112
+ />
113
+ )}
114
+
115
+ {/* Show call initiator when idle/ended or when call is minimized */}
116
+ {(!inCall || isCallMinimized) && (
117
+ <CallInitiator onCallInitiated={handleCallInitiated} />
118
+ )}
119
+
120
+ {/* Show minimized call widget when call is active and minimized */}
121
+ {inCall && isCallMinimized && (
122
+ <MinimizedCall
123
+ onRestore={handleRestoreCall}
124
+ onLeaveCall={handleLeaveCall}
125
+ />
126
+ )}
127
+
128
+ {/* Auto-join status indicator */}
129
+ <AutoJoinStatus />
130
+
131
+ {/* Auto-join settings modal */}
132
+ <AutoJoinSettings
133
+ isOpen={isSettingsOpen}
134
+ onClose={() => setIsSettingsOpen(false)}
135
+ />
136
+ </>
137
+ )
138
+ }
139
+
140
+ function AuthenticatedApp() {
141
+ const { isAuthenticated, loading } = useAuth()
142
+
143
+ if (loading) {
144
+ return (
145
+ <div className="app-loading">
146
+ <div className="loading-spinner"></div>
147
+ <p>Loading...</p>
148
+ </div>
149
+ )
150
+ }
151
+
152
+ if (!isAuthenticated) {
153
+ return <LoginForm />
154
+ }
155
+
156
+ return (
157
+ <RtcProvider options={rtcOptions}>
158
+ <AppInitializer />
159
+ <div className="app">
160
+ <AppContent />
161
+ <IncomingCallModal />
162
+ <CallNotifications />
163
+ </div>
164
+ </RtcProvider>
165
+ )
166
+ }
167
+
168
+ function App() {
169
+ return (
170
+ <AuthProvider>
171
+ <AuthenticatedApp />
172
+ </AuthProvider>
173
+ )
174
+ }
175
+
176
+ export default App
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
@@ -0,0 +1,144 @@
1
+ .login-container {
2
+ min-height: 100vh;
3
+ display: flex;
4
+ align-items: center;
5
+ justify-content: center;
6
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
7
+ padding: 20px;
8
+ }
9
+
10
+ .login-card {
11
+ background: white;
12
+ border-radius: 12px;
13
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
14
+ padding: 40px;
15
+ width: 100%;
16
+ max-width: 400px;
17
+ }
18
+
19
+ .login-header {
20
+ text-align: center;
21
+ margin-bottom: 32px;
22
+ }
23
+
24
+ .login-header h1 {
25
+ margin: 0 0 8px 0;
26
+ font-size: 28px;
27
+ font-weight: 700;
28
+ color: #1a202c;
29
+ }
30
+
31
+ .login-header p {
32
+ margin: 0;
33
+ color: #718096;
34
+ font-size: 16px;
35
+ }
36
+
37
+ .login-form {
38
+ display: flex;
39
+ flex-direction: column;
40
+ gap: 20px;
41
+ }
42
+
43
+ .form-group {
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: 6px;
47
+ }
48
+
49
+ .form-group label {
50
+ font-weight: 500;
51
+ color: #374151;
52
+ font-size: 14px;
53
+ }
54
+
55
+ .form-group input {
56
+ padding: 12px 16px;
57
+ border: 1px solid #d1d5db;
58
+ border-radius: 8px;
59
+ font-size: 16px;
60
+ transition: border-color 0.2s, box-shadow 0.2s;
61
+ }
62
+
63
+ .form-group input:focus {
64
+ outline: none;
65
+ border-color: #667eea;
66
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
67
+ }
68
+
69
+ .form-group input:disabled {
70
+ background-color: #f9fafb;
71
+ color: #9ca3af;
72
+ cursor: not-allowed;
73
+ }
74
+
75
+ .error-message {
76
+ background-color: #fef2f2;
77
+ border: 1px solid #fecaca;
78
+ color: #dc2626;
79
+ padding: 12px 16px;
80
+ border-radius: 8px;
81
+ font-size: 14px;
82
+ text-align: center;
83
+ }
84
+
85
+ .login-button {
86
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
87
+ color: white;
88
+ border: none;
89
+ padding: 14px 20px;
90
+ border-radius: 8px;
91
+ font-size: 16px;
92
+ font-weight: 600;
93
+ cursor: pointer;
94
+ transition: transform 0.2s, box-shadow 0.2s;
95
+ margin-top: 8px;
96
+ }
97
+
98
+ .login-button:hover:not(:disabled) {
99
+ transform: translateY(-1px);
100
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
101
+ }
102
+
103
+ .login-button:disabled {
104
+ opacity: 0.6;
105
+ cursor: not-allowed;
106
+ transform: none;
107
+ box-shadow: none;
108
+ }
109
+
110
+ .login-footer {
111
+ margin-top: 32px;
112
+ padding-top: 24px;
113
+ border-top: 1px solid #e5e7eb;
114
+ text-align: center;
115
+ }
116
+
117
+ .login-footer p {
118
+ margin: 0 0 12px 0;
119
+ color: #6b7280;
120
+ font-size: 14px;
121
+ }
122
+
123
+ .demo-credentials {
124
+ background-color: #f8fafc;
125
+ border: 1px solid #e2e8f0;
126
+ border-radius: 6px;
127
+ padding: 12px;
128
+ }
129
+
130
+ .demo-credentials small {
131
+ color: #475569;
132
+ line-height: 1.5;
133
+ }
134
+
135
+ @media (max-width: 480px) {
136
+ .login-card {
137
+ padding: 24px;
138
+ margin: 10px;
139
+ }
140
+
141
+ .login-header h1 {
142
+ font-size: 24px;
143
+ }
144
+ }
@@ -0,0 +1,80 @@
1
+ import { useState } from 'react';
2
+ import { useAuth } from '../../hooks/useAuth';
3
+ import './LoginForm.css';
4
+
5
+ export function LoginForm() {
6
+ const [email, setEmail] = useState('');
7
+ const [password, setPassword] = useState('');
8
+ const { login, loading, error, clearError } = useAuth();
9
+
10
+ const handleSubmit = async (e: React.FormEvent) => {
11
+ e.preventDefault();
12
+ if (!email || !password) return;
13
+
14
+ clearError();
15
+ await login({ email, password });
16
+ };
17
+
18
+ return (
19
+ <div className="login-container">
20
+ <div className="login-card">
21
+ <div className="login-header">
22
+ <h1>CallPad Demo</h1>
23
+ <p>Sign in to start making calls</p>
24
+ </div>
25
+
26
+ <form onSubmit={handleSubmit} className="login-form">
27
+ <div className="form-group">
28
+ <label htmlFor="email">Email</label>
29
+ <input
30
+ id="email"
31
+ type="email"
32
+ value={email}
33
+ onChange={(e) => setEmail(e.target.value)}
34
+ placeholder="Enter your email"
35
+ required
36
+ disabled={loading}
37
+ />
38
+ </div>
39
+
40
+ <div className="form-group">
41
+ <label htmlFor="password">Password</label>
42
+ <input
43
+ id="password"
44
+ type="password"
45
+ value={password}
46
+ onChange={(e) => setPassword(e.target.value)}
47
+ placeholder="Enter your password"
48
+ required
49
+ disabled={loading}
50
+ />
51
+ </div>
52
+
53
+ {error && (
54
+ <div className="error-message">
55
+ {error}
56
+ </div>
57
+ )}
58
+
59
+ <button
60
+ type="submit"
61
+ className="login-button"
62
+ disabled={loading || !email || !password}
63
+ >
64
+ {loading ? 'Signing in...' : 'Sign In'}
65
+ </button>
66
+ </form>
67
+
68
+ <div className="login-footer">
69
+ <p>Demo credentials for testing:</p>
70
+ <div className="demo-credentials">
71
+ <small>
72
+ <strong>Email:</strong> samuel.mashok@voyatekgroup.com<br/>
73
+ <strong>Password:</strong> Holi$day1
74
+ </small>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ );
80
+ }
@@ -0,0 +1,213 @@
1
+ import { useState } from 'react'
2
+ import { useAutoJoin } from 'vg-x07df'
3
+
4
+ interface AutoJoinSettingsProps {
5
+ isOpen: boolean
6
+ onClose: () => void
7
+ }
8
+
9
+ export function AutoJoinSettings({ isOpen, onClose }: AutoJoinSettingsProps) {
10
+ const autoJoin = useAutoJoin()
11
+ const [localConfig, setLocalConfig] = useState(autoJoin.config)
12
+
13
+ if (!isOpen) return null
14
+
15
+ const handleSave = () => {
16
+ // Note: In a real app, you'd need to recreate the RtcProvider with new config
17
+ // For demo purposes, we'll just close the modal
18
+ console.log('Auto-join configuration would be saved:', localConfig)
19
+ onClose()
20
+ }
21
+
22
+ return (
23
+ <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
24
+ <div className="bg-white rounded-lg p-6 max-w-md w-full mx-4">
25
+ <div className="flex justify-between items-center mb-4">
26
+ <h2 className="text-xl font-semibold text-gray-900">Auto-Join Settings</h2>
27
+ <button
28
+ onClick={onClose}
29
+ className="text-gray-500 hover:text-gray-700"
30
+ >
31
+
32
+ </button>
33
+ </div>
34
+
35
+ <div className="space-y-6">
36
+ {/* Caller Settings */}
37
+ <div>
38
+ <h3 className="text-lg font-medium text-gray-900 mb-3">When You Call Others</h3>
39
+ <div className="space-y-3">
40
+ <label className="flex items-center">
41
+ <input
42
+ type="checkbox"
43
+ checked={localConfig.caller.enabled}
44
+ onChange={(e) => setLocalConfig(prev => ({
45
+ ...prev,
46
+ caller: { ...prev.caller, enabled: e.target.checked }
47
+ }))}
48
+ className="mr-3 h-4 w-4 text-blue-600"
49
+ />
50
+ <span className="text-sm text-gray-700">Auto-join calls when someone accepts</span>
51
+ </label>
52
+
53
+ {localConfig.caller.enabled && (
54
+ <div className="ml-7 space-y-2">
55
+ <label className="flex items-center">
56
+ <input
57
+ type="radio"
58
+ name="callerTrigger"
59
+ checked={localConfig.caller.trigger === 'first-accept'}
60
+ onChange={() => setLocalConfig(prev => ({
61
+ ...prev,
62
+ caller: { ...prev.caller, trigger: 'first-accept' }
63
+ }))}
64
+ className="mr-2 h-3 w-3 text-blue-600"
65
+ />
66
+ <span className="text-xs text-gray-600">Join when first person accepts</span>
67
+ </label>
68
+ <label className="flex items-center">
69
+ <input
70
+ type="radio"
71
+ name="callerTrigger"
72
+ checked={localConfig.caller.trigger === 'manual'}
73
+ onChange={() => setLocalConfig(prev => ({
74
+ ...prev,
75
+ caller: { ...prev.caller, trigger: 'manual' }
76
+ }))}
77
+ className="mr-2 h-3 w-3 text-blue-600"
78
+ />
79
+ <span className="text-xs text-gray-600">Always join manually</span>
80
+ </label>
81
+ </div>
82
+ )}
83
+ </div>
84
+ </div>
85
+
86
+ {/* Callee Settings */}
87
+ <div>
88
+ <h3 className="text-lg font-medium text-gray-900 mb-3">When Others Call You</h3>
89
+ <div className="space-y-3">
90
+ <label className="flex items-center">
91
+ <input
92
+ type="checkbox"
93
+ checked={localConfig.callee.enabled}
94
+ onChange={(e) => setLocalConfig(prev => ({
95
+ ...prev,
96
+ callee: { ...prev.callee, enabled: e.target.checked }
97
+ }))}
98
+ className="mr-3 h-4 w-4 text-blue-600"
99
+ />
100
+ <span className="text-sm text-gray-700">Auto-join after accepting call</span>
101
+ </label>
102
+
103
+ {localConfig.callee.enabled && (
104
+ <div className="ml-7 space-y-2">
105
+ <label className="flex items-center">
106
+ <input
107
+ type="radio"
108
+ name="calleeTrigger"
109
+ checked={localConfig.callee.trigger === 'immediate'}
110
+ onChange={() => setLocalConfig(prev => ({
111
+ ...prev,
112
+ callee: { ...prev.callee, trigger: 'immediate' }
113
+ }))}
114
+ className="mr-2 h-3 w-3 text-blue-600"
115
+ />
116
+ <span className="text-xs text-gray-600">Join immediately after accepting</span>
117
+ </label>
118
+ <label className="flex items-center">
119
+ <input
120
+ type="radio"
121
+ name="calleeTrigger"
122
+ checked={localConfig.callee.trigger === 'manual'}
123
+ onChange={() => setLocalConfig(prev => ({
124
+ ...prev,
125
+ callee: { ...prev.callee, trigger: 'manual' }
126
+ }))}
127
+ className="mr-2 h-3 w-3 text-blue-600"
128
+ />
129
+ <span className="text-xs text-gray-600">Always join manually</span>
130
+ </label>
131
+ </div>
132
+ )}
133
+ </div>
134
+ </div>
135
+
136
+ {/* Fallback Settings */}
137
+ <div>
138
+ <h3 className="text-lg font-medium text-gray-900 mb-3">If Auto-Join Fails</h3>
139
+ <div className="space-y-3">
140
+ <label className="flex items-center">
141
+ <input
142
+ type="radio"
143
+ name="fallback"
144
+ checked={localConfig.fallback.onFailure === 'retry'}
145
+ onChange={() => setLocalConfig(prev => ({
146
+ ...prev,
147
+ fallback: { ...prev.fallback, onFailure: 'retry' }
148
+ }))}
149
+ className="mr-3 h-4 w-4 text-blue-600"
150
+ />
151
+ <span className="text-sm text-gray-700">Retry automatically</span>
152
+ </label>
153
+ <label className="flex items-center">
154
+ <input
155
+ type="radio"
156
+ name="fallback"
157
+ checked={localConfig.fallback.onFailure === 'manual'}
158
+ onChange={() => setLocalConfig(prev => ({
159
+ ...prev,
160
+ fallback: { ...prev.fallback, onFailure: 'manual' }
161
+ }))}
162
+ className="mr-3 h-4 w-4 text-blue-600"
163
+ />
164
+ <span className="text-sm text-gray-700">Show manual join button</span>
165
+ </label>
166
+
167
+ {localConfig.fallback.onFailure === 'retry' && (
168
+ <div className="ml-7">
169
+ <label className="flex items-center space-x-2">
170
+ <span className="text-xs text-gray-600">Max retries:</span>
171
+ <select
172
+ value={localConfig.fallback.retryAttempts}
173
+ onChange={(e) => setLocalConfig(prev => ({
174
+ ...prev,
175
+ fallback: { ...prev.fallback, retryAttempts: parseInt(e.target.value) }
176
+ }))}
177
+ className="text-xs border rounded px-2 py-1"
178
+ >
179
+ <option value="1">1</option>
180
+ <option value="2">2</option>
181
+ <option value="3">3</option>
182
+ <option value="5">5</option>
183
+ </select>
184
+ </label>
185
+ </div>
186
+ )}
187
+ </div>
188
+ </div>
189
+ </div>
190
+
191
+ <div className="flex justify-end space-x-3 mt-6">
192
+ <button
193
+ onClick={onClose}
194
+ className="px-4 py-2 text-gray-600 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
195
+ >
196
+ Cancel
197
+ </button>
198
+ <button
199
+ onClick={handleSave}
200
+ className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
201
+ >
202
+ Save Settings
203
+ </button>
204
+ </div>
205
+
206
+ <div className="mt-4 p-3 bg-yellow-50 border border-yellow-200 rounded text-xs text-yellow-800">
207
+ <strong>Note:</strong> In this demo, settings changes require a page refresh to take effect.
208
+ In a production app, you would implement dynamic configuration updates.
209
+ </div>
210
+ </div>
211
+ </div>
212
+ )
213
+ }