@visns-studio/visns-components 6.26.0 → 6.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -9,6 +9,51 @@ A comprehensive React component library used by the VISNS Studio team for CRM an
|
|
|
9
9
|
|
|
10
10
|
VISNS Components is a React-based UI component library that provides a set of reusable, consistent, and customizable components for building web applications. It includes components for authentication, data grids, forms, navigation, and more, designed to work seamlessly together.
|
|
11
11
|
|
|
12
|
+
## Recent Updates (v6.27.0)
|
|
13
|
+
|
|
14
|
+
### A tint on one column: `cellColours`
|
|
15
|
+
|
|
16
|
+
A DataGrid could already tint a whole ROW from its data —
|
|
17
|
+
`ajaxSetting.rowColours`, first match wins:
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
ajaxSetting: {
|
|
21
|
+
rowColours: [{ id: 'priority', value: 'high', colour: '#FFCDD2' }],
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Which is the right instrument for "this record needs attention" and the wrong
|
|
26
|
+
one for "these are Sam's". A grid where every row is coloured has no signal
|
|
27
|
+
left in colour at all — the eye stops reading it after the second full-width
|
|
28
|
+
band. `cellColours` is the same rule shape declared on a COLUMN, painted onto
|
|
29
|
+
that one cell:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
{
|
|
33
|
+
id: ['owner'],
|
|
34
|
+
label: 'Owner',
|
|
35
|
+
type: 'relation',
|
|
36
|
+
nameFrom: 'name',
|
|
37
|
+
cellColours: [
|
|
38
|
+
{ id: 'owner_id', value: 19, colour: '#E1BEE7' },
|
|
39
|
+
{ id: 'owner_id', value: 30, colour: '#BBDEFB' },
|
|
40
|
+
],
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- **`id` names a field on the ROW, not on the column.** The value worth
|
|
45
|
+
matching is usually the id sitting behind the label the column renders
|
|
46
|
+
(`owner_id` under an Owner relation column), and the cell never shows it.
|
|
47
|
+
- **Loose equality**, as `rowStyles` uses — a JSON config writes `19` and an
|
|
48
|
+
API hands back `"19"`, and a config author should not have to know which.
|
|
49
|
+
- **First match wins**, as `rowColours` does. A cell has one background.
|
|
50
|
+
- **Every column type**, because the rule is applied by the shared column
|
|
51
|
+
`style`, not by any one renderer — relation, text, date, currency alike.
|
|
52
|
+
- **It composes with `rowColours`.** The row tint is a background on the row
|
|
53
|
+
element and the cell's own paints on top of it, so a tinted cell reads as
|
|
54
|
+
itself on a tinted row, and keeps its colour under the hover surface (which
|
|
55
|
+
is also below the cell).
|
|
56
|
+
|
|
12
57
|
## Recent Updates (v6.17.0)
|
|
13
58
|
|
|
14
59
|
### A client's text messages, on the client's own page
|
|
@@ -2167,6 +2212,7 @@ endpoint nor an Echo instance present it renders nothing and logs nothing.
|
|
|
2167
2212
|
| `calendarPath` | `'/calendar'` | |
|
|
2168
2213
|
| `callWorkspacePath` | `'/call/{number}'` | Template, or `(workspaceId, call) => path`. `{number}` is the `61…` form the workspace route expects. |
|
|
2169
2214
|
| `syncChannelName` | `'throughlife-call-queue-pop'` | The `BroadcastChannel` that keeps every open tab's stack in step. Falsy switches cross-tab sync off. |
|
|
2215
|
+
| `clientDetailFields` | `CLIENT_DETAIL_FIELDS` (adviser / coding / age / city) | `[{ key, label, demo? }]` — the client-block rows, in card order; rows with an empty value are dropped; `demo` seeds the demo card. Pass your CRM's own fields so the card never names a field it does not have. Must be referentially stable (a module-scope constant, not an inline literal) — it sits in the demo effect's dependency list. |
|
|
2170
2216
|
| `demoEnabled` | `true` | Registers `window.callPopDemo()` / `window.callPopClear()` for reviewing the UI without a backend. |
|
|
2171
2217
|
|
|
2172
2218
|
**Payload contract.** Snake_case and camelCase are both accepted, so a Laravel
|
|
@@ -2181,6 +2227,11 @@ Named exports for testing: `toLocalDigits`, `formatAuPhone`, `formatEventDate`,
|
|
|
2181
2227
|
`formatDueDate`, `toCallWorkspaceId`, `normaliseCall`, `normalisePickupCodes`,
|
|
2182
2228
|
`formatElapsed`, `hasMonitorPermission`, `clientDetails`.
|
|
2183
2229
|
|
|
2230
|
+
`CLIENT_DETAIL_FIELDS`, `clientDetails` and `demoClientDetails` are exported
|
|
2231
|
+
from the package itself (`@visns-studio/visns-components`), so a host can build
|
|
2232
|
+
its own field list beside the default or reuse the demo seeding; the helpers
|
|
2233
|
+
module is not re-exported wholesale, only those three alongside the component.
|
|
2234
|
+
|
|
2184
2235
|
#### CallQueueSettings
|
|
2185
2236
|
|
|
2186
2237
|
The admin table behind the pop: one row per Zoom call queue, carrying the
|
package/package.json
CHANGED
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
|
94
94
|
},
|
|
95
95
|
"name": "@visns-studio/visns-components",
|
|
96
|
-
"version": "6.
|
|
96
|
+
"version": "6.28.0",
|
|
97
97
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
98
98
|
"main": "src/index.js",
|
|
99
99
|
"files": [
|
|
@@ -128,6 +128,31 @@ const ROW_STYLE_CLASSES = {
|
|
|
128
128
|
complete: styles.rowComplete,
|
|
129
129
|
};
|
|
130
130
|
|
|
131
|
+
/**
|
|
132
|
+
* First matching per-column `cellColours` rule for a row.
|
|
133
|
+
*
|
|
134
|
+
* cellColours: [
|
|
135
|
+
* { id: 'owner_id', value: 19, colour: '#E1BEE7' }
|
|
136
|
+
* ]
|
|
137
|
+
*
|
|
138
|
+
* The column-level twin of `ajaxSetting.rowColours`: the same rule shape,
|
|
139
|
+
* painted onto ONE cell instead of the whole row. A grid where every row is
|
|
140
|
+
* tinted is a grid with no signal left, so a view that only needs to tell its
|
|
141
|
+
* owners apart tints the Owner column and leaves the rest of the row alone.
|
|
142
|
+
*
|
|
143
|
+
* `id` names a field on the ROW, not on the column, because the value worth
|
|
144
|
+
* matching is usually the id sitting behind the label the column renders
|
|
145
|
+
* (`owner_id` under an Owner relation column) and the cell never shows it.
|
|
146
|
+
*
|
|
147
|
+
* Loose equality, as `rowStyles` uses: a JSON config writes 19 and an API
|
|
148
|
+
* hands back "19", and a config author should not have to know which. First
|
|
149
|
+
* match wins, as `rowColours` does — a cell has exactly one background.
|
|
150
|
+
*/
|
|
151
|
+
const findCellColour = (column, data) =>
|
|
152
|
+
Array.isArray(column?.cellColours) && data
|
|
153
|
+
? column.cellColours.find((rule) => data[rule.id] == rule.value)
|
|
154
|
+
: undefined;
|
|
155
|
+
|
|
131
156
|
import '@visns-studio/visns-datagrid-enterprise/index.css';
|
|
132
157
|
|
|
133
158
|
import CustomFetch from './Fetch';
|
|
@@ -4438,6 +4463,26 @@ const DataGrid = forwardRef(
|
|
|
4438
4463
|
columnsMetadata
|
|
4439
4464
|
);
|
|
4440
4465
|
|
|
4466
|
+
// A `cellColours` tint is an inline background on the
|
|
4467
|
+
// cell, and global-datagrid.css paints every cell
|
|
4468
|
+
// `background: transparent !important` (it has to
|
|
4469
|
+
// outrank the vendored theme) — inline loses to
|
|
4470
|
+
// `!important`, so the tint would never be seen. The
|
|
4471
|
+
// literal class marks the cells that carry one and
|
|
4472
|
+
// that rule stands down for them, exactly as
|
|
4473
|
+
// `vs-datagrid--row-tinted` does a level up. The
|
|
4474
|
+
// hover and selected surfaces live on the ROW, so the
|
|
4475
|
+
// tint keeps painting over them and survives a hover.
|
|
4476
|
+
const cellColourClassName = (cellProps, extra = '') =>
|
|
4477
|
+
[
|
|
4478
|
+
findCellColour(column, cellProps?.data)
|
|
4479
|
+
? 'vs-datagrid--cell-tinted'
|
|
4480
|
+
: '',
|
|
4481
|
+
extra,
|
|
4482
|
+
]
|
|
4483
|
+
.filter(Boolean)
|
|
4484
|
+
.join(' ');
|
|
4485
|
+
|
|
4441
4486
|
const commonProps = {
|
|
4442
4487
|
header: column.label,
|
|
4443
4488
|
defaultFlex: 1,
|
|
@@ -4481,7 +4526,7 @@ const DataGrid = forwardRef(
|
|
|
4481
4526
|
? style.text_vertical_align
|
|
4482
4527
|
: 'center',
|
|
4483
4528
|
style: (cellProps) => {
|
|
4484
|
-
const { id, value } = cellProps;
|
|
4529
|
+
const { id, value, data } = cellProps;
|
|
4485
4530
|
let columnStyle = {};
|
|
4486
4531
|
|
|
4487
4532
|
if (
|
|
@@ -4501,8 +4546,24 @@ const DataGrid = forwardRef(
|
|
|
4501
4546
|
};
|
|
4502
4547
|
}
|
|
4503
4548
|
|
|
4549
|
+
// `cellColours` last, so a column's static
|
|
4550
|
+
// `style` cannot quietly outrank the rule the
|
|
4551
|
+
// view asked for. It paints over any
|
|
4552
|
+
// `rowColours` tint for this one cell without
|
|
4553
|
+
// touching it: the row tint is a background on
|
|
4554
|
+
// the ROW element and this one sits on the cell
|
|
4555
|
+
// above it.
|
|
4556
|
+
const cellColour = findCellColour(column, data);
|
|
4557
|
+
|
|
4558
|
+
if (cellColour) {
|
|
4559
|
+
columnStyle.backgroundColor =
|
|
4560
|
+
cellColour.colour;
|
|
4561
|
+
}
|
|
4562
|
+
|
|
4504
4563
|
return columnStyle;
|
|
4505
4564
|
},
|
|
4565
|
+
className: (cellProps) =>
|
|
4566
|
+
cellColourClassName(cellProps),
|
|
4506
4567
|
type: column.type,
|
|
4507
4568
|
};
|
|
4508
4569
|
|
|
@@ -4966,9 +5027,13 @@ const DataGrid = forwardRef(
|
|
|
4966
5027
|
name: columnId,
|
|
4967
5028
|
filterEditor: filterEditor,
|
|
4968
5029
|
filterEditorProps: filterEditorProps,
|
|
4969
|
-
className:
|
|
4970
|
-
|
|
4971
|
-
|
|
5030
|
+
className: (cellProps) =>
|
|
5031
|
+
cellColourClassName(
|
|
5032
|
+
cellProps,
|
|
5033
|
+
finalWordWrap
|
|
5034
|
+
? 'cell-word-wrap'
|
|
5035
|
+
: ''
|
|
5036
|
+
),
|
|
4972
5037
|
render: ({ data }) => {
|
|
4973
5038
|
if (
|
|
4974
5039
|
data &&
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
defaultClientTasksUrl,
|
|
41
41
|
defaultClientUrl,
|
|
42
42
|
defaultTaskUrl,
|
|
43
|
+
demoClientDetails,
|
|
43
44
|
directRingingLine,
|
|
44
45
|
formatAuPhone,
|
|
45
46
|
formatDueDate,
|
|
@@ -62,6 +63,7 @@ export {
|
|
|
62
63
|
callBadgeLabel,
|
|
63
64
|
calleeLabel,
|
|
64
65
|
clientDetails,
|
|
66
|
+
demoClientDetails,
|
|
65
67
|
directRingingLine,
|
|
66
68
|
formatAuPhone,
|
|
67
69
|
formatDueDate,
|
|
@@ -286,6 +288,16 @@ const CallQueuePop = ({
|
|
|
286
288
|
calendarPath = '/calendar',
|
|
287
289
|
callWorkspacePath = '/call/{number}',
|
|
288
290
|
syncChannelName = SYNC_CHANNEL,
|
|
291
|
+
// The client-block rows, in card order — the host's own fields, so the
|
|
292
|
+
// card never names one its CRM does not have. The default is this
|
|
293
|
+
// component's original financial-planning shape (adviser / coding / age /
|
|
294
|
+
// city) and the demo card seeds itself from the same list, so a host that
|
|
295
|
+
// passes its own fields also demos its own fields.
|
|
296
|
+
//
|
|
297
|
+
// MUST be referentially stable — a module-scope constant, not an inline
|
|
298
|
+
// literal — because it sits in the demo effect's dependency list, and a
|
|
299
|
+
// fresh array per render would re-register the demo helpers every time.
|
|
300
|
+
clientDetailFields = CLIENT_DETAIL_FIELDS,
|
|
289
301
|
demoEnabled = true,
|
|
290
302
|
missedGraceMs = DEFAULT_MISSED_GRACE_MS,
|
|
291
303
|
}) => {
|
|
@@ -680,20 +692,22 @@ const CallQueuePop = ({
|
|
|
680
692
|
queueName: 'Test Call Queue',
|
|
681
693
|
callerNumber: '+61298765432',
|
|
682
694
|
callerName: 'Margaret Chen',
|
|
683
|
-
//
|
|
684
|
-
//
|
|
695
|
+
// The rich card reviewed without a real match. The detail
|
|
696
|
+
// rows are seeded from `clientDetailFields` itself, so the
|
|
697
|
+
// demo shows exactly the host's own fields with the host's
|
|
698
|
+
// own sample values rather than another CRM's. id 0
|
|
685
699
|
// renders the name as plain text, not a link.
|
|
686
700
|
client: {
|
|
687
701
|
id: 0,
|
|
688
702
|
name: 'Chen, Margaret (Mrs)',
|
|
689
703
|
matched_on: 'home',
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
704
|
+
// Before the spread, so a host listing `email` among
|
|
705
|
+
// its own fields demos its own sample value while a
|
|
706
|
+
// host that does not still gets one.
|
|
693
707
|
email: 'margaret.chen@example.com',
|
|
694
|
-
|
|
708
|
+
...demoClientDetails(clientDetailFields),
|
|
695
709
|
next_event: {
|
|
696
|
-
title: 'Client
|
|
710
|
+
title: 'Client review',
|
|
697
711
|
date: new Date(
|
|
698
712
|
Date.now() + 2 * 24 * 60 * 60 * 1000
|
|
699
713
|
).toISOString(),
|
|
@@ -705,19 +719,19 @@ const CallQueuePop = ({
|
|
|
705
719
|
tasks: [
|
|
706
720
|
{
|
|
707
721
|
id: 90001,
|
|
708
|
-
label: 'Confirm
|
|
722
|
+
label: 'Confirm the site visit time',
|
|
709
723
|
due_date: new Date(
|
|
710
724
|
Date.now() + 3 * 24 * 60 * 60 * 1000
|
|
711
725
|
).toISOString(),
|
|
712
|
-
task_type: '
|
|
726
|
+
task_type: 'Ticket',
|
|
713
727
|
},
|
|
714
728
|
{
|
|
715
729
|
id: 90002,
|
|
716
|
-
label: 'Send updated
|
|
730
|
+
label: 'Send the updated quote',
|
|
717
731
|
due_date: new Date(
|
|
718
732
|
Date.now() + 9 * 24 * 60 * 60 * 1000
|
|
719
733
|
).toISOString(),
|
|
720
|
-
task_type: '
|
|
734
|
+
task_type: 'Quote',
|
|
721
735
|
},
|
|
722
736
|
],
|
|
723
737
|
startedAt: new Date().toISOString(),
|
|
@@ -745,6 +759,7 @@ const CallQueuePop = ({
|
|
|
745
759
|
addDemoPickupCode,
|
|
746
760
|
broadcastSync,
|
|
747
761
|
clearCalls,
|
|
762
|
+
clientDetailFields,
|
|
748
763
|
demoEnabled,
|
|
749
764
|
dropDemoPickupCode,
|
|
750
765
|
]);
|
|
@@ -1707,7 +1722,7 @@ const CallQueuePop = ({
|
|
|
1707
1722
|
{calls.map((call) => {
|
|
1708
1723
|
const pickupCode = pickupCodeFor(call);
|
|
1709
1724
|
const client = call.client;
|
|
1710
|
-
const details = clientDetails(client);
|
|
1725
|
+
const details = clientDetails(client, clientDetailFields);
|
|
1711
1726
|
const nextEvent = client?.next_event ?? null;
|
|
1712
1727
|
const nextEventDate = formatEventDate(nextEvent?.date);
|
|
1713
1728
|
const openTasks = Number(client?.open_tasks ?? 0);
|
|
@@ -210,32 +210,69 @@ export const defaultTaskUrl = (task) => `/tasks/detail/${task?.id}`;
|
|
|
210
210
|
/** Default detail route for a matched client. */
|
|
211
211
|
export const defaultClientUrl = (client) => `/clients/${client?.id}`;
|
|
212
212
|
|
|
213
|
-
/**
|
|
213
|
+
/**
|
|
214
|
+
* The client fields shown as label/value rows, in card order — the DEFAULT,
|
|
215
|
+
* inherited from the financial-planning CRM this component was ported from.
|
|
216
|
+
* Each entry's `demo` is the sample value the demo card shows for it.
|
|
217
|
+
*
|
|
218
|
+
* A host whose caller enrichment returns a different shape passes its own list
|
|
219
|
+
* as `clientDetailFields` rather than living with rows it can never fill.
|
|
220
|
+
*/
|
|
214
221
|
export const CLIENT_DETAIL_FIELDS = [
|
|
215
|
-
{ key: 'adviser', label: 'Adviser' },
|
|
216
|
-
{ key: 'coding', label: 'Coding' },
|
|
217
|
-
{ key: 'age', label: 'Age' },
|
|
218
|
-
{ key: 'city', label: 'City' },
|
|
222
|
+
{ key: 'adviser', label: 'Adviser', demo: 'Alina Bailey' },
|
|
223
|
+
{ key: 'coding', label: 'Coding', demo: 'Early Retirement' },
|
|
224
|
+
{ key: 'age', label: 'Age', demo: 66 },
|
|
225
|
+
{ key: 'city', label: 'City', demo: 'Fremantle' },
|
|
219
226
|
];
|
|
220
227
|
|
|
221
228
|
/**
|
|
222
229
|
* Detail rows for a client block, with the empty ones dropped — a card only
|
|
223
230
|
* ever shows what the CRM actually knows, never a row with a blank value.
|
|
231
|
+
*
|
|
232
|
+
* `fields` is the host's own list, so the card only ever names fields its CRM
|
|
233
|
+
* actually has. An entry with no string `key` is malformed and is ignored
|
|
234
|
+
* rather than drawn as a row with nothing behind it.
|
|
224
235
|
*/
|
|
225
|
-
export const clientDetails = (client) => {
|
|
236
|
+
export const clientDetails = (client, fields = CLIENT_DETAIL_FIELDS) => {
|
|
226
237
|
if (!client || typeof client !== 'object') {
|
|
227
238
|
return [];
|
|
228
239
|
}
|
|
229
240
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
(
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
241
|
+
const list = Array.isArray(fields) ? fields : CLIENT_DETAIL_FIELDS;
|
|
242
|
+
|
|
243
|
+
return list
|
|
244
|
+
.filter((field) => field && typeof field.key === 'string')
|
|
245
|
+
.map(({ key, label }) => ({
|
|
246
|
+
label,
|
|
247
|
+
value: client[key],
|
|
248
|
+
}))
|
|
249
|
+
.filter(
|
|
250
|
+
(row) =>
|
|
251
|
+
row.value !== null &&
|
|
252
|
+
row.value !== undefined &&
|
|
253
|
+
String(row.value).trim() !== ''
|
|
254
|
+
);
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* The demo card's client block, built from the same list the real card draws
|
|
259
|
+
* from — so a host passing its own fields sees its own fields in the demo
|
|
260
|
+
* rather than another CRM's. An entry carrying no `demo` contributes nothing.
|
|
261
|
+
*/
|
|
262
|
+
export const demoClientDetails = (fields = CLIENT_DETAIL_FIELDS) => {
|
|
263
|
+
const list = Array.isArray(fields) ? fields : CLIENT_DETAIL_FIELDS;
|
|
264
|
+
|
|
265
|
+
return list.reduce((carry, field) => {
|
|
266
|
+
if (!field || typeof field.key !== 'string') {
|
|
267
|
+
return carry;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (field.demo === undefined) {
|
|
271
|
+
return carry;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return { ...carry, [field.key]: field.demo };
|
|
275
|
+
}, {});
|
|
239
276
|
};
|
|
240
277
|
|
|
241
278
|
/** Seconds since `startedAt` rendered as m:ss (never negative). */
|
|
@@ -715,11 +715,25 @@ body.tablet-mode .InovuaReactDataGrid__cell.cell-word-wrap > div {
|
|
|
715
715
|
.InovuaReactDataGrid__cell {
|
|
716
716
|
border: 0 !important;
|
|
717
717
|
border-bottom: 1px solid var(--dgx-line) !important;
|
|
718
|
-
background: transparent !important;
|
|
719
718
|
color: var(--dgx-ink) !important;
|
|
720
719
|
font-size: 0.8125rem !important;
|
|
721
720
|
}
|
|
722
721
|
|
|
722
|
+
/* The same story as the row surface above, one level down. A per-column
|
|
723
|
+
`cellColours` tint arrives as an INLINE background on the CELL, and inline
|
|
724
|
+
loses to `!important` — so a blanket transparent cell would erase every
|
|
725
|
+
column tint a view configures. Cells carrying one are marked
|
|
726
|
+
`vs-datagrid--cell-tinted` (written by the column's className in
|
|
727
|
+
DataGrid.jsx) and this rule stands down for them; nothing else here paints
|
|
728
|
+
a cell, so the inline tint wins over the vendored theme's non-important
|
|
729
|
+
surface.
|
|
730
|
+
Hover and selected stay untouched because they are declared on the ROW and
|
|
731
|
+
the row's cell wrap, which sit BELOW the cell — the tint paints over them
|
|
732
|
+
and stays visible on hover, which is the point of tinting the cell. */
|
|
733
|
+
.InovuaReactDataGrid__cell:not(.vs-datagrid--cell-tinted) {
|
|
734
|
+
background: transparent !important;
|
|
735
|
+
}
|
|
736
|
+
|
|
723
737
|
.InovuaReactDataGrid__cell__content {
|
|
724
738
|
padding: 0 0.85rem !important;
|
|
725
739
|
line-height: 1.45 !important;
|
package/src/index.js
CHANGED
|
@@ -141,6 +141,13 @@ import {
|
|
|
141
141
|
import CallQueuePop from './components/callQueue/CallQueuePop';
|
|
142
142
|
import CallQueueSettings from './components/callQueue/CallQueueSettings';
|
|
143
143
|
import CallQueueDiagnostics from './components/callQueue/CallQueueDiagnostics';
|
|
144
|
+
// The client-block field list and its two readers, so a host can build its own
|
|
145
|
+
// `clientDetailFields` beside the default rather than restating the shape.
|
|
146
|
+
import {
|
|
147
|
+
CLIENT_DETAIL_FIELDS,
|
|
148
|
+
clientDetails,
|
|
149
|
+
demoClientDetails,
|
|
150
|
+
} from './components/callQueue/callQueueHelpers';
|
|
144
151
|
import {
|
|
145
152
|
CALL_POP_STATUS_EVENT,
|
|
146
153
|
getCallPopStatus,
|
|
@@ -365,6 +372,9 @@ export {
|
|
|
365
372
|
CallQueueDiagnostics,
|
|
366
373
|
CallQueuePop,
|
|
367
374
|
CallQueueSettings,
|
|
375
|
+
CLIENT_DETAIL_FIELDS,
|
|
376
|
+
clientDetails,
|
|
377
|
+
demoClientDetails,
|
|
368
378
|
getCallPopStatus,
|
|
369
379
|
updateCallPopStatus,
|
|
370
380
|
cleanBase32,
|