@dora-cell/sdk-react 1.0.0 → 1.0.2

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/README.md CHANGED
@@ -6,8 +6,9 @@ React hooks and components for the Dora Cell VoIP SDK.
6
6
 
7
7
  ✅ **React Hooks** - Easy-to-use hooks for call management
8
8
  ✅ **Context Provider** - Simple setup with `DoraCellProvider`
9
- ✅ **Pre-built UI** - Includes `Dialpad` and `CallInterface` components
10
- ✅ **Auto state management** - Automatic call state and connection tracking
9
+ ✅ **Pre-built UI** - Includes `Dialpad`, `CallInterface`, and `CreditBalance`
10
+ ✅ **Auto state management** - Automatic call state and connection tracking
11
+ ✅ **DID Management** - Hooks to fetch and switch between caller IDs
11
12
 
12
13
  ## Installation
13
14
 
@@ -34,10 +35,11 @@ function App() {
34
35
  <DoraCellProvider
35
36
  config={{
36
37
  auth: {
37
- type: "extension",
38
- extension: "1001",
38
+ type: "api-token",
39
+ publicKey: "pk_...",
40
+ secretKey: "sk_...",
39
41
  },
40
- debug: true,
42
+ environment: "production",
41
43
  }}
42
44
  autoInitialize={true}
43
45
  >
@@ -99,17 +101,21 @@ function Dashboard() {
99
101
 
100
102
  ### `Dialpad`
101
103
 
102
- A flexible keypad for entering numbers and initiating calls.
104
+ A flexible keypad for entering numbers and initiating calls. It automatically fetches available Caller IDs (extensions) and allows switching between them.
105
+
106
+ ### `CreditBalance`
107
+
108
+ A standalone component that displays the user's current credit balance and currency. It automatically refreshes whenever the SDK initializes and whenever a call ends.
103
109
 
104
110
  ```tsx
105
- import { Dialpad } from "@dora-cell/sdk-react";
111
+ import { CreditBalance } from "@dora-cell/sdk-react";
106
112
 
107
- function DialerModal() {
113
+ function Header() {
108
114
  return (
109
- <Dialpad
110
- showKeys={true}
111
- onCallInitiated={(number) => console.log("Placing call to:", number)}
112
- />
115
+ <header>
116
+ <h1>My App</h1>
117
+ <CreditBalance />
118
+ </header>
113
119
  );
114
120
  }
115
121
  ```
@@ -134,8 +140,26 @@ Returns the SIP registration state.
134
140
 
135
141
  - `isConnected`: `true` when registered and ready.
136
142
  - `connectionStatus`: `'disconnected' | 'connecting' | 'connected' | 'registered' | 'registrationFailed'`.
143
+ - `extension`: The currently registered extension number.
137
144
  - `error`: Any connection error object.
138
145
 
146
+ ### `useWallet()`
147
+
148
+ Returns the user's credit balance.
149
+
150
+ - `balance`: Numerical balance.
151
+ - `currency`: Currency code (e.g., `"NGN"`).
152
+ - `isLoading`: Boolean loading state.
153
+ - `refresh()`: Fetch the latest balance.
154
+
155
+ ### `useExtensions()`
156
+
157
+ Returns available Caller IDs.
158
+
159
+ - `extensions`: Array of available DID numbers/extensions.
160
+ - `setExtension(ext)`: Switch to a different Caller ID.
161
+ - `isLoading`: Boolean loading state.
162
+
139
163
  ## Styling
140
164
 
141
165
  The components use Tailwind CSS internally. To avoid conflicts, all styles are prefixed with `dora-`. You must import the CSS file in your main entry file (e.g., `layout.tsx` or `App.tsx`):
package/dist/index.d.mts CHANGED
@@ -23,7 +23,7 @@ interface DialpadProps {
23
23
  selectedExtension?: string;
24
24
  onExtensionChange?: (ext: string) => void;
25
25
  }
26
- declare function Dialpad({ onCallInitiated, initialNumber, showKeys, className, availableExtensions, selectedExtension, onExtensionChange, }: DialpadProps): react_jsx_runtime.JSX.Element;
26
+ declare function Dialpad({ onCallInitiated, initialNumber, showKeys, className, availableExtensions: providedExtensions, selectedExtension: providedSelectedExtension, onExtensionChange, }: DialpadProps): react_jsx_runtime.JSX.Element;
27
27
 
28
28
  interface DoraCellContextValue {
29
29
  sdk: DoraCell | null;
@@ -34,6 +34,7 @@ interface DoraCellContextValue {
34
34
  isMuted: boolean;
35
35
  isInitialized: boolean;
36
36
  error: Error | null;
37
+ extension: string | null;
37
38
  call: (phoneNumber: string, extension?: string) => Promise<void>;
38
39
  hangup: () => void;
39
40
  toggleMute: () => void;
@@ -71,6 +72,31 @@ declare function useConnectionStatus(): {
71
72
  isInitialized: boolean;
72
73
  isConnected: boolean;
73
74
  error: Error | null;
75
+ extension: string | null;
74
76
  };
77
+ /**
78
+ * Hook to fetch wallet/credit balance
79
+ */
80
+ declare function useWallet(): {
81
+ balance: number;
82
+ currency: string;
83
+ isLoading: boolean;
84
+ error: Error | null;
85
+ refresh: () => Promise<void>;
86
+ };
87
+ /**
88
+ * Hook for managing extensions (DID numbers)
89
+ */
90
+ declare function useExtensions(): {
91
+ extensions: any[];
92
+ isLoading: boolean;
93
+ error: Error | null;
94
+ setExtension: (extension: string) => Promise<void>;
95
+ refresh: () => Promise<void>;
96
+ };
97
+ /**
98
+ * Credit Balance component - mirrors main app style
99
+ */
100
+ declare function CreditBalance(): react_jsx_runtime.JSX.Element;
75
101
 
76
- export { CallInterface, Dialpad, DoraCellProvider, type DoraCellProviderProps, useCall, useConnectionStatus, useDoraCell };
102
+ export { CallInterface, CreditBalance, Dialpad, DoraCellProvider, type DoraCellProviderProps, useCall, useConnectionStatus, useDoraCell, useExtensions, useWallet };
package/dist/index.d.ts CHANGED
@@ -23,7 +23,7 @@ interface DialpadProps {
23
23
  selectedExtension?: string;
24
24
  onExtensionChange?: (ext: string) => void;
25
25
  }
26
- declare function Dialpad({ onCallInitiated, initialNumber, showKeys, className, availableExtensions, selectedExtension, onExtensionChange, }: DialpadProps): react_jsx_runtime.JSX.Element;
26
+ declare function Dialpad({ onCallInitiated, initialNumber, showKeys, className, availableExtensions: providedExtensions, selectedExtension: providedSelectedExtension, onExtensionChange, }: DialpadProps): react_jsx_runtime.JSX.Element;
27
27
 
28
28
  interface DoraCellContextValue {
29
29
  sdk: DoraCell | null;
@@ -34,6 +34,7 @@ interface DoraCellContextValue {
34
34
  isMuted: boolean;
35
35
  isInitialized: boolean;
36
36
  error: Error | null;
37
+ extension: string | null;
37
38
  call: (phoneNumber: string, extension?: string) => Promise<void>;
38
39
  hangup: () => void;
39
40
  toggleMute: () => void;
@@ -71,6 +72,31 @@ declare function useConnectionStatus(): {
71
72
  isInitialized: boolean;
72
73
  isConnected: boolean;
73
74
  error: Error | null;
75
+ extension: string | null;
74
76
  };
77
+ /**
78
+ * Hook to fetch wallet/credit balance
79
+ */
80
+ declare function useWallet(): {
81
+ balance: number;
82
+ currency: string;
83
+ isLoading: boolean;
84
+ error: Error | null;
85
+ refresh: () => Promise<void>;
86
+ };
87
+ /**
88
+ * Hook for managing extensions (DID numbers)
89
+ */
90
+ declare function useExtensions(): {
91
+ extensions: any[];
92
+ isLoading: boolean;
93
+ error: Error | null;
94
+ setExtension: (extension: string) => Promise<void>;
95
+ refresh: () => Promise<void>;
96
+ };
97
+ /**
98
+ * Credit Balance component - mirrors main app style
99
+ */
100
+ declare function CreditBalance(): react_jsx_runtime.JSX.Element;
75
101
 
76
- export { CallInterface, Dialpad, DoraCellProvider, type DoraCellProviderProps, useCall, useConnectionStatus, useDoraCell };
102
+ export { CallInterface, CreditBalance, Dialpad, DoraCellProvider, type DoraCellProviderProps, useCall, useConnectionStatus, useDoraCell, useExtensions, useWallet };
package/dist/index.js CHANGED
@@ -101,8 +101,12 @@ var __iconNode2 = [
101
101
  ];
102
102
  var ArrowUpRight = createLucideIcon("arrow-up-right", __iconNode2);
103
103
 
104
+ // ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.3/node_modules/lucide-react/dist/esm/icons/chevron-down.js
105
+ var __iconNode3 = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
106
+ var ChevronDown = createLucideIcon("chevron-down", __iconNode3);
107
+
104
108
  // ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.3/node_modules/lucide-react/dist/esm/icons/delete.js
105
- var __iconNode3 = [
109
+ var __iconNode4 = [
106
110
  [
107
111
  "path",
108
112
  {
@@ -113,19 +117,19 @@ var __iconNode3 = [
113
117
  ["path", { d: "m12 9 6 6", key: "anjzzh" }],
114
118
  ["path", { d: "m18 9-6 6", key: "1fp51s" }]
115
119
  ];
116
- var Delete = createLucideIcon("delete", __iconNode3);
120
+ var Delete = createLucideIcon("delete", __iconNode4);
117
121
 
118
122
  // ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.3/node_modules/lucide-react/dist/esm/icons/maximize.js
119
- var __iconNode4 = [
123
+ var __iconNode5 = [
120
124
  ["path", { d: "M8 3H5a2 2 0 0 0-2 2v3", key: "1dcmit" }],
121
125
  ["path", { d: "M21 8V5a2 2 0 0 0-2-2h-3", key: "1e4gt3" }],
122
126
  ["path", { d: "M3 16v3a2 2 0 0 0 2 2h3", key: "wsl5sc" }],
123
127
  ["path", { d: "M16 21h3a2 2 0 0 0 2-2v-3", key: "18trek" }]
124
128
  ];
125
- var Maximize = createLucideIcon("maximize", __iconNode4);
129
+ var Maximize = createLucideIcon("maximize", __iconNode5);
126
130
 
127
131
  // ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.3/node_modules/lucide-react/dist/esm/icons/mic-off.js
128
- var __iconNode5 = [
132
+ var __iconNode6 = [
129
133
  ["path", { d: "M12 19v3", key: "npa21l" }],
130
134
  ["path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33", key: "1gzdoj" }],
131
135
  ["path", { d: "M16.95 16.95A7 7 0 0 1 5 12v-2", key: "cqa7eg" }],
@@ -133,27 +137,27 @@ var __iconNode5 = [
133
137
  ["path", { d: "m2 2 20 20", key: "1ooewy" }],
134
138
  ["path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12", key: "r2i35w" }]
135
139
  ];
136
- var MicOff = createLucideIcon("mic-off", __iconNode5);
140
+ var MicOff = createLucideIcon("mic-off", __iconNode6);
137
141
 
138
142
  // ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.3/node_modules/lucide-react/dist/esm/icons/mic.js
139
- var __iconNode6 = [
143
+ var __iconNode7 = [
140
144
  ["path", { d: "M12 19v3", key: "npa21l" }],
141
145
  ["path", { d: "M19 10v2a7 7 0 0 1-14 0v-2", key: "1vc78b" }],
142
146
  ["rect", { x: "9", y: "2", width: "6", height: "13", rx: "3", key: "s6n7sd" }]
143
147
  ];
144
- var Mic = createLucideIcon("mic", __iconNode6);
148
+ var Mic = createLucideIcon("mic", __iconNode7);
145
149
 
146
150
  // ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.3/node_modules/lucide-react/dist/esm/icons/minimize-2.js
147
- var __iconNode7 = [
151
+ var __iconNode8 = [
148
152
  ["path", { d: "m14 10 7-7", key: "oa77jy" }],
149
153
  ["path", { d: "M20 10h-6V4", key: "mjg0md" }],
150
154
  ["path", { d: "m3 21 7-7", key: "tjx5ai" }],
151
155
  ["path", { d: "M4 14h6v6", key: "rmj7iw" }]
152
156
  ];
153
- var Minimize2 = createLucideIcon("minimize-2", __iconNode7);
157
+ var Minimize2 = createLucideIcon("minimize-2", __iconNode8);
154
158
 
155
159
  // ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.3/node_modules/lucide-react/dist/esm/icons/phone.js
156
- var __iconNode8 = [
160
+ var __iconNode9 = [
157
161
  [
158
162
  "path",
159
163
  {
@@ -162,21 +166,21 @@ var __iconNode8 = [
162
166
  }
163
167
  ]
164
168
  ];
165
- var Phone = createLucideIcon("phone", __iconNode8);
169
+ var Phone = createLucideIcon("phone", __iconNode9);
166
170
 
167
171
  // ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.3/node_modules/lucide-react/dist/esm/icons/user.js
168
- var __iconNode9 = [
172
+ var __iconNode10 = [
169
173
  ["path", { d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2", key: "975kel" }],
170
174
  ["circle", { cx: "12", cy: "7", r: "4", key: "17ys0d" }]
171
175
  ];
172
- var User = createLucideIcon("user", __iconNode9);
176
+ var User = createLucideIcon("user", __iconNode10);
173
177
 
174
178
  // ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.3/node_modules/lucide-react/dist/esm/icons/x.js
175
- var __iconNode10 = [
179
+ var __iconNode11 = [
176
180
  ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
177
181
  ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
178
182
  ];
179
- var X = createLucideIcon("x", __iconNode10);
183
+ var X = createLucideIcon("x", __iconNode11);
180
184
  function CallInterface({
181
185
  isOpen = false,
182
186
  onOpenChange,
@@ -423,43 +427,91 @@ function Dialpad({
423
427
  initialNumber = "",
424
428
  showKeys = true,
425
429
  className = "",
426
- availableExtensions = [],
427
- selectedExtension,
430
+ availableExtensions: providedExtensions,
431
+ selectedExtension: providedSelectedExtension,
428
432
  onExtensionChange
429
433
  }) {
430
434
  const { call, callStatus } = useCall();
431
435
  const { isConnected } = useConnectionStatus();
436
+ const { extensions: fetchedExtensions, setExtension } = useExtensions();
432
437
  const [number, setNumber] = react.useState(initialNumber);
433
438
  const [keysVisible, setKeysVisible] = react.useState(showKeys);
439
+ const [localSelectedExt, setLocalSelectedExt] = react.useState("");
440
+ const extensions = providedExtensions?.length ? providedExtensions : fetchedExtensions.map((ext) => ({
441
+ label: ext.extension,
442
+ value: ext.extension
443
+ }));
444
+ const activeExtension = providedSelectedExtension || localSelectedExt || (extensions.length > 0 ? extensions[0].value : "");
445
+ react.useEffect(() => {
446
+ if (!localSelectedExt && extensions.length > 0 && !providedSelectedExtension) {
447
+ setLocalSelectedExt(extensions[0].value);
448
+ }
449
+ }, [extensions, providedSelectedExtension]);
434
450
  const append = (digit) => setNumber((s) => s + digit);
435
451
  const backspace = () => setNumber((s) => s.slice(0, -1));
452
+ const handleExtensionChange = async (ext) => {
453
+ setLocalSelectedExt(ext);
454
+ onExtensionChange?.(ext);
455
+ await setExtension(ext);
456
+ };
436
457
  const handleCall = async () => {
437
458
  if (!number || number.trim() === "") return;
438
459
  try {
439
- await call(number, selectedExtension);
460
+ await call(number, activeExtension);
440
461
  onCallInitiated?.(number);
441
462
  } catch (e) {
442
463
  console.error("Call failed", e);
443
464
  }
444
465
  };
445
466
  const isCallDisabled = !number || number.trim() === "" || !isConnected || callStatus === "ringing" || callStatus === "ongoing" || callStatus === "connecting";
446
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `dora-space-y-4 ${className}`, children: [
447
- availableExtensions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dora-gap-2 dora-bg-[#F6F7F9] dora-rounded-lg dora-flex dora-items-center dora-justify-between dora-p-2 md:dora-p-2.5 dora-mb-4", children: [
448
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "dora-text-xs md:dora-text-sm dora-text-neutral-500 dora-font-normal dora-whitespace-nowrap", children: "Caller ID" }),
449
- /* @__PURE__ */ jsxRuntime.jsx(
450
- "select",
467
+ const keypad_icon = /* @__PURE__ */ jsxRuntime.jsx(
468
+ "svg",
469
+ {
470
+ width: "20",
471
+ height: "20",
472
+ viewBox: "0 0 20 20",
473
+ fill: "none",
474
+ xmlns: "http://www.w3.org/2000/svg",
475
+ children: /* @__PURE__ */ jsxRuntime.jsx(
476
+ "path",
451
477
  {
452
- className: "dora-bg-transparent dora-text-sm dora-font-medium dora-outline-none",
453
- value: selectedExtension,
454
- onChange: (e) => onExtensionChange?.(e.target.value),
455
- children: availableExtensions.map((ext) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: ext.value, children: ext.label }, ext.value))
478
+ d: "M11.5333 3.2006C11.5339 3.50413 11.4444 3.801 11.2759 4.05352C11.1075 4.30605 10.8679 4.50284 10.5874 4.61891C10.3069 4.73499 9.99832 4.76512 9.70071 4.70548C9.40309 4.64584 9.12991 4.49911 8.91583 4.28393C8.77351 4.14156 8.66063 3.97254 8.58363 3.78654C8.50662 3.60054 8.46701 3.4012 8.46705 3.19989C8.46709 2.99858 8.50678 2.79925 8.58385 2.61328C8.66093 2.4273 8.77387 2.25834 8.91625 2.11602C9.05862 1.9737 9.22764 1.86081 9.41364 1.78381C9.59964 1.70681 9.79898 1.6672 10.0003 1.66723C10.2016 1.66727 10.4009 1.70696 10.5869 1.78404C10.7729 1.86111 10.9418 1.97406 11.0842 2.11643C11.3717 2.40393 11.5342 2.7931 11.5342 3.19977M14.5333 4.73227C14.7376 4.73703 14.9408 4.7009 15.131 4.62601C15.3211 4.55111 15.4944 4.43896 15.6406 4.29614C15.7868 4.15333 15.9029 3.98273 15.9822 3.79438C16.0616 3.60602 16.1024 3.40372 16.1024 3.19935C16.1024 2.99498 16.0616 2.79267 15.9822 2.60432C15.9029 2.41597 15.7868 2.24537 15.6406 2.10255C15.4944 1.95974 15.3211 1.84759 15.131 1.77269C14.9408 1.6978 14.7376 1.66167 14.5333 1.66643C14.1267 1.66643 13.7367 1.82798 13.4491 2.11554C13.1615 2.40309 13 2.7931 13 3.19977C13 3.60643 13.1615 3.99644 13.4491 4.284C13.7367 4.57155 14.1267 4.73227 14.5333 4.73227ZM7 3.2006C7.00061 3.50413 6.91102 3.801 6.7426 4.05352C6.57419 4.30605 6.33454 4.50284 6.05408 4.61891C5.77362 4.73499 5.46499 4.76512 5.16737 4.70548C4.86976 4.64584 4.59657 4.49911 4.3825 4.28393C4.24018 4.14161 4.12728 3.97265 4.05026 3.7867C3.97324 3.60075 3.93359 3.40145 3.93359 3.20018C3.93359 2.99891 3.97324 2.79961 4.05026 2.61366C4.12728 2.42771 4.24018 2.25875 4.3825 2.11643C4.52482 1.97411 4.69378 1.86122 4.87973 1.78419C5.06568 1.70717 5.26498 1.66753 5.46625 1.66753C5.66752 1.66753 5.86682 1.70717 6.05277 1.78419C6.23872 1.86122 6.40768 1.97411 6.55 2.11643C6.8375 2.40393 6.99917 2.7931 6.99917 3.19977M11.5333 7.7306C11.5352 7.93448 11.4964 8.1367 11.4192 8.32539C11.342 8.51409 11.2279 8.68548 11.0836 8.82952C10.9392 8.97357 10.7677 9.08737 10.5788 9.16426C10.39 9.24114 10.1877 9.27958 9.98381 9.2773C9.77993 9.27503 9.57856 9.2321 9.39148 9.15102C9.2044 9.06994 9.03538 8.95234 8.89433 8.80511C8.75327 8.65789 8.64301 8.484 8.57 8.29362C8.497 8.10325 8.46272 7.90022 8.46917 7.69643C8.48185 7.29562 8.65113 6.91575 8.94068 6.63833C9.23024 6.3609 9.617 6.20802 10.018 6.21249C10.419 6.21697 10.8022 6.37843 11.0855 6.66224C11.3688 6.94606 11.5296 7.32961 11.5333 7.7306ZM14.5333 9.2631C14.7376 9.26786 14.9408 9.23174 15.131 9.15684C15.3211 9.08194 15.4944 8.96979 15.6406 8.82698C15.7868 8.68416 15.9029 8.51356 15.9822 8.32521C16.0616 8.13686 16.1024 7.93455 16.1024 7.73018C16.1024 7.52581 16.0616 7.32351 15.9822 7.13515C15.9029 6.9468 15.7868 6.7762 15.6406 6.63339C15.4944 6.49057 15.3211 6.37842 15.131 6.30352C14.9408 6.22863 14.7376 6.1925 14.5333 6.19727C14.1267 6.19727 13.7367 6.35881 13.4491 6.64637C13.1615 6.93392 13 7.32393 13 7.7306C13 8.13726 13.1615 8.52727 13.4491 8.81483C13.7367 9.10239 14.1267 9.26393 14.5333 9.26393M7 7.7306C7.00021 8.03395 6.91038 8.33054 6.7419 8.58281C6.57341 8.83507 6.33385 9.03165 6.05356 9.14766C5.77326 9.26367 5.46485 9.29389 5.16737 9.23448C4.86989 9.17506 4.59673 9.0287 4.3825 8.81393C4.20461 8.63559 4.07342 8.41615 4.00054 8.17503C3.92766 7.93391 3.91534 7.67854 3.96468 7.43152C4.01401 7.1845 4.12347 6.95345 4.28337 6.75882C4.44328 6.56419 4.64869 6.41197 4.88144 6.31564C5.11419 6.21931 5.36709 6.18184 5.61777 6.20654C5.86845 6.23124 6.10918 6.31735 6.31865 6.45725C6.52812 6.59716 6.69988 6.78654 6.81872 7.00864C6.93756 7.23073 6.99982 7.4787 7 7.7306ZM11.5333 12.2623C11.5348 12.4893 11.4857 12.7138 11.3897 12.9196C11.2937 13.1253 11.1532 13.3071 10.9783 13.4519C10.8034 13.5967 10.5985 13.7007 10.3784 13.7566C10.1583 13.8124 9.9286 13.8186 9.70583 13.7748C9.42088 13.7191 9.1576 13.5836 8.94662 13.3841C8.73564 13.1847 8.58563 12.9294 8.51403 12.648C8.44244 12.3666 8.4522 12.0707 8.5422 11.7947C8.63219 11.5186 8.79871 11.2738 9.02238 11.0887C9.24606 10.9036 9.51769 10.7857 9.8057 10.7489C10.0937 10.7122 10.3862 10.7579 10.6493 10.8808C10.9123 11.0038 11.135 11.1989 11.2915 11.4434C11.448 11.688 11.5319 11.9719 11.5333 12.2623ZM11.5333 16.8006C11.5337 17.0274 11.4836 17.2515 11.3868 17.4567C11.29 17.6618 11.1488 17.8429 10.9735 17.9868C10.7981 18.1308 10.593 18.2339 10.3729 18.2889C10.1528 18.3439 9.92326 18.3493 9.70083 18.3048C9.40375 18.2453 9.13093 18.0992 8.91681 17.8849C8.70269 17.6705 8.55688 17.3975 8.49777 17.1004C8.43866 16.8032 8.46891 16.4952 8.58469 16.2152C8.70048 15.9353 8.89661 15.6959 9.14833 15.5273C9.37915 15.3731 9.64749 15.2845 9.92473 15.2708C10.202 15.2572 10.4777 15.3191 10.7225 15.4499C10.9673 15.5807 11.1721 15.7755 11.3149 16.0135C11.4577 16.2515 11.5332 16.523 11.5333 16.8006ZM16.0667 12.2623C16.0681 12.4893 16.019 12.7138 15.923 12.9196C15.827 13.1253 15.6865 13.3071 15.5116 13.4519C15.3367 13.5967 15.1318 13.7007 14.9117 13.7566C14.6917 13.8124 14.4619 13.8186 14.2392 13.7748C14.0165 13.7312 13.8062 13.6388 13.6236 13.5041C13.4409 13.3695 13.2904 13.1959 13.183 12.9961C13.0755 12.7962 13.0137 12.575 13.0021 12.3484C12.9905 12.1218 13.0294 11.8954 13.1158 11.6856C13.2509 11.3581 13.4952 11.0875 13.8072 10.9197C14.1193 10.7519 14.4797 10.6974 14.8274 10.7653C15.1751 10.8333 15.4886 11.0195 15.7145 11.2924C15.9404 11.5653 16.0648 11.908 16.0667 12.2623ZM7 12.2623C7.00227 12.5661 6.91412 12.8638 6.74675 13.1174C6.57939 13.371 6.34039 13.5691 6.06013 13.6864C5.77987 13.8038 5.47104 13.8352 5.1729 13.7766C4.87476 13.718 4.60079 13.572 4.38583 13.3573C4.20705 13.1796 4.07483 12.9606 4.00086 12.7196C3.9269 12.4787 3.91347 12.2232 3.96176 11.9759C4.01006 11.7285 4.11859 11.4968 4.27776 11.3014C4.43692 11.106 4.64181 10.9528 4.87428 10.8554C5.10676 10.758 5.35966 10.7194 5.61059 10.7431C5.86153 10.7668 6.10276 10.8519 6.31294 10.991C6.52313 11.1301 6.69578 11.3189 6.81561 11.5406C6.93545 11.7624 6.99878 12.0102 7 12.2623Z",
479
+ fill: "currentColor"
456
480
  }
457
481
  )
482
+ }
483
+ );
484
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `dora-bg-white dora-rounded-3xl ${className} dora-font-sans dora-relative dora-z-10`, children: [
485
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dora-flex dora-items-center dora-justify-between dora-mb-6", children: [
486
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "dora-bg-emerald-500 dora-text-white dora-px-3 dora-py-1.5 dora-rounded-lg dora-text-xs md:dora-text-sm dora-font-semibold dora-shadow-sm", children: "Dora-cell" }),
487
+ /* @__PURE__ */ jsxRuntime.jsx(CreditBalance, {})
458
488
  ] }),
459
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dora-space-y-2 md:dora-space-y-3 dora-bg-[#F6F7F9] dora-p-2 md:dora-p-2.5 dora-rounded-lg", children: [
460
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dora-flex dora-items-center dora-gap-2 md:dora-gap-3", children: [
461
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dora-flex-1 dora-min-w-0 dora-flex dora-items-center dora-bg-white dora-rounded-md dora-px-3 dora-h-10 dora-border dora-border-transparent focus-within:dora-border-green-500 dora-transition-colors", children: [
462
- /* @__PURE__ */ jsxRuntime.jsx(User, { className: "dora-text-gray-400 dora-mr-2", size: 18 }),
489
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dora-bg-zinc-50 dora-p-6 dora-rounded-3xl dora-border dora-border-zinc-100 dora-shadow-inner", children: [
490
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dora-flex dora-items-center dora-justify-between dora-mb-5", children: [
491
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dora-text-zinc-500 dora-font-medium dora-text-sm dora-tracking-tight", children: "Caller ID" }),
492
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dora-relative", children: [
493
+ /* @__PURE__ */ jsxRuntime.jsx(
494
+ "select",
495
+ {
496
+ disabled: extensions.length === 0,
497
+ className: "dora-appearance-none dora-bg-white dora-border dora-border-zinc-200 dora-rounded-xl dora-px-4 dora-py-2 dora-text-sm dora-font-semibold dora-text-zinc-900 dora-min-w-[170px] dora-pr-10 dora-outline-none focus:dora-border-emerald-500 dora-transition-all dora-shadow-sm disabled:dora-opacity-50",
498
+ value: activeExtension,
499
+ onChange: (e) => handleExtensionChange(e.target.value),
500
+ children: extensions.length > 0 ? extensions.map((ext) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: ext.value, children: ext.label }, ext.value)) : /* @__PURE__ */ jsxRuntime.jsx("option", { children: "Loading..." })
501
+ }
502
+ ),
503
+ /* @__PURE__ */ jsxRuntime.jsx(
504
+ ChevronDown,
505
+ {
506
+ size: 18,
507
+ className: "dora-absolute dora-right-4 dora-top-1/2 dora-transform dora--translate-y-1/2 dora-text-zinc-500 dora-pointer-events-none"
508
+ }
509
+ )
510
+ ] })
511
+ ] }),
512
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dora-flex dora-items-center dora-gap-3 dora-mb-6", children: [
513
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dora-flex-1 dora-bg-white dora-border dora-border-zinc-200 dora-rounded-xl dora-flex dora-items-center dora-px-4 dora-h-12 dora-shadow-sm focus-within:dora-border-emerald-500 dora-transition-all", children: [
514
+ /* @__PURE__ */ jsxRuntime.jsx(User, { size: 18, className: "dora-text-zinc-300 dora-mr-2" }),
463
515
  /* @__PURE__ */ jsxRuntime.jsx(
464
516
  "input",
465
517
  {
@@ -467,7 +519,7 @@ function Dialpad({
467
519
  placeholder: "Enter number",
468
520
  value: number,
469
521
  onChange: (e) => setNumber(e.target.value),
470
- className: "dora-bg-transparent dora-border-none dora-outline-none dora-w-full dora-text-base dora-text-black"
522
+ className: "dora-bg-transparent dora-border-none dora-outline-none dora-w-full dora-text-base dora-text-zinc-900 dora-font-medium dora-placeholder-zinc-300"
471
523
  }
472
524
  )
473
525
  ] }),
@@ -476,25 +528,24 @@ function Dialpad({
476
528
  {
477
529
  disabled: isCallDisabled,
478
530
  onClick: handleCall,
479
- title: "Place Call",
480
- className: "dora-bg-green-500 dora-text-white hover:dora-bg-green-600 dora-rounded-lg dora-h-9 md:dora-h-10 dora-w-9 md:dora-w-11 dora-flex dora-items-center dora-justify-center disabled:dora-opacity-50 disabled:dora-cursor-not-allowed dora-transition-colors",
481
- children: /* @__PURE__ */ jsxRuntime.jsx(Phone, { size: 16, fill: "currentColor" })
531
+ className: "dora-bg-emerald-500 dora-text-white dora-w-12 dora-h-12 dora-rounded-xl dora-flex dora-items-center dora-justify-center dora-shadow-lg dora-shadow-emerald-500/20 hover:dora-bg-emerald-600 active:dora-scale-95 dora-transition-all disabled:dora-opacity-50",
532
+ children: /* @__PURE__ */ jsxRuntime.jsx(Phone, { size: 20, fill: "white", className: "dora-text-white" })
482
533
  }
483
534
  )
484
535
  ] }),
485
- /* @__PURE__ */ jsxRuntime.jsx("div", { children: keysVisible ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dora-pt-3", children: [
486
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "dora-flex dora-items-center dora-gap-3 dora-mb-3", children: /* @__PURE__ */ jsxRuntime.jsxs(
536
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "dora-space-y-5", children: keysVisible ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
537
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "dora-flex dora-mb-5", children: /* @__PURE__ */ jsxRuntime.jsxs(
487
538
  "button",
488
539
  {
489
540
  onClick: () => setKeysVisible(false),
490
- className: "dora-px-2 md:dora-px-2.5 dora-h-8 dora-bg-white dora-rounded-full dora-shadow dora-text-neutral-500 dora-flex dora-items-center dora-gap-1.5 md:dora-gap-2 dora-text-xs md:dora-text-sm dora-font-normal hover:dora-bg-gray-50 dora-transition-colors",
541
+ className: "dora-flex dora-items-center dora-gap-2 dora-px-4 dora-py-2 dora-bg-white dora-border dora-border-zinc-200 dora-rounded-full dora-text-xs dora-font-semibold dora-text-zinc-600 hover:dora-bg-zinc-50 dora-transition-colors dora-shadow-sm active:dora-scale-95",
491
542
  children: [
492
- /* @__PURE__ */ jsxRuntime.jsx(X, { color: "red", size: 16 }),
493
- " Close keypad"
543
+ /* @__PURE__ */ jsxRuntime.jsx(X, { size: 16, className: "dora-text-red-500" }),
544
+ "Close"
494
545
  ]
495
546
  }
496
547
  ) }),
497
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "dora-grid dora-grid-cols-3 dora-gap-2 md:dora-gap-3", children: [
548
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "dora-grid dora-grid-cols-3 dora-gap-4", children: [
498
549
  "1",
499
550
  "2",
500
551
  "3",
@@ -511,20 +562,19 @@ function Dialpad({
511
562
  "button",
512
563
  {
513
564
  onClick: () => d === "del" ? backspace() : append(d),
514
- className: "dora-h-10 md:dora-h-12 dora-bg-white dora-rounded-xl dora-shadow dora-flex dora-items-center dora-justify-center dora-text-base md:dora-text-lg dora-font-medium dora-text-slate-900 hover:dora-bg-gray-50 active:dora-bg-gray-100 dora-transition-colors",
515
- "aria-label": d === "del" ? "Delete" : `Dial ${d}`,
516
- children: d === "del" ? /* @__PURE__ */ jsxRuntime.jsx(Delete, { size: 18 }) : d
565
+ className: "dora-h-12 dora-bg-white dora-rounded-xl dora-flex dora-items-center dora-justify-center dora-text-lg dora-font-semibold dora-text-zinc-900 dora-shadow-sm border dora-border-zinc-50 hover:dora-bg-zinc-50 active:dora-scale-95 dora-transition-all",
566
+ children: d === "del" ? /* @__PURE__ */ jsxRuntime.jsx(Delete, { size: 18, className: "dora-text-zinc-500" }) : d
517
567
  },
518
568
  d
519
569
  )) })
520
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "dora-pt-2 md:dora-pt-3", children: /* @__PURE__ */ jsxRuntime.jsxs(
570
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "dora-flex dora-justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs(
521
571
  "button",
522
572
  {
523
573
  onClick: () => setKeysVisible(true),
524
- className: "dora-rounded-full dora-bg-[#EDEEF1] dora-h-9 md:dora-h-10 dora-flex dora-justify-center dora-items-center dora-px-3 md:dora-px-4 dora-w-full dora-text-xs md:dora-text-sm hover:dora-bg-[#E3E4E8] dora-transition-colors",
574
+ className: "dora-flex dora-items-center dora-gap-2 dora-px-4 dora-py-2 dora-bg-white dora-border dora-border-zinc-200 dora-rounded-full dora-text-xs dora-font-semibold dora-text-zinc-500 hover:dora-bg-zinc-50 dora-transition-all active:dora-scale-95 dora-shadow-sm",
525
575
  children: [
526
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dora-mr-2", children: "\u{1F522}" }),
527
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dora-text-xs md:dora-text-sm dora-text-neutral-500 dora-font-normal dora-ml-1.5", children: "Open dialer" })
576
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dora-transform dora-scale-90", children: keypad_icon }),
577
+ "Open dialer"
528
578
  ]
529
579
  }
530
580
  ) }) })
@@ -545,6 +595,7 @@ function DoraCellProvider({
545
595
  const [callDuration, setCallDuration] = react.useState("00:00");
546
596
  const [isMuted, setIsMuted] = react.useState(false);
547
597
  const [error, setError] = react.useState(null);
598
+ const [extension, setExtension] = react.useState(null);
548
599
  const durationIntervalRef = react.useRef(null);
549
600
  react.useEffect(() => {
550
601
  if (!autoInitialize) return;
@@ -565,6 +616,9 @@ function DoraCellProvider({
565
616
  if (!sdk$1) return;
566
617
  const handleConnectionStatus = (state) => {
567
618
  setConnectionStatus(state.status);
619
+ if (state.extension) {
620
+ setExtension(state.extension);
621
+ }
568
622
  if (state.error) {
569
623
  setError(new Error(state.error));
570
624
  }
@@ -639,10 +693,10 @@ function DoraCellProvider({
639
693
  }
640
694
  };
641
695
  }, [callStatus, currentCall]);
642
- const call = async (phoneNumber, extension) => {
696
+ const call = async (phoneNumber, extension2) => {
643
697
  try {
644
698
  setError(null);
645
- await sdk$1.call(phoneNumber, { extension });
699
+ await sdk$1.call(phoneNumber, { extension: extension2 });
646
700
  } catch (err) {
647
701
  setError(err instanceof Error ? err : new Error("Failed to make call"));
648
702
  throw err;
@@ -684,7 +738,8 @@ function DoraCellProvider({
684
738
  call,
685
739
  hangup,
686
740
  toggleMute,
687
- answerCall
741
+ answerCall,
742
+ extension
688
743
  };
689
744
  return /* @__PURE__ */ jsxRuntime.jsx(DoraCellContext.Provider, { value: contextValue, children });
690
745
  }
@@ -710,13 +765,106 @@ function useCall() {
710
765
  };
711
766
  }
712
767
  function useConnectionStatus() {
713
- const { connectionStatus, isInitialized, error } = useDoraCell();
768
+ const { connectionStatus, isInitialized, error, extension } = useDoraCell();
714
769
  return {
715
770
  connectionStatus,
716
771
  isInitialized,
717
772
  isConnected: connectionStatus === "registered",
718
- error
773
+ error,
774
+ extension
775
+ };
776
+ }
777
+ function useWallet() {
778
+ const { sdk, isInitialized } = useDoraCell();
779
+ const [data, setData] = react.useState(null);
780
+ const [isLoading, setIsLoading] = react.useState(false);
781
+ const [error, setError] = react.useState(null);
782
+ const fetchData = async () => {
783
+ if (!sdk || !isInitialized) return;
784
+ setIsLoading(true);
785
+ setError(null);
786
+ try {
787
+ const val = await sdk.getWallet();
788
+ setData(val);
789
+ } catch (err) {
790
+ setError(err instanceof Error ? err : new Error("Failed to fetch wallet"));
791
+ } finally {
792
+ setIsLoading(false);
793
+ }
794
+ };
795
+ react.useEffect(() => {
796
+ if (isInitialized) {
797
+ console.log("SDK: useWallet fetching data...");
798
+ fetchData();
799
+ }
800
+ }, [isInitialized, sdk]);
801
+ react.useEffect(() => {
802
+ if (!sdk || !isInitialized) return;
803
+ const handleCallEnded = () => {
804
+ console.log("SDK: Call ended, refreshing wallet balance in 2 seconds...");
805
+ setTimeout(fetchData, 2e3);
806
+ };
807
+ sdk.on("call:ended", handleCallEnded);
808
+ return () => {
809
+ sdk.off("call:ended", handleCallEnded);
810
+ };
811
+ }, [sdk, isInitialized]);
812
+ return {
813
+ balance: data?.balance ?? 0,
814
+ currency: data?.currency ?? "NGN",
815
+ isLoading,
816
+ error,
817
+ refresh: fetchData
818
+ };
819
+ }
820
+ function useExtensions() {
821
+ const { sdk, isInitialized } = useDoraCell();
822
+ const [extensions, setExtensions] = react.useState([]);
823
+ const [isLoading, setIsLoading] = react.useState(false);
824
+ const [error, setError] = react.useState(null);
825
+ const fetchExtensions = async () => {
826
+ if (!sdk || !isInitialized) return;
827
+ setIsLoading(true);
828
+ setError(null);
829
+ try {
830
+ const data = await sdk.fetchExtensions();
831
+ setExtensions(data);
832
+ } catch (err) {
833
+ setError(err instanceof Error ? err : new Error("Failed to fetch extensions"));
834
+ } finally {
835
+ setIsLoading(false);
836
+ }
837
+ };
838
+ const setExtension = async (extension) => {
839
+ if (!sdk) return;
840
+ await sdk.setExtension(extension);
719
841
  };
842
+ react.useEffect(() => {
843
+ if (isInitialized) {
844
+ fetchExtensions();
845
+ }
846
+ }, [isInitialized, sdk]);
847
+ return {
848
+ extensions,
849
+ isLoading,
850
+ error,
851
+ setExtension,
852
+ refresh: fetchExtensions
853
+ };
854
+ }
855
+ function CreditBalance() {
856
+ const { balance, currency, isLoading, error } = useWallet();
857
+ if (error) {
858
+ console.warn("SDK: CreditBalance error:", error);
859
+ }
860
+ const formatted = new Intl.NumberFormat("en-NG", {
861
+ style: "currency",
862
+ currency: currency === "NGN" ? "NGN" : currency || "NGN"
863
+ }).format(balance);
864
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dora-credit-balance dora-px-4 dora-py-2 dora-bg-zinc-100 dora-border dora-border-zinc-200 dora-rounded-full dora-text-sm dora-text-zinc-600 dora-whitespace-nowrap dora-flex dora-items-center dora-shadow-sm", children: [
865
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dora-text-xs dora-text-zinc-500 dora-mr-2", children: "Credit balance:" }),
866
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dora-font-bold dora-text-zinc-800", children: isLoading ? "..." : formatted })
867
+ ] });
720
868
  }
721
869
  /*! Bundled license information:
722
870
 
@@ -726,6 +874,7 @@ lucide-react/dist/esm/Icon.js:
726
874
  lucide-react/dist/esm/createLucideIcon.js:
727
875
  lucide-react/dist/esm/icons/arrow-down-left.js:
728
876
  lucide-react/dist/esm/icons/arrow-up-right.js:
877
+ lucide-react/dist/esm/icons/chevron-down.js:
729
878
  lucide-react/dist/esm/icons/delete.js:
730
879
  lucide-react/dist/esm/icons/maximize.js:
731
880
  lucide-react/dist/esm/icons/mic-off.js:
@@ -744,10 +893,13 @@ lucide-react/dist/esm/lucide-react.js:
744
893
  */
745
894
 
746
895
  exports.CallInterface = CallInterface;
896
+ exports.CreditBalance = CreditBalance;
747
897
  exports.Dialpad = Dialpad;
748
898
  exports.DoraCellProvider = DoraCellProvider;
749
899
  exports.useCall = useCall;
750
900
  exports.useConnectionStatus = useConnectionStatus;
751
901
  exports.useDoraCell = useDoraCell;
902
+ exports.useExtensions = useExtensions;
903
+ exports.useWallet = useWallet;
752
904
  //# sourceMappingURL=index.js.map
753
905
  //# sourceMappingURL=index.js.map