@visns-studio/visns-components 6.6.4 → 6.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1623 -18
- package/package.json +10 -4
- package/src/components/DataGrid.jsx +181 -38
- package/src/components/Fetch.jsx +80 -3
- package/src/components/Form.jsx +9 -0
- package/src/components/Navigation.jsx +109 -50
- package/src/components/Notification.jsx +279 -9
- package/src/components/TableFilter.jsx +14 -1
- package/src/components/auth/AuthBrandPanel.jsx +43 -0
- package/src/components/auth/AuthLoading.jsx +78 -0
- package/src/components/auth/AuthShell.jsx +59 -0
- package/src/components/auth/ClientAuth.jsx +161 -0
- package/src/components/auth/ClientAuthFrame.jsx +59 -0
- package/src/components/auth/ClientLogin.jsx +266 -55
- package/src/components/auth/ClientOTPVerify.jsx +587 -115
- package/src/components/auth/ImpersonateGate.jsx +254 -0
- package/src/components/auth/Login.jsx +134 -41
- package/src/components/auth/LogoutScreen.jsx +112 -0
- package/src/components/auth/Reset.jsx +134 -77
- package/src/components/auth/TwoFactorAuth.jsx +475 -297
- package/src/components/auth/Verify.jsx +237 -126
- package/src/components/auth/authEndpoints.js +105 -0
- package/src/components/auth/authFont.js +23 -0
- package/src/components/auth/authHelpers.js +465 -0
- package/src/components/auth/clientAuthProtocols.js +240 -0
- package/src/components/auth/useOptionalRouter.js +49 -0
- package/src/components/callQueue/CallQueuePop.jsx +1502 -0
- package/src/components/callQueue/CallQueueSettings.jsx +508 -0
- package/src/components/callQueue/callQueueHelpers.js +280 -0
- package/src/components/callQueue/callQueueSettingsHelpers.js +75 -0
- package/src/components/columns/ColumnRenderers.jsx +53 -7
- package/src/components/generic/GenericAuth.jsx +163 -96
- package/src/components/generic/GenericDetail.jsx +215 -90
- package/src/components/generic/GenericIndex.jsx +20 -47
- package/src/components/generic/GenericMain.jsx +5 -0
- package/src/components/generic/GroupedReportRenderer.jsx +1 -5
- package/src/components/generic/StandardModal.jsx +17 -5
- package/src/components/generic/reportSemanticSteps/SemanticEntityStep.jsx +1 -6
- package/src/components/generic/reportSemanticSteps/SemanticFieldsStep.jsx +4 -11
- package/src/components/generic/reportSemanticSteps/SemanticFiltersStep.jsx +9 -27
- package/src/components/generic/reportSemanticSteps/SemanticGroupingStep.jsx +5 -13
- package/src/components/generic/reportSemanticSteps/SemanticParameterPrompt.jsx +5 -13
- package/src/components/generic/reportSemanticSteps/SemanticPreviewStep.jsx +14 -31
- package/src/components/generic/reportSemanticSteps/SemanticRelationsStep.jsx +3 -9
- package/src/components/generic/reportSemanticSteps/SemanticValueInput.jsx +2 -15
- package/src/components/navActive.js +60 -0
- package/src/components/notify/desktopNotifications.js +256 -0
- package/src/components/sketch/SketchField.jsx +12 -2
- package/src/components/sms/SmsComposeModal.jsx +495 -0
- package/src/components/sms/SmsInbox.jsx +596 -0
- package/src/components/sms/SmsInboxBadge.jsx +468 -0
- package/src/components/sms/SmsLineSettings.jsx +854 -0
- package/src/components/sms/SmsThreadPanel.jsx +1184 -0
- package/src/components/sms/smsEndpoints.js +56 -0
- package/src/components/sms/smsHelpers.js +1075 -0
- package/src/components/sms/smsLiveState.js +132 -0
- package/src/components/sms/useSmsLive.js +307 -0
- package/src/components/styles/CallQueuePop.module.scss +646 -0
- package/src/components/styles/CallQueueSettings.module.scss +460 -0
- package/src/components/styles/ClientAuth.module.scss +229 -0
- package/src/components/styles/GenericDetail.module.scss +153 -26
- package/src/components/styles/GenericIndex.module.scss +13 -0
- package/src/components/styles/GenericMain.module.scss +19 -2
- package/src/components/styles/ImpersonateGate.module.scss +85 -0
- package/src/components/styles/Login.module.scss +13 -122
- package/src/components/styles/LogoutScreen.module.scss +89 -0
- package/src/components/styles/Navigation.module.scss +509 -223
- package/src/components/styles/Notification.module.scss +103 -0
- package/src/components/styles/Reset.module.scss +52 -186
- package/src/components/styles/Sms.module.scss +2382 -0
- package/src/components/styles/TableFilter.module.scss +118 -98
- package/src/components/styles/TwoFactorAuth.module.scss +115 -176
- package/src/components/styles/Vault.module.scss +1983 -0
- package/src/components/styles/Verify.module.scss +57 -180
- package/src/components/styles/_authBrandPanel.scss +163 -0
- package/src/components/styles/_authShell.scss +369 -0
- package/src/components/styles/global-datagrid.css +27 -2
- package/src/components/utils/buildEnv.js +67 -0
- package/src/components/utils/displayValue.js +94 -0
- package/src/components/utils/useDensity.js +345 -9
- package/src/components/vault/OtpChip.jsx +208 -0
- package/src/components/vault/PasswordGenerator.jsx +145 -0
- package/src/components/vault/QrScanner.jsx +326 -0
- package/src/components/vault/VaultAccessLog.jsx +225 -0
- package/src/components/vault/VaultConfirmPanel.jsx +113 -0
- package/src/components/vault/VaultEntryForm.jsx +740 -0
- package/src/components/vault/VaultManager.jsx +1000 -0
- package/src/components/vault/VaultQuickSearch.jsx +681 -0
- package/src/components/vault/qrDecode.js +257 -0
- package/src/components/vault/useDebouncedValue.js +22 -0
- package/src/components/vault/useVaultReveal.js +160 -0
- package/src/components/vault/vaultClipboard.js +33 -0
- package/src/components/vault/vaultEndpoints.js +40 -0
- package/src/components/vault/vaultFit.js +116 -0
- package/src/components/vault/vaultHelpers.js +547 -0
- package/src/components/vault/vaultNavigation.jsx +58 -0
- package/src/components/vault/vaultOtp.js +105 -0
- package/src/index.js +233 -1
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Call Queue Settings (.cqsettings)
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// The admin table of every Zoom call queue with its pickup code and pop
|
|
5
|
+
// exclusion (components/callQueue/CallQueueSettings.jsx). Sibling of
|
|
6
|
+
// CallQueuePop.module.scss because they are two ends of one feature — the
|
|
7
|
+
// codes edited here are what the pop's Pick up button dials.
|
|
8
|
+
//
|
|
9
|
+
// Ported from the `.cqsettings` half of the CRM's resources/css/_call-queue.scss.
|
|
10
|
+
// The sass `config` variables it used do not exist in this library, so every
|
|
11
|
+
// brand value is a CSS custom property with a neutral fallback.
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
$cqsettings-mono: 'SFMono-Regular', 'Menlo', 'Consolas', monospace;
|
|
15
|
+
|
|
16
|
+
.cqsettings {
|
|
17
|
+
// Local aliases only — every brand value comes from the consuming app's
|
|
18
|
+
// token layer so this library stays reusable.
|
|
19
|
+
--cq-primary: var(--primary-color, #00386c);
|
|
20
|
+
--cq-secondary: var(--secondary-color, #404040);
|
|
21
|
+
--cq-tertiary: var(--tertiary-color, #fff);
|
|
22
|
+
--cq-paragraph: var(--paragraph-color, #2b2b2b);
|
|
23
|
+
--cq-background: var(--background-color, #fff);
|
|
24
|
+
--cq-br: var(--br, 5px);
|
|
25
|
+
--cq-btn-br: var(--btn-br, 5px);
|
|
26
|
+
|
|
27
|
+
// Reused from the pop so the two ends of the feature agree on what "good"
|
|
28
|
+
// and "wrong" look like.
|
|
29
|
+
--cq-ok: rgb(43, 145, 85);
|
|
30
|
+
--cq-bad: rgb(214, 100, 100);
|
|
31
|
+
|
|
32
|
+
// Full-width layout: the queue-name column absorbs the slack so the
|
|
33
|
+
// control cluster keeps its size and sits at the row's end.
|
|
34
|
+
width: 100%;
|
|
35
|
+
padding: 1rem 1.5rem 2rem;
|
|
36
|
+
|
|
37
|
+
// The CRM relied on a global `spin` keyframe from main.scss; this library
|
|
38
|
+
// has none, so the animation is defined at the bottom of this module and
|
|
39
|
+
// the helper class is a module class the JSX references as styles.spin.
|
|
40
|
+
.spin {
|
|
41
|
+
animation: spin 1s linear infinite;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// --- the copy ----------------------------------------------------------
|
|
45
|
+
// One muted line saying what the page is, then one callout for the single
|
|
46
|
+
// caveat that actually changes what an admin has to do. Nothing else.
|
|
47
|
+
.cqsettingsIntro {
|
|
48
|
+
margin: 0 0 0.75rem;
|
|
49
|
+
color: color-mix(in srgb, var(--cq-paragraph) 70%, transparent);
|
|
50
|
+
font-size: 0.82rem;
|
|
51
|
+
line-height: 1.5;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.cqsettingsInfo,
|
|
55
|
+
.cqsettingsWarning {
|
|
56
|
+
display: flex;
|
|
57
|
+
gap: 0.5rem;
|
|
58
|
+
align-items: flex-start;
|
|
59
|
+
// Full container width so the callout's edges line up with the table
|
|
60
|
+
// panel below it instead of stopping mid-page.
|
|
61
|
+
margin: 0 0 1rem;
|
|
62
|
+
padding: 0.55rem 0.75rem;
|
|
63
|
+
border-radius: var(--cq-br);
|
|
64
|
+
font-size: 0.76rem;
|
|
65
|
+
line-height: 1.45;
|
|
66
|
+
|
|
67
|
+
svg {
|
|
68
|
+
flex: none;
|
|
69
|
+
margin-top: 0.12rem;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.cqsettingsInfo {
|
|
74
|
+
border: 1px solid color-mix(in srgb, var(--cq-primary) 18%, transparent);
|
|
75
|
+
background-color: color-mix(in srgb, var(--cq-primary) 5%, transparent);
|
|
76
|
+
color: color-mix(in srgb, var(--cq-paragraph) 80%, transparent);
|
|
77
|
+
|
|
78
|
+
svg {
|
|
79
|
+
color: var(--cq-primary);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.cqsettingsWarning {
|
|
84
|
+
border: 1px solid color-mix(in srgb, var(--cq-bad) 35%, transparent);
|
|
85
|
+
background-color: color-mix(in srgb, var(--cq-bad) 8%, transparent);
|
|
86
|
+
color: color-mix(in srgb, var(--cq-bad) 67%, #000);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// --- the table ---------------------------------------------------------
|
|
90
|
+
// The panel is the scroll container: on a narrow viewport the table slides
|
|
91
|
+
// inside its own frame instead of pushing the page body sideways.
|
|
92
|
+
.cqsettingsPanel {
|
|
93
|
+
border: 1px solid color-mix(in srgb, var(--cq-primary) 14%, transparent);
|
|
94
|
+
border-radius: var(--cq-br);
|
|
95
|
+
background: var(--cq-background);
|
|
96
|
+
overflow-x: auto;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.cqsettingsState {
|
|
100
|
+
display: flex;
|
|
101
|
+
gap: 0.5rem;
|
|
102
|
+
align-items: center;
|
|
103
|
+
padding: 1.25rem;
|
|
104
|
+
color: color-mix(in srgb, var(--cq-paragraph) 60%, transparent);
|
|
105
|
+
font-size: 0.82rem;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.cqsettingsTable {
|
|
109
|
+
width: 100%;
|
|
110
|
+
// Below this the controls start colliding — the panel scrolls instead.
|
|
111
|
+
min-width: 940px;
|
|
112
|
+
border-collapse: collapse;
|
|
113
|
+
table-layout: fixed;
|
|
114
|
+
font-size: 0.82rem;
|
|
115
|
+
|
|
116
|
+
th,
|
|
117
|
+
td {
|
|
118
|
+
padding: 0.6rem 1rem;
|
|
119
|
+
text-align: left;
|
|
120
|
+
vertical-align: middle;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.isCentred {
|
|
124
|
+
text-align: center;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
thead th {
|
|
128
|
+
border-bottom: 1px solid
|
|
129
|
+
color-mix(in srgb, var(--cq-primary) 18%, transparent);
|
|
130
|
+
background: #f0f0f0;
|
|
131
|
+
color: var(--cq-primary);
|
|
132
|
+
font-size: 0.68rem;
|
|
133
|
+
font-weight: 700;
|
|
134
|
+
letter-spacing: 0.06em;
|
|
135
|
+
text-transform: uppercase;
|
|
136
|
+
white-space: nowrap;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
tbody tr {
|
|
140
|
+
border-bottom: 1px solid
|
|
141
|
+
color-mix(in srgb, var(--cq-secondary) 12%, transparent);
|
|
142
|
+
transition: background 0.12s ease;
|
|
143
|
+
|
|
144
|
+
&:last-child {
|
|
145
|
+
border-bottom: none;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
&:hover {
|
|
149
|
+
background: color-mix(in srgb, var(--cq-primary) 3.5%, transparent);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// An excluded queue never pops; the row reads as switched off.
|
|
153
|
+
&.isExcluded {
|
|
154
|
+
background: color-mix(in srgb, var(--cq-secondary) 3.5%, transparent);
|
|
155
|
+
|
|
156
|
+
.cqsettingsName,
|
|
157
|
+
.cqsettingsNumber {
|
|
158
|
+
color: color-mix(in srgb, var(--cq-paragraph) 55%, transparent);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// The first column has no width: with `table-layout: fixed` it takes
|
|
165
|
+
// whatever the sized columns leave, so the queue name is the only thing
|
|
166
|
+
// that flexes.
|
|
167
|
+
.cqsettingsColNumber {
|
|
168
|
+
width: 170px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.cqsettingsColStatus {
|
|
172
|
+
width: 120px;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.cqsettingsColCode {
|
|
176
|
+
width: 190px;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.cqsettingsColPop {
|
|
180
|
+
width: 170px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.cqsettingsColExclude {
|
|
184
|
+
width: 110px;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.cqsettingsColSave {
|
|
188
|
+
width: 130px;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// --- the queue cell ----------------------------------------------------
|
|
192
|
+
.cqsettingsName {
|
|
193
|
+
display: flex;
|
|
194
|
+
gap: 0.4rem;
|
|
195
|
+
align-items: center;
|
|
196
|
+
min-width: 0;
|
|
197
|
+
color: var(--cq-paragraph);
|
|
198
|
+
font-weight: 600;
|
|
199
|
+
|
|
200
|
+
// Queue names come from Zoom admin and can run long; one line, clipped.
|
|
201
|
+
> span {
|
|
202
|
+
overflow: hidden;
|
|
203
|
+
text-overflow: ellipsis;
|
|
204
|
+
white-space: nowrap;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Gives the phone glyph something to sit in so the name has a left edge to
|
|
209
|
+
// hang off rather than floating in the cell.
|
|
210
|
+
.cqsettingsIcon {
|
|
211
|
+
display: inline-flex;
|
|
212
|
+
flex: none;
|
|
213
|
+
align-items: center;
|
|
214
|
+
justify-content: center;
|
|
215
|
+
width: 22px;
|
|
216
|
+
height: 22px;
|
|
217
|
+
border-radius: var(--cq-br);
|
|
218
|
+
background: color-mix(in srgb, var(--cq-primary) 10%, transparent);
|
|
219
|
+
color: var(--cq-primary);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Extension (and the "gone from Zoom" flag) as a quiet second line, indented
|
|
223
|
+
// to the name's own left edge — 22px badge + the 0.4rem gap.
|
|
224
|
+
.cqsettingsMeta {
|
|
225
|
+
display: block;
|
|
226
|
+
margin-top: 0.1rem;
|
|
227
|
+
padding-left: 1.775rem;
|
|
228
|
+
color: color-mix(in srgb, var(--cq-paragraph) 50%, transparent);
|
|
229
|
+
font-size: 0.68rem;
|
|
230
|
+
line-height: 1.4;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.cqsettingsFlag {
|
|
234
|
+
color: color-mix(in srgb, var(--cq-bad) 82%, #000);
|
|
235
|
+
font-weight: 600;
|
|
236
|
+
|
|
237
|
+
&::before {
|
|
238
|
+
content: '·';
|
|
239
|
+
margin: 0 0.3rem;
|
|
240
|
+
color: color-mix(in srgb, var(--cq-paragraph) 35%, transparent);
|
|
241
|
+
font-weight: 400;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.cqsettingsNumber {
|
|
246
|
+
color: var(--cq-paragraph);
|
|
247
|
+
font-variant-numeric: tabular-nums;
|
|
248
|
+
white-space: nowrap;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.cqsettingsNone {
|
|
252
|
+
color: color-mix(in srgb, var(--cq-paragraph) 30%, transparent);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// --- pills -------------------------------------------------------------
|
|
256
|
+
// One shape for both status columns: muted by default, tinted green only
|
|
257
|
+
// when the answer is "yes, this is working".
|
|
258
|
+
.cqsettingsPill {
|
|
259
|
+
display: inline-flex;
|
|
260
|
+
align-items: center;
|
|
261
|
+
padding: 0.15rem 0.5rem;
|
|
262
|
+
border-radius: 999px;
|
|
263
|
+
background: color-mix(in srgb, var(--cq-secondary) 12%, transparent);
|
|
264
|
+
color: color-mix(in srgb, var(--cq-paragraph) 60%, transparent);
|
|
265
|
+
font-size: 0.68rem;
|
|
266
|
+
font-weight: 700;
|
|
267
|
+
letter-spacing: 0.02em;
|
|
268
|
+
line-height: 1.5;
|
|
269
|
+
white-space: nowrap;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.cqsettingsPillOk {
|
|
273
|
+
background: color-mix(in srgb, var(--cq-ok) 15%, transparent);
|
|
274
|
+
color: color-mix(in srgb, var(--cq-ok) 88%, #000);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// --- pickup code -------------------------------------------------------
|
|
278
|
+
// An input group, not a text box: the `*` adornment IS the dialled form, so
|
|
279
|
+
// the row no longer needs a "Dialled as *6439" line under it.
|
|
280
|
+
.cqsettingsCode {
|
|
281
|
+
display: inline-flex;
|
|
282
|
+
align-items: stretch;
|
|
283
|
+
// Wide enough for the *99 prefix cell plus four spaced monospace
|
|
284
|
+
// digits without clipping.
|
|
285
|
+
width: 150px;
|
|
286
|
+
border: 1px solid color-mix(in srgb, var(--cq-secondary) 35%, transparent);
|
|
287
|
+
border-radius: var(--cq-br);
|
|
288
|
+
background: var(--cq-background);
|
|
289
|
+
overflow: hidden;
|
|
290
|
+
transition:
|
|
291
|
+
border-color 0.15s ease,
|
|
292
|
+
box-shadow 0.15s ease;
|
|
293
|
+
|
|
294
|
+
&:focus-within {
|
|
295
|
+
border-color: var(--cq-primary);
|
|
296
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--cq-primary) 12%, transparent);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
&.hasError {
|
|
300
|
+
border-color: var(--cq-bad);
|
|
301
|
+
|
|
302
|
+
&:focus-within {
|
|
303
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--cq-bad) 15%, transparent);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.cqsettingsCodePrefix {
|
|
309
|
+
display: flex;
|
|
310
|
+
flex: none;
|
|
311
|
+
align-items: center;
|
|
312
|
+
padding: 0 0.45rem;
|
|
313
|
+
border-right: 1px solid
|
|
314
|
+
color-mix(in srgb, var(--cq-secondary) 22%, transparent);
|
|
315
|
+
background: color-mix(in srgb, var(--cq-secondary) 7%, transparent);
|
|
316
|
+
color: color-mix(in srgb, var(--cq-paragraph) 60%, transparent);
|
|
317
|
+
font-family: $cqsettings-mono;
|
|
318
|
+
font-size: 0.85rem;
|
|
319
|
+
font-weight: 700;
|
|
320
|
+
line-height: 1;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.cqsettingsCodeInput {
|
|
324
|
+
width: 100%;
|
|
325
|
+
min-width: 0;
|
|
326
|
+
padding: 0.32rem 0.45rem;
|
|
327
|
+
border: none;
|
|
328
|
+
background: none;
|
|
329
|
+
color: var(--cq-paragraph);
|
|
330
|
+
font-family: $cqsettings-mono;
|
|
331
|
+
font-size: 0.85rem;
|
|
332
|
+
font-weight: 600;
|
|
333
|
+
letter-spacing: 0.12em;
|
|
334
|
+
|
|
335
|
+
&:focus {
|
|
336
|
+
outline: none;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
&::placeholder {
|
|
340
|
+
color: color-mix(in srgb, var(--cq-paragraph) 28%, transparent);
|
|
341
|
+
font-weight: 400;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Only rendered while the typed code breaks a Zoom rule, so it can afford to
|
|
346
|
+
// add a line to the row.
|
|
347
|
+
.cqsettingsError {
|
|
348
|
+
display: block;
|
|
349
|
+
max-width: 120px;
|
|
350
|
+
margin-top: 0.2rem;
|
|
351
|
+
color: color-mix(in srgb, var(--cq-bad) 73%, #000);
|
|
352
|
+
font-size: 0.66rem;
|
|
353
|
+
line-height: 1.3;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// --- exclude switch ----------------------------------------------------
|
|
357
|
+
// react-toggle is the switch the rest of the CRM uses; scaled down to table
|
|
358
|
+
// size and recoloured to the house navy, because "on" here means "a setting
|
|
359
|
+
// is applied", not "all good". react-toggle owns those class names, so they
|
|
360
|
+
// stay global inside this module.
|
|
361
|
+
.cqsettingsToggle {
|
|
362
|
+
display: inline-flex;
|
|
363
|
+
align-items: center;
|
|
364
|
+
justify-content: center;
|
|
365
|
+
height: 24px;
|
|
366
|
+
|
|
367
|
+
:global(.react-toggle) {
|
|
368
|
+
transform: scale(0.75);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// react-toggle's default unchecked track is near-black (#4D4D4D),
|
|
372
|
+
// which reads as "on". Off must be unmistakably light.
|
|
373
|
+
:global(.react-toggle .react-toggle-track) {
|
|
374
|
+
background-color: color-mix(in srgb, var(--cq-secondary) 35%, transparent);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
:global(
|
|
378
|
+
.react-toggle:hover:not(.react-toggle--disabled):not(.react-toggle--checked)
|
|
379
|
+
.react-toggle-track
|
|
380
|
+
) {
|
|
381
|
+
background-color: color-mix(in srgb, var(--cq-secondary) 50%, transparent);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
:global(.react-toggle--checked .react-toggle-track) {
|
|
385
|
+
background-color: var(--cq-primary);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
:global(
|
|
389
|
+
.react-toggle--checked:hover:not(.react-toggle--disabled)
|
|
390
|
+
.react-toggle-track
|
|
391
|
+
) {
|
|
392
|
+
background-color: color-mix(in srgb, var(--cq-primary) 91%, #000);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
:global(.react-toggle--checked .react-toggle-thumb) {
|
|
396
|
+
border-color: var(--cq-primary);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
:global(.react-toggle--focus .react-toggle-thumb),
|
|
400
|
+
:global(.react-toggle:active:not(.react-toggle--disabled) .react-toggle-thumb) {
|
|
401
|
+
box-shadow: 0 0 2px 3px color-mix(in srgb, var(--cq-primary) 35%, transparent);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// --- save --------------------------------------------------------------
|
|
406
|
+
// Primary colour while there is something to save, visibly muted (not just
|
|
407
|
+
// faded) when the row is clean, so "nothing to do" never reads as "broken".
|
|
408
|
+
.cqsettingsSave {
|
|
409
|
+
display: inline-flex;
|
|
410
|
+
align-items: center;
|
|
411
|
+
justify-content: center;
|
|
412
|
+
gap: 0.35rem;
|
|
413
|
+
padding: 0.34rem 0.65rem;
|
|
414
|
+
border: 1px solid color-mix(in srgb, var(--cq-primary) 91%, #000);
|
|
415
|
+
border-radius: var(--cq-btn-br);
|
|
416
|
+
background: var(--cq-primary);
|
|
417
|
+
color: var(--cq-tertiary);
|
|
418
|
+
font-family: inherit;
|
|
419
|
+
font-size: 0.74rem;
|
|
420
|
+
font-weight: 700;
|
|
421
|
+
line-height: 1.4;
|
|
422
|
+
cursor: pointer;
|
|
423
|
+
transition:
|
|
424
|
+
background 0.15s ease,
|
|
425
|
+
border-color 0.15s ease,
|
|
426
|
+
color 0.15s ease;
|
|
427
|
+
|
|
428
|
+
svg {
|
|
429
|
+
flex: none;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
&:hover:not(:disabled) {
|
|
433
|
+
background: color-mix(in srgb, var(--cq-primary) 88%, #000);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
&:focus-visible {
|
|
437
|
+
outline: 2px solid color-mix(in srgb, var(--cq-primary) 45%, transparent);
|
|
438
|
+
outline-offset: 1px;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
&:disabled {
|
|
442
|
+
border-color: color-mix(in srgb, var(--cq-secondary) 20%, transparent);
|
|
443
|
+
background: color-mix(in srgb, var(--cq-secondary) 7%, transparent);
|
|
444
|
+
color: color-mix(in srgb, var(--cq-paragraph) 40%, transparent);
|
|
445
|
+
cursor: default;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// The CRM had this globally in main.scss; the library ships it with the module
|
|
451
|
+
// that needs it so `.spin` is never an animation-less no-op.
|
|
452
|
+
@keyframes spin {
|
|
453
|
+
from {
|
|
454
|
+
transform: rotate(0deg);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
to {
|
|
458
|
+
transform: rotate(360deg);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
@use './authBrandPanel' as brand;
|
|
2
|
+
|
|
1
3
|
// Client Auth styles
|
|
2
4
|
.container {
|
|
3
5
|
min-height: 100vh;
|
|
@@ -165,3 +167,230 @@
|
|
|
165
167
|
color: var(--secondary-color, #3cbf7d);
|
|
166
168
|
}
|
|
167
169
|
}
|
|
170
|
+
|
|
171
|
+
// --- 6.7.0 additions, all rendered only behind an opt-in prop ---------------
|
|
172
|
+
|
|
173
|
+
// The live "Email address detected" line under the contact field. Quiet: it
|
|
174
|
+
// confirms what the field already shows, it does not instruct.
|
|
175
|
+
.inputHint {
|
|
176
|
+
margin: 0.4rem 0 0;
|
|
177
|
+
font-size: 0.8rem;
|
|
178
|
+
line-height: 1.4;
|
|
179
|
+
color: #888;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Development-only OTP panel. Deliberately loud — it must be impossible to
|
|
183
|
+
// mistake a build showing this for a production one.
|
|
184
|
+
.devOtp {
|
|
185
|
+
display: flex;
|
|
186
|
+
flex-wrap: wrap;
|
|
187
|
+
gap: 0.5rem;
|
|
188
|
+
align-items: center;
|
|
189
|
+
justify-content: space-between;
|
|
190
|
+
margin-top: 0.6rem;
|
|
191
|
+
padding: 0.5rem 0.7rem;
|
|
192
|
+
border: 1px dashed #e0a800;
|
|
193
|
+
border-radius: 4px;
|
|
194
|
+
background-color: #fff8e1;
|
|
195
|
+
color: #7a5b00;
|
|
196
|
+
font-size: 0.82rem;
|
|
197
|
+
|
|
198
|
+
strong {
|
|
199
|
+
font-family: 'SFMono-Regular', 'Menlo', 'Consolas', monospace;
|
|
200
|
+
letter-spacing: 0.04em;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.devOtpFill {
|
|
205
|
+
padding: 0.25rem 0.6rem;
|
|
206
|
+
border: 1px solid #e0a800;
|
|
207
|
+
border-radius: 4px;
|
|
208
|
+
background-color: #ffe9a8;
|
|
209
|
+
color: #7a5b00;
|
|
210
|
+
font-family: inherit;
|
|
211
|
+
font-size: 0.78rem;
|
|
212
|
+
font-weight: 600;
|
|
213
|
+
cursor: pointer;
|
|
214
|
+
transition: background-color 0.2s ease;
|
|
215
|
+
|
|
216
|
+
&:hover {
|
|
217
|
+
background-color: #ffdf85;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// The read-only contact echo above the code field (`showContactField`), and
|
|
222
|
+
// its masked caption. Muted rather than greyed to unreadability: it is there to
|
|
223
|
+
// be read, it is simply not there to be edited.
|
|
224
|
+
.disabledInput {
|
|
225
|
+
background-color: #f4f4f5;
|
|
226
|
+
color: #666;
|
|
227
|
+
cursor: default;
|
|
228
|
+
|
|
229
|
+
&:disabled {
|
|
230
|
+
// Safari and Firefox each fade a disabled input differently; stating
|
|
231
|
+
// the colour keeps the value legible in both.
|
|
232
|
+
opacity: 1;
|
|
233
|
+
-webkit-text-fill-color: #666;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.contactInfo {
|
|
238
|
+
margin: 0.4rem 0 0;
|
|
239
|
+
font-size: 0.8rem;
|
|
240
|
+
line-height: 1.4;
|
|
241
|
+
color: #888;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
// Split layout (`layout="split"`)
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
// The card layout above floats a small box in whatever vertical space the host
|
|
249
|
+
// page gives it, which on the portal meant a login card marooned in a tall
|
|
250
|
+
// empty band. This is the staff sign-in's design language brought across: the
|
|
251
|
+
// brand on one side, the task on the other.
|
|
252
|
+
//
|
|
253
|
+
// It is a SECTION, not a screen. The staff `.auth` is `position: fixed;
|
|
254
|
+
// inset: 0` because it owns the whole viewport; this one cannot be, because the
|
|
255
|
+
// consumer keeps its own header and footer and hands this the space between.
|
|
256
|
+
// Hence `height: 100%` with a `flex: 1` fallback and no `100vh` anywhere — the
|
|
257
|
+
// parent decides how tall this is.
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
|
|
260
|
+
.split {
|
|
261
|
+
// Local aliases, same names and same neutral fallbacks as the staff
|
|
262
|
+
// screens, so one token layer themes the whole product. The brand panel
|
|
263
|
+
// mixin below reads --navy-deep and --flag from here.
|
|
264
|
+
--ink: var(--header-color, #10192a);
|
|
265
|
+
--navy: var(--primary-color, #26364a);
|
|
266
|
+
--navy-deep: var(--primary-color-darker, #16202e);
|
|
267
|
+
--flag: var(--accent-color, #8a8f98);
|
|
268
|
+
--paper: var(--surface-color, #ffffff);
|
|
269
|
+
--warm: var(--hover-color, #f4f4f2);
|
|
270
|
+
--line: var(--border-color, #e4e1dc);
|
|
271
|
+
--muted: var(--muted-color, #6b7688);
|
|
272
|
+
|
|
273
|
+
display: grid;
|
|
274
|
+
// Asymmetric on purpose. A 50/50 split reads as a template; giving the
|
|
275
|
+
// photograph the larger share makes it the subject and the form the task.
|
|
276
|
+
grid-template-columns: 52fr 48fr;
|
|
277
|
+
width: 100%;
|
|
278
|
+
// Fill the parent, however the parent is laid out: `height` for a sized
|
|
279
|
+
// block, `flex` for a flex column, `align-self` so a stretch container does
|
|
280
|
+
// not centre it. `min-height` keeps it honest when the parent has no
|
|
281
|
+
// height of its own to give.
|
|
282
|
+
height: 100%;
|
|
283
|
+
min-height: 24rem;
|
|
284
|
+
flex: 1 1 auto;
|
|
285
|
+
align-self: stretch;
|
|
286
|
+
background: var(--paper);
|
|
287
|
+
font-family: var(--auth-font-family, 'Barlow', system-ui, sans-serif);
|
|
288
|
+
color: var(--ink);
|
|
289
|
+
|
|
290
|
+
@media (max-width: 900px) {
|
|
291
|
+
// The brand becomes a header band rather than disappearing: the portal
|
|
292
|
+
// is a public front door, and on a phone it is the only thing telling
|
|
293
|
+
// the visitor whose front door it is.
|
|
294
|
+
grid-template-columns: 1fr;
|
|
295
|
+
grid-template-rows: auto 1fr;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
@include brand.auth-brand-panel(band);
|
|
300
|
+
@include brand.auth-rise-keyframes;
|
|
301
|
+
|
|
302
|
+
// The band treatment the mixin leaves to the consumer: the eyebrow and the
|
|
303
|
+
// tagline are the first things to go, because at that width they are two more
|
|
304
|
+
// lines between the visitor and the field they came to fill in.
|
|
305
|
+
@media (max-width: 900px) {
|
|
306
|
+
.brandInk {
|
|
307
|
+
left: 1.5rem;
|
|
308
|
+
right: 1.5rem;
|
|
309
|
+
bottom: 1.25rem;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.eyebrow,
|
|
313
|
+
.brandSub {
|
|
314
|
+
display: none;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.brandLine {
|
|
318
|
+
margin: 0;
|
|
319
|
+
font-size: clamp(1.5rem, 6vw, 2rem);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// The form side. Scrolls internally rather than growing the section, so a tall
|
|
324
|
+
// step-two — contact field, code field, dev panel, two links — cannot push the
|
|
325
|
+
// consumer's footer off the bottom of the page.
|
|
326
|
+
.splitPane {
|
|
327
|
+
display: flex;
|
|
328
|
+
align-items: center;
|
|
329
|
+
justify-content: center;
|
|
330
|
+
padding: 2rem clamp(1.5rem, 5vw, 4.5rem);
|
|
331
|
+
background: var(--paper);
|
|
332
|
+
overflow-y: auto;
|
|
333
|
+
min-width: 0;
|
|
334
|
+
|
|
335
|
+
@media (max-width: 900px) {
|
|
336
|
+
// The warm neutral stops the stacked screen being a blank white sheet.
|
|
337
|
+
background: linear-gradient(var(--warm), var(--paper) 22rem);
|
|
338
|
+
padding: 2rem 1.5rem 2.5rem;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Holds exactly what the card layout holds — same children, same props, same
|
|
343
|
+
// form. Only the frame around them changed.
|
|
344
|
+
.splitCard {
|
|
345
|
+
width: 100%;
|
|
346
|
+
max-width: 24rem;
|
|
347
|
+
// Left aligned, not centred. Centred text over a form is a template look,
|
|
348
|
+
// and the eye has to travel back and forth to read labels and fields.
|
|
349
|
+
text-align: left;
|
|
350
|
+
animation: rise 0.55s cubic-bezier(0.2, 0.7, 0.3, 1) both;
|
|
351
|
+
animation-delay: 0.08s;
|
|
352
|
+
|
|
353
|
+
// The card layout centres its logo, title and subtitle; this one does not,
|
|
354
|
+
// so those three are re-aligned here rather than being made conditional in
|
|
355
|
+
// half a dozen places in the JSX.
|
|
356
|
+
.logo {
|
|
357
|
+
max-width: 190px;
|
|
358
|
+
margin: 0 0 2rem;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.title {
|
|
362
|
+
text-align: left;
|
|
363
|
+
font-size: 1.9rem;
|
|
364
|
+
letter-spacing: -0.02em;
|
|
365
|
+
line-height: 1.1;
|
|
366
|
+
color: var(--ink);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.subtitle {
|
|
370
|
+
text-align: left;
|
|
371
|
+
color: var(--muted);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// The masked-destination line under a disabled contact field, and the live
|
|
375
|
+
// contact-type hint, both read as captions here rather than centred notes.
|
|
376
|
+
.inputHint,
|
|
377
|
+
.contactInfo {
|
|
378
|
+
text-align: left;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// The sign-off, when `branding.footer` is set. Same treatment as the staff
|
|
383
|
+
// panel's, at the foot of the form column.
|
|
384
|
+
.splitFoot {
|
|
385
|
+
margin: 2.5rem 0 0;
|
|
386
|
+
font-size: 0.78rem;
|
|
387
|
+
line-height: 1.5;
|
|
388
|
+
color: var(--muted);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
@media (prefers-reduced-motion: reduce) {
|
|
392
|
+
.brandInk,
|
|
393
|
+
.splitCard {
|
|
394
|
+
animation: none;
|
|
395
|
+
}
|
|
396
|
+
}
|