@visns-studio/visns-components 6.6.3 → 6.15.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 +1658 -18
- package/package.json +10 -4
- package/src/components/DataGrid.jsx +619 -158
- 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/AutoGrowCell.jsx +141 -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/DataGrid.module.scss +10 -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 +29 -0
- 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
|
@@ -1,19 +1,84 @@
|
|
|
1
1
|
import '../styles/global.css';
|
|
2
|
+
import './authFont';
|
|
2
3
|
|
|
3
4
|
import React, { useState } from 'react';
|
|
4
|
-
import { useNavigate, useParams } from 'react-router-dom';
|
|
5
|
+
import { Link, useNavigate, useParams } from 'react-router-dom';
|
|
5
6
|
import { toast } from 'react-toastify';
|
|
6
|
-
import parse from 'html-react-parser';
|
|
7
7
|
import validator from 'validator';
|
|
8
|
-
import
|
|
8
|
+
import { ArrowLeft, Eye, EyeOff } from 'lucide-react';
|
|
9
9
|
|
|
10
10
|
import CustomFetch from '../Fetch';
|
|
11
|
+
import AuthShell from './AuthShell';
|
|
12
|
+
import { resolveAuthEndpoints } from './authEndpoints';
|
|
13
|
+
import { normaliseAuthError } from './authHelpers';
|
|
11
14
|
|
|
12
15
|
import styles from '../styles/Verify.module.scss';
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Set a new password — the second half of the reset journey, reached from the
|
|
19
|
+
* link in the reset email (`/verify/:code`).
|
|
20
|
+
*
|
|
21
|
+
* Redesigned in 6.7.0 onto the same two-pane frame as the rest of the journey.
|
|
22
|
+
* The one behavioural change that came with it: the password rules are now
|
|
23
|
+
* stated up front and tick themselves off as the user types, instead of
|
|
24
|
+
* arriving as a five-line `<br />`-separated toast after a rejected submit. The
|
|
25
|
+
* rules themselves are unchanged — `validator.isStrongPassword`'s defaults, the
|
|
26
|
+
* same function that gates the submit.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The checks `validator.isStrongPassword` applies at its defaults, restated so
|
|
31
|
+
* the screen can show which are already satisfied. This list is display only:
|
|
32
|
+
* the submit is still gated by validator itself, so the two can never disagree
|
|
33
|
+
* about whether a password is acceptable.
|
|
34
|
+
*/
|
|
35
|
+
const PASSWORD_RULES = [
|
|
36
|
+
{ label: 'At least 8 characters', test: (v) => v.length >= 8 },
|
|
37
|
+
{ label: 'One uppercase letter', test: (v) => /[A-Z]/.test(v) },
|
|
38
|
+
{ label: 'One lowercase letter', test: (v) => /[a-z]/.test(v) },
|
|
39
|
+
{ label: 'One number', test: (v) => /\d/.test(v) },
|
|
40
|
+
{ label: 'One symbol', test: (v) => /[^A-Za-z0-9]/.test(v) },
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const DEFAULT_COPY = {
|
|
44
|
+
title: 'Reset your password',
|
|
45
|
+
subtitle: 'Choose a new password for your account.',
|
|
46
|
+
passwordLabel: 'Password',
|
|
47
|
+
repeatLabel: 'Re-type Password',
|
|
48
|
+
submit: 'Set New Password',
|
|
49
|
+
submitting: 'Saving…',
|
|
50
|
+
back: 'Back to Login',
|
|
51
|
+
success:
|
|
52
|
+
'You have successfully set a new password, please use your new password to login into the system.',
|
|
53
|
+
/** Client-side validation. */
|
|
54
|
+
required: 'Please type in your new password.',
|
|
55
|
+
mismatch: 'The password does not match, please re-type the password.',
|
|
56
|
+
weak: 'That password does not meet all of the requirements listed below.',
|
|
57
|
+
failed: 'Could not set a new password, please try again.',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const Verify = ({
|
|
61
|
+
logo,
|
|
62
|
+
loginBg,
|
|
63
|
+
config = {},
|
|
64
|
+
/** Merged over the 6.6.3 literals — see authEndpoints.js. */
|
|
65
|
+
endpoints,
|
|
66
|
+
/** Merged over DEFAULT_COPY above. */
|
|
67
|
+
copy: copyOverrides,
|
|
68
|
+
/** Set false to hide the live rules checklist. */
|
|
69
|
+
showRules = true,
|
|
70
|
+
rules = PASSWORD_RULES,
|
|
71
|
+
loginPath = '/login',
|
|
72
|
+
}) => {
|
|
15
73
|
const navigate = useNavigate();
|
|
16
74
|
const { code } = useParams();
|
|
75
|
+
|
|
76
|
+
const urls = resolveAuthEndpoints(endpoints);
|
|
77
|
+
const copy = { ...DEFAULT_COPY, ...(copyOverrides || {}) };
|
|
78
|
+
const branding = config.branding || {};
|
|
79
|
+
|
|
80
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
81
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
17
82
|
const [formData, setFormData] = useState({
|
|
18
83
|
password: '',
|
|
19
84
|
passwordRepeat: '',
|
|
@@ -26,150 +91,196 @@ const Verify = ({ logo }) => {
|
|
|
26
91
|
|
|
27
92
|
const handleInputChange = (e) => {
|
|
28
93
|
const { name, value } = e.target;
|
|
29
|
-
|
|
30
|
-
|
|
94
|
+
|
|
95
|
+
setFormData((prev) => ({
|
|
96
|
+
...prev,
|
|
31
97
|
[name]: value,
|
|
32
98
|
passwordClass: '',
|
|
33
99
|
passwordRepeatClass: '',
|
|
34
100
|
}));
|
|
35
101
|
};
|
|
36
102
|
|
|
103
|
+
/** Returns an error string, or '' when the pair is acceptable. */
|
|
37
104
|
const validatePassword = () => {
|
|
38
|
-
let _error = '';
|
|
39
|
-
|
|
40
105
|
if (password === '') {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
106
|
+
setFormData((prev) => ({ ...prev, passwordClass: 'inputError' }));
|
|
107
|
+
|
|
108
|
+
return copy.required;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (password !== passwordRepeat) {
|
|
112
|
+
setFormData((prev) => ({
|
|
113
|
+
...prev,
|
|
44
114
|
passwordClass: 'inputError',
|
|
115
|
+
passwordRepeatClass: 'inputError',
|
|
45
116
|
}));
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}));
|
|
55
|
-
} else {
|
|
56
|
-
if (!validator.isStrongPassword(password)) {
|
|
57
|
-
_error =
|
|
58
|
-
'Your password is too weak it must contain the following:<br />- Must contain at least 8 characters.<br />- Must contain at least one uppercase letter.<br />- Must contain at least one lowercase letter.<br />- Must contain at least one number.<br />- Must contain at least one symbol.<br />';
|
|
59
|
-
}
|
|
60
|
-
}
|
|
117
|
+
|
|
118
|
+
return copy.mismatch;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!validator.isStrongPassword(password)) {
|
|
122
|
+
setFormData((prev) => ({ ...prev, passwordClass: 'inputError' }));
|
|
123
|
+
|
|
124
|
+
return copy.weak;
|
|
61
125
|
}
|
|
62
126
|
|
|
63
|
-
return
|
|
127
|
+
return '';
|
|
64
128
|
};
|
|
65
129
|
|
|
66
130
|
const handleSubmit = (e) => {
|
|
67
|
-
if (e) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (errorMsg === '') {
|
|
72
|
-
CustomFetch(
|
|
73
|
-
'/password/reset',
|
|
74
|
-
'POST',
|
|
75
|
-
{
|
|
76
|
-
code: code,
|
|
77
|
-
password: password,
|
|
78
|
-
password_confirmation: passwordRepeat,
|
|
79
|
-
},
|
|
80
|
-
function (result) {
|
|
81
|
-
if (result.error === '') {
|
|
82
|
-
toast.success(
|
|
83
|
-
'You have successfully set a new password, please use your new password to login into the system.'
|
|
84
|
-
);
|
|
131
|
+
if (!e) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
85
134
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
135
|
+
e.preventDefault();
|
|
136
|
+
|
|
137
|
+
// The button is disabled too; this also covers Enter in the field.
|
|
138
|
+
if (isLoading) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const errorMsg = validatePassword();
|
|
143
|
+
|
|
144
|
+
if (errorMsg !== '') {
|
|
145
|
+
toast.error(errorMsg);
|
|
146
|
+
|
|
147
|
+
return;
|
|
95
148
|
}
|
|
149
|
+
|
|
150
|
+
setIsLoading(true);
|
|
151
|
+
|
|
152
|
+
CustomFetch(
|
|
153
|
+
urls.passwordReset,
|
|
154
|
+
'POST',
|
|
155
|
+
{
|
|
156
|
+
code: code,
|
|
157
|
+
password: password,
|
|
158
|
+
password_confirmation: passwordRepeat,
|
|
159
|
+
},
|
|
160
|
+
(result) => {
|
|
161
|
+
// Cleared only on failure: on success this screen navigates
|
|
162
|
+
// away, and a button that returns to "Set New Password" first
|
|
163
|
+
// invites a second submit against a spent reset code.
|
|
164
|
+
const failure =
|
|
165
|
+
(result && typeof result === 'object' && result.error) ||
|
|
166
|
+
'';
|
|
167
|
+
|
|
168
|
+
if (failure) {
|
|
169
|
+
setIsLoading(false);
|
|
170
|
+
toast.error(normaliseAuthError(failure, copy.failed));
|
|
171
|
+
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
toast.success(copy.success);
|
|
176
|
+
navigate(loginPath);
|
|
177
|
+
},
|
|
178
|
+
(error) => {
|
|
179
|
+
setIsLoading(false);
|
|
180
|
+
toast.error(normaliseAuthError(error, copy.failed));
|
|
181
|
+
}
|
|
182
|
+
);
|
|
96
183
|
};
|
|
97
184
|
|
|
98
185
|
return (
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
type="password"
|
|
144
|
-
name="passwordRepeat"
|
|
145
|
-
value={passwordRepeat}
|
|
146
|
-
onChange={handleInputChange}
|
|
147
|
-
tabIndex="2"
|
|
148
|
-
className={passwordRepeatClass}
|
|
149
|
-
/>
|
|
150
|
-
<span className={styles.fi__span}>
|
|
151
|
-
Re-type Password
|
|
152
|
-
</span>
|
|
153
|
-
</label>
|
|
154
|
-
</div>
|
|
155
|
-
<div
|
|
156
|
-
className={`${styles.formItem} ${styles.fwItem} ${styles.lastItem}`}
|
|
157
|
-
>
|
|
158
|
-
<button
|
|
159
|
-
className={styles.btn}
|
|
160
|
-
type="submit"
|
|
161
|
-
tabIndex="3"
|
|
162
|
-
>
|
|
163
|
-
Set New Password
|
|
164
|
-
</button>
|
|
165
|
-
</div>
|
|
166
|
-
</div>
|
|
167
|
-
</Reveal>
|
|
168
|
-
</form>
|
|
186
|
+
<AuthShell
|
|
187
|
+
styles={styles}
|
|
188
|
+
logo={logo}
|
|
189
|
+
loginBg={loginBg}
|
|
190
|
+
branding={branding}
|
|
191
|
+
title={copy.title}
|
|
192
|
+
subtitle={copy.subtitle}
|
|
193
|
+
footer={branding.footer}
|
|
194
|
+
>
|
|
195
|
+
<form onSubmit={handleSubmit} className={styles.form}>
|
|
196
|
+
<div className={styles.field}>
|
|
197
|
+
<input
|
|
198
|
+
id="verify-password"
|
|
199
|
+
type={showPassword ? 'text' : 'password'}
|
|
200
|
+
name="password"
|
|
201
|
+
value={password}
|
|
202
|
+
onChange={handleInputChange}
|
|
203
|
+
tabIndex="1"
|
|
204
|
+
className={passwordClass}
|
|
205
|
+
autoComplete="new-password"
|
|
206
|
+
placeholder=" "
|
|
207
|
+
autoFocus
|
|
208
|
+
disabled={isLoading}
|
|
209
|
+
/>
|
|
210
|
+
<label htmlFor="verify-password">
|
|
211
|
+
{copy.passwordLabel}
|
|
212
|
+
</label>
|
|
213
|
+
{/* Typing a password blind, twice, is where most failed
|
|
214
|
+
resets come from. */}
|
|
215
|
+
<button
|
|
216
|
+
type="button"
|
|
217
|
+
className={styles.reveal}
|
|
218
|
+
onClick={() => setShowPassword((on) => !on)}
|
|
219
|
+
aria-label={
|
|
220
|
+
showPassword ? 'Hide password' : 'Show password'
|
|
221
|
+
}
|
|
222
|
+
tabIndex="-1"
|
|
223
|
+
>
|
|
224
|
+
{showPassword ? (
|
|
225
|
+
<EyeOff size={18} strokeWidth={1.9} />
|
|
226
|
+
) : (
|
|
227
|
+
<Eye size={18} strokeWidth={1.9} />
|
|
228
|
+
)}
|
|
229
|
+
</button>
|
|
169
230
|
</div>
|
|
170
|
-
|
|
171
|
-
|
|
231
|
+
|
|
232
|
+
<div className={styles.field}>
|
|
233
|
+
<input
|
|
234
|
+
id="verify-password-repeat"
|
|
235
|
+
type={showPassword ? 'text' : 'password'}
|
|
236
|
+
name="passwordRepeat"
|
|
237
|
+
value={passwordRepeat}
|
|
238
|
+
onChange={handleInputChange}
|
|
239
|
+
tabIndex="2"
|
|
240
|
+
className={passwordRepeatClass}
|
|
241
|
+
autoComplete="new-password"
|
|
242
|
+
placeholder=" "
|
|
243
|
+
disabled={isLoading}
|
|
244
|
+
/>
|
|
245
|
+
<label htmlFor="verify-password-repeat">
|
|
246
|
+
{copy.repeatLabel}
|
|
247
|
+
</label>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
{showRules ? (
|
|
251
|
+
<ul className={styles.rules}>
|
|
252
|
+
{rules.map((rule) => {
|
|
253
|
+
const met = rule.test(password);
|
|
254
|
+
|
|
255
|
+
return (
|
|
256
|
+
<li
|
|
257
|
+
key={rule.label}
|
|
258
|
+
className={met ? styles.ruleMet : undefined}
|
|
259
|
+
>
|
|
260
|
+
{rule.label}
|
|
261
|
+
</li>
|
|
262
|
+
);
|
|
263
|
+
})}
|
|
264
|
+
</ul>
|
|
265
|
+
) : null}
|
|
266
|
+
|
|
267
|
+
<button
|
|
268
|
+
className={styles.submit}
|
|
269
|
+
type="submit"
|
|
270
|
+
disabled={isLoading}
|
|
271
|
+
tabIndex="3"
|
|
272
|
+
>
|
|
273
|
+
{isLoading ? copy.submitting : copy.submit}
|
|
274
|
+
</button>
|
|
275
|
+
</form>
|
|
276
|
+
|
|
277
|
+
<Link to={loginPath} className={styles.back}>
|
|
278
|
+
<ArrowLeft size={15} strokeWidth={2} />
|
|
279
|
+
{copy.back}
|
|
280
|
+
</Link>
|
|
281
|
+
</AuthShell>
|
|
172
282
|
);
|
|
173
283
|
};
|
|
174
284
|
|
|
175
285
|
export default Verify;
|
|
286
|
+
export { PASSWORD_RULES };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth endpoint table.
|
|
3
|
+
*
|
|
4
|
+
* Every URL the auth screens talk to used to be a string literal inside the
|
|
5
|
+
* component that used it, which made the whole journey unusable by any app
|
|
6
|
+
* whose backend does not mount Laravel's routes at exactly those paths. They
|
|
7
|
+
* are collected here instead, and every screen takes an `endpoints` prop that
|
|
8
|
+
* is merged over these defaults.
|
|
9
|
+
*
|
|
10
|
+
* The defaults ARE the 6.6.3 literals, so an app that passes nothing keeps the
|
|
11
|
+
* behaviour it already has.
|
|
12
|
+
*
|
|
13
|
+
* Pure module (no React, no DOM) so it can be unit-tested with `node --test`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** The paths the library used before they were configurable. */
|
|
17
|
+
export const DEFAULT_AUTH_ENDPOINTS = {
|
|
18
|
+
/** Login.jsx — POST {email, password, remember, location}. */
|
|
19
|
+
authenticate: '/login/authenticate',
|
|
20
|
+
/** TwoFactorAuth.jsx — POST {code, previous_url, remember}. */
|
|
21
|
+
twoFactorChallenge: '/login/two-factor-challenge',
|
|
22
|
+
/** TwoFactorAuth.jsx (mode="code") — POST {} to re-send the SMS code. */
|
|
23
|
+
twoFactorResend: '/login/two-factor-resend',
|
|
24
|
+
/** Login.jsx — the azure SSO button navigates here. */
|
|
25
|
+
azureSso: '/auth/azure',
|
|
26
|
+
/** GenericAuth.jsx — GET, polled to decide whether a session exists. */
|
|
27
|
+
profile: '/ajax/user/profile',
|
|
28
|
+
/** Reset.jsx — POST {email}. */
|
|
29
|
+
passwordForgot: '/password/forgot',
|
|
30
|
+
/** Verify.jsx — POST {code, password, password_confirmation}. */
|
|
31
|
+
passwordReset: '/password/reset',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Merge a partial `endpoints` prop over the defaults.
|
|
36
|
+
*
|
|
37
|
+
* Nullish and empty-string entries are dropped rather than applied: passing
|
|
38
|
+
* `{authenticate: undefined}` must not blank the default and leave a component
|
|
39
|
+
* POSTing to ''. Unknown keys are carried through, so a consumer can park its
|
|
40
|
+
* own paths on the same object.
|
|
41
|
+
*/
|
|
42
|
+
export const resolveAuthEndpoints = (overrides) => {
|
|
43
|
+
const resolved = { ...DEFAULT_AUTH_ENDPOINTS };
|
|
44
|
+
|
|
45
|
+
if (!overrides || typeof overrides !== 'object') {
|
|
46
|
+
return resolved;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Object.keys(overrides).forEach((key) => {
|
|
50
|
+
const value = overrides[key];
|
|
51
|
+
|
|
52
|
+
if (value === null || value === undefined || value === '') {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
resolved[key] = value;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return resolved;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** The client-portal route paths, as they were hardcoded before 6.7.0. */
|
|
63
|
+
export const DEFAULT_CLIENT_PATHS = {
|
|
64
|
+
/** Route segment the client routes are nested under in GenericAuth. */
|
|
65
|
+
base: 'client',
|
|
66
|
+
login: '/client/login',
|
|
67
|
+
verify: '/client/verify',
|
|
68
|
+
portal: '/client/portal',
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Merge a partial `paths` prop over the client defaults, the same way
|
|
73
|
+
* `resolveAuthEndpoints` does. When only `base` is given the three absolute
|
|
74
|
+
* paths are derived from it, so `{base: 'portal'}` yields the whole
|
|
75
|
+
* `/portal/*` family without restating each one.
|
|
76
|
+
*/
|
|
77
|
+
export const resolveClientPaths = (overrides) => {
|
|
78
|
+
if (!overrides || typeof overrides !== 'object') {
|
|
79
|
+
return { ...DEFAULT_CLIENT_PATHS };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const base =
|
|
83
|
+
typeof overrides.base === 'string' && overrides.base !== ''
|
|
84
|
+
? overrides.base.replace(/^\/+|\/+$/g, '')
|
|
85
|
+
: DEFAULT_CLIENT_PATHS.base;
|
|
86
|
+
|
|
87
|
+
const derived = {
|
|
88
|
+
base,
|
|
89
|
+
login: `/${base}/login`,
|
|
90
|
+
verify: `/${base}/verify`,
|
|
91
|
+
portal: `/${base}/portal`,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
Object.keys(overrides).forEach((key) => {
|
|
95
|
+
const value = overrides[key];
|
|
96
|
+
|
|
97
|
+
if (key === 'base' || value === null || value === undefined || value === '') {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
derived[key] = value;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return derived;
|
|
105
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The auth journey's typeface.
|
|
3
|
+
*
|
|
4
|
+
* Login.module.scss has asked for 'Barlow' since the screen was redesigned, but
|
|
5
|
+
* nothing in this library ever loaded it — @fontsource/barlow was a dependency
|
|
6
|
+
* that no file imported, so every consumer fell through to system-ui and the
|
|
7
|
+
* screen only looked right in the one app that happened to load Barlow itself.
|
|
8
|
+
*
|
|
9
|
+
* Importing it from a module of its own means each auth screen adds the font
|
|
10
|
+
* with one line, and an app that never renders an auth screen never pays for
|
|
11
|
+
* the four @font-face declarations.
|
|
12
|
+
*
|
|
13
|
+
* The weights are exactly the ones the auth styles use: 300 (.brandSub), 400
|
|
14
|
+
* (body), 600 (labels, buttons) and 700 (.brandLine, .title). An app that
|
|
15
|
+
* prefers its own face sets `--auth-font-family` and the CSS never asks for
|
|
16
|
+
* Barlow at all — the files are still fetched by the browser only if a rule
|
|
17
|
+
* actually uses the family.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import '@fontsource/barlow/300.css';
|
|
21
|
+
import '@fontsource/barlow/400.css';
|
|
22
|
+
import '@fontsource/barlow/600.css';
|
|
23
|
+
import '@fontsource/barlow/700.css';
|