@visns-studio/visns-components 6.6.4 → 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 +1623 -18
- package/package.json +10 -4
- package/src/components/DataGrid.jsx +171 -37
- 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/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,78 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The please-wait curtain.
|
|
5
|
+
*
|
|
6
|
+
* Shown while the profile check is in flight, and — since 6.8.1 — for the
|
|
7
|
+
* whole of any transition that ends in the browser unloading the page.
|
|
8
|
+
* Rendering null through either of those leaves a blank white screen, or worse,
|
|
9
|
+
* a screen that still looks interactive while nothing on it will respond.
|
|
10
|
+
*
|
|
11
|
+
* Lived inside GenericAuth until the auth screens needed it too. Extracted
|
|
12
|
+
* verbatim rather than reimplemented, so the curtain a screen raises for itself
|
|
13
|
+
* and the one GenericAuth raises over it are the same object: when both are up
|
|
14
|
+
* at once — which is exactly what happens on a successful 2FA hand-off — there
|
|
15
|
+
* is nothing to see between them.
|
|
16
|
+
*
|
|
17
|
+
* Self-contained on purpose: a SMIL animation, no stylesheet dependency, and
|
|
18
|
+
* the consuming app's design tokens with neutral fallbacks. It has to be able
|
|
19
|
+
* to render when the page it covers has not finished loading its CSS.
|
|
20
|
+
*/
|
|
21
|
+
const AuthLoading = ({ label }) => (
|
|
22
|
+
<div
|
|
23
|
+
role="status"
|
|
24
|
+
aria-label="Loading"
|
|
25
|
+
aria-live="polite"
|
|
26
|
+
style={{
|
|
27
|
+
position: 'fixed',
|
|
28
|
+
inset: 0,
|
|
29
|
+
display: 'flex',
|
|
30
|
+
flexDirection: 'column',
|
|
31
|
+
gap: '1rem',
|
|
32
|
+
alignItems: 'center',
|
|
33
|
+
justifyContent: 'center',
|
|
34
|
+
background: 'var(--bg-color, #f4f4f5)',
|
|
35
|
+
// Above every app surface — this is a curtain, not a page.
|
|
36
|
+
zIndex: 2000,
|
|
37
|
+
}}
|
|
38
|
+
>
|
|
39
|
+
<svg width="42" height="42" viewBox="0 0 42 42" fill="none">
|
|
40
|
+
<circle
|
|
41
|
+
cx="21"
|
|
42
|
+
cy="21"
|
|
43
|
+
r="18"
|
|
44
|
+
stroke="var(--border-color, #dfe3eb)"
|
|
45
|
+
strokeWidth="4"
|
|
46
|
+
/>
|
|
47
|
+
<path
|
|
48
|
+
d="M39 21a18 18 0 0 0-18-18"
|
|
49
|
+
stroke="var(--primary-color, #52525b)"
|
|
50
|
+
strokeWidth="4"
|
|
51
|
+
strokeLinecap="round"
|
|
52
|
+
>
|
|
53
|
+
<animateTransform
|
|
54
|
+
attributeName="transform"
|
|
55
|
+
type="rotate"
|
|
56
|
+
from="0 21 21"
|
|
57
|
+
to="360 21 21"
|
|
58
|
+
dur="0.9s"
|
|
59
|
+
repeatCount="indefinite"
|
|
60
|
+
/>
|
|
61
|
+
</path>
|
|
62
|
+
</svg>
|
|
63
|
+
{label ? (
|
|
64
|
+
<p
|
|
65
|
+
style={{
|
|
66
|
+
margin: 0,
|
|
67
|
+
fontFamily: 'var(--font-family, inherit)',
|
|
68
|
+
fontSize: 'var(--font-size-md, 0.9375rem)',
|
|
69
|
+
color: 'var(--paragraph-color, #52525b)',
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
{label}
|
|
73
|
+
</p>
|
|
74
|
+
) : null}
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
export default AuthLoading;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two-pane frame every staff auth screen sits in.
|
|
3
|
+
*
|
|
4
|
+
* Login.jsx keeps its own copy of this markup — it is the screen the design was
|
|
5
|
+
* drawn for and it has an SSO disclosure the others do not — but TwoFactorAuth,
|
|
6
|
+
* Reset and Verify all render the same frame around different contents, so it
|
|
7
|
+
* lives here rather than three times over.
|
|
8
|
+
*
|
|
9
|
+
* The styles come from the caller: each screen owns its own CSS module (they
|
|
10
|
+
* all `@include auth-shell` from styles/_authShell.scss, so the class names are
|
|
11
|
+
* identical) and passes it in as `styles`. That keeps the module scoping intact
|
|
12
|
+
* while the markup stays in one place.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import React from 'react';
|
|
16
|
+
|
|
17
|
+
import AuthBrandPanel from './AuthBrandPanel';
|
|
18
|
+
|
|
19
|
+
const AuthShell = ({
|
|
20
|
+
styles,
|
|
21
|
+
logo,
|
|
22
|
+
loginBg,
|
|
23
|
+
branding = {},
|
|
24
|
+
title,
|
|
25
|
+
subtitle,
|
|
26
|
+
children,
|
|
27
|
+
footer,
|
|
28
|
+
}) => (
|
|
29
|
+
<div className={styles.auth}>
|
|
30
|
+
<AuthBrandPanel
|
|
31
|
+
styles={styles}
|
|
32
|
+
loginBg={loginBg}
|
|
33
|
+
branding={branding}
|
|
34
|
+
/>
|
|
35
|
+
|
|
36
|
+
<main className={styles.panel}>
|
|
37
|
+
<div className={styles.panelInner}>
|
|
38
|
+
{logo ? (
|
|
39
|
+
<img
|
|
40
|
+
src={logo}
|
|
41
|
+
alt={branding.name || 'Logo'}
|
|
42
|
+
className={styles.logo}
|
|
43
|
+
/>
|
|
44
|
+
) : null}
|
|
45
|
+
|
|
46
|
+
{title ? <h1 className={styles.title}>{title}</h1> : null}
|
|
47
|
+
{subtitle ? (
|
|
48
|
+
<p className={styles.subtitle}>{subtitle}</p>
|
|
49
|
+
) : null}
|
|
50
|
+
|
|
51
|
+
{children}
|
|
52
|
+
|
|
53
|
+
{footer ? <p className={styles.foot}>{footer}</p> : null}
|
|
54
|
+
</div>
|
|
55
|
+
</main>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
export default AuthShell;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import ClientLogin from './ClientLogin';
|
|
4
|
+
import ClientOTPVerify from './ClientOTPVerify';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Both client sign-in steps on one page.
|
|
8
|
+
*
|
|
9
|
+
* ClientLogin and ClientOTPVerify were written as two routes with a router
|
|
10
|
+
* navigation between them. That is one shape a sign-in can take; the other is
|
|
11
|
+
* a single page that swaps its own contents, which is what the Next.js portal
|
|
12
|
+
* does and what an App Router page can actually host. This composes the two
|
|
13
|
+
* screens into that shape by holding the challenge in state instead of in
|
|
14
|
+
* router state.
|
|
15
|
+
*
|
|
16
|
+
* It needs no router, so it mounts cleanly under Next.js, and it is the whole
|
|
17
|
+
* of the "zero adapter code" story: a consumer supplies endpoints, a protocol
|
|
18
|
+
* name and an `onAuthenticated`, and writes nothing that knows a request body
|
|
19
|
+
* from a response field.
|
|
20
|
+
*
|
|
21
|
+
* <ClientAuth
|
|
22
|
+
* layout="split"
|
|
23
|
+
* loginBg="/images/office.jpg"
|
|
24
|
+
* branding={{ name: 'Client Portal', tagline: '…' }}
|
|
25
|
+
* protocol="contact"
|
|
26
|
+
* urls={{ login: '/api/auth/request-otp', verify: '/api/auth/login-otp' }}
|
|
27
|
+
* usernameDomain="virtualportal.com.au"
|
|
28
|
+
* showContactHint
|
|
29
|
+
* showDevOtp
|
|
30
|
+
* showMaskedContact
|
|
31
|
+
* showContactField
|
|
32
|
+
* resendCooldown={0}
|
|
33
|
+
* onAuthenticated={(user, token) => {
|
|
34
|
+
* setCookie('portal_token', token, { maxAge: 28800 });
|
|
35
|
+
* setUserData(user);
|
|
36
|
+
* router.push('/portal/dashboard');
|
|
37
|
+
* }}
|
|
38
|
+
* />
|
|
39
|
+
*
|
|
40
|
+
* Props are forwarded to whichever step owns them; `loginProps` and
|
|
41
|
+
* `verifyProps` are escape hatches for anything that has to differ between the
|
|
42
|
+
* two. The shared ones (logo, urls, protocol, …) are passed to both.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
const ClientAuth = ({
|
|
46
|
+
/** Shared. */
|
|
47
|
+
logo,
|
|
48
|
+
/**
|
|
49
|
+
* 'card' (default) renders exactly what every consumer rendered before
|
|
50
|
+
* 6.8.3: a box centred in the space the host page gives it. 'split' is the
|
|
51
|
+
* staff sign-in's two-pane language — brand on one side, task on the other
|
|
52
|
+
* — sized to fill its PARENT rather than the viewport, so the consumer
|
|
53
|
+
* keeps its own header and footer and hands this the space between.
|
|
54
|
+
*/
|
|
55
|
+
layout = 'card',
|
|
56
|
+
/** Split layout: the brand panel's photograph. Ignored by 'card'. */
|
|
57
|
+
loginBg,
|
|
58
|
+
/**
|
|
59
|
+
* Split layout: {eyebrow, name, tagline, footer}, rendered in the same
|
|
60
|
+
* type hierarchy as the staff Login's brand panel. Ignored by 'card'.
|
|
61
|
+
*/
|
|
62
|
+
branding,
|
|
63
|
+
urls,
|
|
64
|
+
paths,
|
|
65
|
+
protocol,
|
|
66
|
+
contactField,
|
|
67
|
+
usernameDomain = null,
|
|
68
|
+
showDevOtp = false,
|
|
69
|
+
onError,
|
|
70
|
+
|
|
71
|
+
/** Step one. */
|
|
72
|
+
showContactHint = false,
|
|
73
|
+
contactHints,
|
|
74
|
+
loginCopy,
|
|
75
|
+
loginLabels,
|
|
76
|
+
|
|
77
|
+
/** Step two. */
|
|
78
|
+
resendCooldown = 60,
|
|
79
|
+
showMaskedContact = false,
|
|
80
|
+
showContactField = false,
|
|
81
|
+
minimalResponse = true,
|
|
82
|
+
verifyCopy,
|
|
83
|
+
verifyLabels,
|
|
84
|
+
|
|
85
|
+
/** Called with `(user, token, rawResponse)` on a successful verification. */
|
|
86
|
+
onAuthenticated,
|
|
87
|
+
|
|
88
|
+
/** Notified with the challenge each time a code is sent. */
|
|
89
|
+
onCodeSent,
|
|
90
|
+
|
|
91
|
+
/** Extra props for either step, merged last. */
|
|
92
|
+
loginProps = {},
|
|
93
|
+
verifyProps = {},
|
|
94
|
+
}) => {
|
|
95
|
+
const [challenge, setChallenge] = useState(null);
|
|
96
|
+
// What the user typed, kept across the back link so returning to step one
|
|
97
|
+
// does not blank the field.
|
|
98
|
+
const [contact, setContact] = useState('');
|
|
99
|
+
|
|
100
|
+
if (challenge) {
|
|
101
|
+
return (
|
|
102
|
+
<ClientOTPVerify
|
|
103
|
+
logo={logo}
|
|
104
|
+
layout={layout}
|
|
105
|
+
loginBg={loginBg}
|
|
106
|
+
branding={branding}
|
|
107
|
+
urls={urls}
|
|
108
|
+
paths={paths}
|
|
109
|
+
protocol={protocol}
|
|
110
|
+
contactField={contactField}
|
|
111
|
+
challenge={challenge}
|
|
112
|
+
copy={verifyCopy}
|
|
113
|
+
labels={verifyLabels}
|
|
114
|
+
resendCooldown={resendCooldown}
|
|
115
|
+
showMaskedContact={showMaskedContact}
|
|
116
|
+
showContactField={showContactField}
|
|
117
|
+
showDevOtp={showDevOtp}
|
|
118
|
+
minimalResponse={minimalResponse}
|
|
119
|
+
onAuthenticated={onAuthenticated}
|
|
120
|
+
onError={onError}
|
|
121
|
+
// The back link clears the challenge rather than navigating.
|
|
122
|
+
// Passing it is also what stops the verify screen redirecting
|
|
123
|
+
// when it decides it has nothing to answer.
|
|
124
|
+
onBack={() => setChallenge(null)}
|
|
125
|
+
{...verifyProps}
|
|
126
|
+
/>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<ClientLogin
|
|
132
|
+
logo={logo}
|
|
133
|
+
layout={layout}
|
|
134
|
+
loginBg={loginBg}
|
|
135
|
+
branding={branding}
|
|
136
|
+
urls={urls}
|
|
137
|
+
paths={paths}
|
|
138
|
+
protocol={protocol}
|
|
139
|
+
contactField={contactField}
|
|
140
|
+
usernameDomain={usernameDomain}
|
|
141
|
+
showContactHint={showContactHint}
|
|
142
|
+
contactHints={contactHints}
|
|
143
|
+
showDevOtp={showDevOtp}
|
|
144
|
+
copy={loginCopy}
|
|
145
|
+
labels={loginLabels}
|
|
146
|
+
initialContact={contact}
|
|
147
|
+
onError={onError}
|
|
148
|
+
onChallengeIssued={(issued) => {
|
|
149
|
+
setContact(issued.rawContact || '');
|
|
150
|
+
setChallenge(issued);
|
|
151
|
+
|
|
152
|
+
if (typeof onCodeSent === 'function') {
|
|
153
|
+
onCodeSent(issued);
|
|
154
|
+
}
|
|
155
|
+
}}
|
|
156
|
+
{...loginProps}
|
|
157
|
+
/>
|
|
158
|
+
);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export default ClientAuth;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import AuthBrandPanel from './AuthBrandPanel';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The frame around a client sign-in step.
|
|
7
|
+
*
|
|
8
|
+
* Both steps render the same children — logo, title, subtitle, form — and the
|
|
9
|
+
* only thing `layout` changes is what sits around them. Keeping that choice
|
|
10
|
+
* here means neither ClientLogin nor ClientOTPVerify has to know there are two
|
|
11
|
+
* layouts, and the form markup stays untouched by the design work.
|
|
12
|
+
*
|
|
13
|
+
* `card` (default) — the original: a box centred in whatever vertical space
|
|
14
|
+
* the host page happens to give it. Byte-identical to
|
|
15
|
+
* what every consumer rendered before 6.8.3.
|
|
16
|
+
* `split` — the staff sign-in's language: the brand on one side, the task on
|
|
17
|
+
* the other. A SECTION rather than a screen, sized by its parent —
|
|
18
|
+
* the consumer keeps its own header and footer and hands this the
|
|
19
|
+
* space between, which is why there is no `100vh` in its styles.
|
|
20
|
+
*/
|
|
21
|
+
const ClientAuthFrame = ({
|
|
22
|
+
layout = 'card',
|
|
23
|
+
styles,
|
|
24
|
+
loginBg,
|
|
25
|
+
branding = {},
|
|
26
|
+
children,
|
|
27
|
+
}) => {
|
|
28
|
+
if (layout === 'split') {
|
|
29
|
+
return (
|
|
30
|
+
<section className={styles.split}>
|
|
31
|
+
<AuthBrandPanel
|
|
32
|
+
styles={styles}
|
|
33
|
+
loginBg={loginBg}
|
|
34
|
+
branding={branding}
|
|
35
|
+
/>
|
|
36
|
+
|
|
37
|
+
<div className={styles.splitPane}>
|
|
38
|
+
<div className={styles.splitCard}>
|
|
39
|
+
{children}
|
|
40
|
+
|
|
41
|
+
{branding.footer ? (
|
|
42
|
+
<p className={styles.splitFoot}>
|
|
43
|
+
{branding.footer}
|
|
44
|
+
</p>
|
|
45
|
+
) : null}
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</section>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div className={styles.container}>
|
|
54
|
+
<div className={styles.loginBox}>{children}</div>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default ClientAuthFrame;
|