@visns-studio/visns-components 6.17.2 → 6.20.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/package.json +10 -7
- package/src/components/AssociationManager.jsx +344 -177
- package/src/components/AsyncSelect.jsx +2 -2
- package/src/components/Autocomplete.jsx +57 -12
- package/src/components/Breadcrumb.jsx +23 -4
- package/src/components/ClientAssociationManager.jsx +321 -145
- package/src/components/DataGrid.jsx +625 -20
- package/src/components/DropZone.jsx +4 -1
- package/src/components/Form.jsx +233 -6
- package/src/components/MultiSelect.jsx +2 -2
- package/src/components/Navigation.jsx +39 -23
- package/src/components/TableFilter.jsx +172 -12
- package/src/components/UniversalSearch.jsx +390 -0
- package/src/components/auth/Login.jsx +164 -8
- package/src/components/auth/Profile.jsx +209 -22
- package/src/components/auth/authEndpoints.js +4 -0
- package/src/components/auth/webauthn.js +194 -0
- package/src/components/columns/ColumnRenderers.jsx +6 -1
- package/src/components/controls/DataGridSearch.jsx +11 -5
- package/src/components/generic/GenericAuth.jsx +6 -0
- package/src/components/generic/GenericDashboard.jsx +93 -36
- package/src/components/generic/GenericDetail.jsx +146 -61
- package/src/components/generic/GenericIndex.jsx +134 -38
- package/src/components/generic/GenericSettings.jsx +220 -0
- package/src/components/generic/StandardModal.jsx +165 -8
- package/src/components/generic/dashboardChartDefaults.js +230 -0
- package/src/components/generic/shared/indexLayout.js +38 -0
- package/src/components/settings/IntegrationsManager.jsx +474 -0
- package/src/components/sms/SmsLineSettings.jsx +16 -12
- package/src/components/styles/AssociationManager.module.scss +4 -363
- package/src/components/styles/AuditLog.module.scss +6 -6
- package/src/components/styles/AuditLogs.module.scss +3 -3
- package/src/components/styles/Autocomplete.module.scss +7 -7
- package/src/components/styles/BusinessCardOcr.module.scss +6 -1
- package/src/components/styles/CallQueueSettings.module.scss +4 -1
- package/src/components/styles/CameraPlacement.module.scss +19 -19
- package/src/components/styles/CategorizedDropZone.module.scss +6 -1
- package/src/components/styles/ClientAssociationManager.module.scss +5 -289
- package/src/components/styles/ClientAuth.module.scss +1 -1
- package/src/components/styles/DataGrid.module.scss +455 -8
- package/src/components/styles/Field.module.scss +32 -24
- package/src/components/styles/Form.module.scss +123 -3
- package/src/components/styles/GenericDashboard.module.scss +612 -825
- package/src/components/styles/GenericDetail.module.scss +406 -61
- package/src/components/styles/GenericDynamic.module.scss +25 -25
- package/src/components/styles/GenericEditableTable.module.scss +33 -33
- package/src/components/styles/GenericFormBuilder.module.scss +119 -119
- package/src/components/styles/GenericIndex.module.scss +483 -38
- package/src/components/styles/GenericMain.module.scss +6 -3
- package/src/components/styles/GenericQuote.module.scss +399 -81
- package/src/components/styles/GenericReport.module.scss +8 -8
- package/src/components/styles/GenericSettings.module.scss +209 -0
- package/src/components/styles/GenericSort.module.scss +9 -9
- package/src/components/styles/Integrations.module.scss +321 -0
- package/src/components/styles/List.module.scss +4 -4
- package/src/components/styles/Login.module.scss +57 -0
- package/src/components/styles/MultiSelect.module.scss +4 -4
- package/src/components/styles/Navigation.module.scss +12 -12
- package/src/components/styles/Notification.module.scss +12 -8
- package/src/components/styles/NotificationList.module.scss +5 -5
- package/src/components/styles/Profile.module.scss +5 -5
- package/src/components/styles/QrCode.module.scss +4 -4
- package/src/components/styles/QuickAction.module.scss +20 -20
- package/src/components/styles/SortableQuoteItems.module.scss +4 -4
- package/src/components/styles/TableFilter.module.scss +128 -17
- package/src/components/styles/TicketConversation.module.scss +497 -0
- package/src/components/styles/UniversalSearch.module.scss +229 -0
- package/src/components/styles/Vault.module.scss +413 -0
- package/src/components/styles/_associationManager.scss +467 -0
- package/src/components/styles/_authBrandPanel.scss +15 -9
- package/src/components/styles/_controls.scss +43 -39
- package/src/components/styles/global-datagrid.css +196 -0
- package/src/components/styles/global.css +168 -17
- package/src/components/tickets/TicketConversation.jsx +455 -0
- package/src/components/utils/rowActionSettings.js +117 -0
- package/src/components/vault/VaultClientPicker.jsx +183 -0
- package/src/components/vault/VaultEntryForm.jsx +23 -0
- package/src/components/vault/VaultManager.jsx +382 -34
- package/src/components/vault/VaultShareModal.jsx +454 -0
- package/src/components/vault/vaultEndpoints.js +16 -2
- package/src/components/vault/vaultShares.js +247 -0
- package/src/index.js +35 -0
- package/src/utils/fetchUtil.js +38 -14
|
@@ -36,7 +36,12 @@ const VisnsAutocomplete = memo((props) => {
|
|
|
36
36
|
(value) => {
|
|
37
37
|
if (!value || !api) return null;
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
// The search text is a PATH segment, so it has to be escaped: an
|
|
40
|
+
// unencoded "5/12 Smith St" split the path and 404'd, which is a
|
|
41
|
+
// shape half of Australian addresses take.
|
|
42
|
+
let url = `https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(
|
|
43
|
+
value
|
|
44
|
+
)}.json?types=address,poi&access_token=${api}`;
|
|
40
45
|
|
|
41
46
|
if (country) {
|
|
42
47
|
url += `&country=${country}`;
|
|
@@ -149,20 +154,39 @@ const VisnsAutocomplete = memo((props) => {
|
|
|
149
154
|
|
|
150
155
|
const finalValue = displayValue || fallbackValue;
|
|
151
156
|
|
|
152
|
-
setSearch(finalValue);
|
|
153
157
|
setResults([]);
|
|
154
158
|
setIsFocused(false);
|
|
155
159
|
|
|
156
|
-
|
|
160
|
+
/*
|
|
161
|
+
* A form that handles the pick owns this field's value.
|
|
162
|
+
*
|
|
163
|
+
* It splits the place across its own fields — street line,
|
|
164
|
+
* suburb, postcode, state — and only the street line belongs in
|
|
165
|
+
* this input. Writing Mapbox's `place_name` here first left the
|
|
166
|
+
* box showing the whole formatted address, suburb and state
|
|
167
|
+
* included, right next to the fields that had just been filled
|
|
168
|
+
* with those same parts: the field looked like it had mangled
|
|
169
|
+
* the address rather than parsed it.
|
|
170
|
+
*
|
|
171
|
+
* The `inputValue` effect below cannot undo that, because it
|
|
172
|
+
* only fires when the form's value *changes*, and the form's
|
|
173
|
+
* street line is usually exactly what the user typed. So don't
|
|
174
|
+
* write the wrong value in the first place: hand the place to
|
|
175
|
+
* the form and follow whatever it puts back.
|
|
176
|
+
*/
|
|
177
|
+
if (onSelect) {
|
|
178
|
+
onSelect(place, field);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
setSearch(finalValue);
|
|
183
|
+
|
|
184
|
+
// Standalone use (no form to parse the place): the field keeps
|
|
185
|
+
// the full formatted address, which is the only useful value.
|
|
157
186
|
setFormData((prevState) => ({
|
|
158
187
|
...prevState,
|
|
159
188
|
[field]: finalValue,
|
|
160
189
|
}));
|
|
161
|
-
|
|
162
|
-
// Call the onSelect callback with the place object and field
|
|
163
|
-
if (onSelect) {
|
|
164
|
-
onSelect(place, field);
|
|
165
|
-
}
|
|
166
190
|
},
|
|
167
191
|
[field, onSelect, setFormData]
|
|
168
192
|
);
|
|
@@ -176,13 +200,34 @@ const VisnsAutocomplete = memo((props) => {
|
|
|
176
200
|
});
|
|
177
201
|
}, []);
|
|
178
202
|
|
|
179
|
-
//
|
|
203
|
+
// What is in the box, readable without making it a trigger.
|
|
204
|
+
const searchRef = useRef('');
|
|
205
|
+
const isFocusedRef = useRef(false);
|
|
206
|
+
searchRef.current = search;
|
|
207
|
+
isFocusedRef.current = isFocused;
|
|
208
|
+
|
|
209
|
+
/*
|
|
210
|
+
* Switching autocomplete back on searches for whatever is already typed.
|
|
211
|
+
*
|
|
212
|
+
* Deliberately NOT keyed on `search`: typing is already handled by the
|
|
213
|
+
* debounced path, and this effect calls `performSearch` directly. Keyed
|
|
214
|
+
* on `search` it fired once per keystroke and bypassed the 500ms debounce
|
|
215
|
+
* entirely — 21 characters produced 21 Mapbox requests (42 with the CORS
|
|
216
|
+
* preflight each one triggered), where the debounce is there to produce
|
|
217
|
+
* one. Focus is handled by `handleFocus`, so it is not a trigger either.
|
|
218
|
+
*/
|
|
180
219
|
useEffect(() => {
|
|
181
|
-
if (
|
|
220
|
+
if (overwrite) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const value = searchRef.current;
|
|
225
|
+
|
|
226
|
+
if (isFocusedRef.current && value && value.length > 1) {
|
|
182
227
|
setIsLoading(true);
|
|
183
|
-
performSearch(1,
|
|
228
|
+
performSearch(1, value);
|
|
184
229
|
}
|
|
185
|
-
}, [overwrite,
|
|
230
|
+
}, [overwrite, performSearch]);
|
|
186
231
|
|
|
187
232
|
// Effect to sync with inputValue prop
|
|
188
233
|
useEffect(() => {
|
|
@@ -4,6 +4,21 @@ import dayjs from 'dayjs';
|
|
|
4
4
|
import parse from 'html-react-parser';
|
|
5
5
|
import { ChevronRight } from 'lucide-react';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* The page header trail: parent link → page → record name.
|
|
9
|
+
*
|
|
10
|
+
* It stays an `<h1>` on purpose. Three stylesheets — GenericIndex,
|
|
11
|
+
* GenericDetail and GenericFormBuilder — style this element through
|
|
12
|
+
* `.crmtitle h1` descendant selectors, so changing the tag to the `<nav><ol>`
|
|
13
|
+
* a breadcrumb "should" be would silently restyle every index, detail and
|
|
14
|
+
* form-builder page in the app. The semantics are fixed the way that costs
|
|
15
|
+
* nothing instead: a labelled `nav` around it, the separators hidden from
|
|
16
|
+
* assistive tech, and the final crumb marked as the current page.
|
|
17
|
+
*
|
|
18
|
+
* Before this, a screen reader read the whole trail as one heading —
|
|
19
|
+
* "Customers greater-than Acme Pty Ltd" — with the chevrons announced as
|
|
20
|
+
* content and no indication which part was where you actually were.
|
|
21
|
+
*/
|
|
7
22
|
function Breadcrumb({ data, page }) {
|
|
8
23
|
// Function to get nested data, handling array of keys
|
|
9
24
|
const getNestedData = (data, keys) => {
|
|
@@ -69,6 +84,7 @@ function Breadcrumb({ data, page }) {
|
|
|
69
84
|
};
|
|
70
85
|
|
|
71
86
|
return (
|
|
87
|
+
<nav aria-label="Breadcrumb">
|
|
72
88
|
<h1>
|
|
73
89
|
{page?.parentTitle && (
|
|
74
90
|
<>
|
|
@@ -77,7 +93,7 @@ function Breadcrumb({ data, page }) {
|
|
|
77
93
|
) : (
|
|
78
94
|
<span>{parse(page.parentTitle)}</span>
|
|
79
95
|
)}
|
|
80
|
-
<ChevronRight strokeWidth={2} size={
|
|
96
|
+
<ChevronRight strokeWidth={2} size={16} aria-hidden="true" />
|
|
81
97
|
</>
|
|
82
98
|
)}
|
|
83
99
|
|
|
@@ -113,9 +129,11 @@ function Breadcrumb({ data, page }) {
|
|
|
113
129
|
)
|
|
114
130
|
)}
|
|
115
131
|
|
|
132
|
+
{/* The record name is the last crumb whenever there is one, so it
|
|
133
|
+
is the one that carries "you are here". */}
|
|
116
134
|
{page.titleKey && data && (
|
|
117
|
-
|
|
118
|
-
<ChevronRight strokeWidth={2} size={
|
|
135
|
+
<span aria-current="page">
|
|
136
|
+
<ChevronRight strokeWidth={2} size={16} aria-hidden="true" />
|
|
119
137
|
{Array.isArray(page.titleKey) ? (
|
|
120
138
|
<>
|
|
121
139
|
{page.titleText && (
|
|
@@ -145,7 +163,7 @@ function Breadcrumb({ data, page }) {
|
|
|
145
163
|
)}
|
|
146
164
|
</>
|
|
147
165
|
)}
|
|
148
|
-
|
|
166
|
+
</span>
|
|
149
167
|
)}
|
|
150
168
|
|
|
151
169
|
{/* Display image if condition is met */}
|
|
@@ -163,6 +181,7 @@ function Breadcrumb({ data, page }) {
|
|
|
163
181
|
/>
|
|
164
182
|
)}
|
|
165
183
|
</h1>
|
|
184
|
+
</nav>
|
|
166
185
|
);
|
|
167
186
|
}
|
|
168
187
|
|