@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.
Files changed (97) hide show
  1. package/README.md +1623 -18
  2. package/package.json +10 -4
  3. package/src/components/DataGrid.jsx +171 -37
  4. package/src/components/Fetch.jsx +80 -3
  5. package/src/components/Form.jsx +9 -0
  6. package/src/components/Navigation.jsx +109 -50
  7. package/src/components/Notification.jsx +279 -9
  8. package/src/components/TableFilter.jsx +14 -1
  9. package/src/components/auth/AuthBrandPanel.jsx +43 -0
  10. package/src/components/auth/AuthLoading.jsx +78 -0
  11. package/src/components/auth/AuthShell.jsx +59 -0
  12. package/src/components/auth/ClientAuth.jsx +161 -0
  13. package/src/components/auth/ClientAuthFrame.jsx +59 -0
  14. package/src/components/auth/ClientLogin.jsx +266 -55
  15. package/src/components/auth/ClientOTPVerify.jsx +587 -115
  16. package/src/components/auth/ImpersonateGate.jsx +254 -0
  17. package/src/components/auth/Login.jsx +134 -41
  18. package/src/components/auth/LogoutScreen.jsx +112 -0
  19. package/src/components/auth/Reset.jsx +134 -77
  20. package/src/components/auth/TwoFactorAuth.jsx +475 -297
  21. package/src/components/auth/Verify.jsx +237 -126
  22. package/src/components/auth/authEndpoints.js +105 -0
  23. package/src/components/auth/authFont.js +23 -0
  24. package/src/components/auth/authHelpers.js +465 -0
  25. package/src/components/auth/clientAuthProtocols.js +240 -0
  26. package/src/components/auth/useOptionalRouter.js +49 -0
  27. package/src/components/callQueue/CallQueuePop.jsx +1502 -0
  28. package/src/components/callQueue/CallQueueSettings.jsx +508 -0
  29. package/src/components/callQueue/callQueueHelpers.js +280 -0
  30. package/src/components/callQueue/callQueueSettingsHelpers.js +75 -0
  31. package/src/components/columns/ColumnRenderers.jsx +53 -7
  32. package/src/components/generic/GenericAuth.jsx +163 -96
  33. package/src/components/generic/GenericDetail.jsx +215 -90
  34. package/src/components/generic/GenericIndex.jsx +20 -47
  35. package/src/components/generic/GenericMain.jsx +5 -0
  36. package/src/components/generic/GroupedReportRenderer.jsx +1 -5
  37. package/src/components/generic/StandardModal.jsx +17 -5
  38. package/src/components/generic/reportSemanticSteps/SemanticEntityStep.jsx +1 -6
  39. package/src/components/generic/reportSemanticSteps/SemanticFieldsStep.jsx +4 -11
  40. package/src/components/generic/reportSemanticSteps/SemanticFiltersStep.jsx +9 -27
  41. package/src/components/generic/reportSemanticSteps/SemanticGroupingStep.jsx +5 -13
  42. package/src/components/generic/reportSemanticSteps/SemanticParameterPrompt.jsx +5 -13
  43. package/src/components/generic/reportSemanticSteps/SemanticPreviewStep.jsx +14 -31
  44. package/src/components/generic/reportSemanticSteps/SemanticRelationsStep.jsx +3 -9
  45. package/src/components/generic/reportSemanticSteps/SemanticValueInput.jsx +2 -15
  46. package/src/components/navActive.js +60 -0
  47. package/src/components/notify/desktopNotifications.js +256 -0
  48. package/src/components/sketch/SketchField.jsx +12 -2
  49. package/src/components/sms/SmsComposeModal.jsx +495 -0
  50. package/src/components/sms/SmsInbox.jsx +596 -0
  51. package/src/components/sms/SmsInboxBadge.jsx +468 -0
  52. package/src/components/sms/SmsLineSettings.jsx +854 -0
  53. package/src/components/sms/SmsThreadPanel.jsx +1184 -0
  54. package/src/components/sms/smsEndpoints.js +56 -0
  55. package/src/components/sms/smsHelpers.js +1075 -0
  56. package/src/components/sms/smsLiveState.js +132 -0
  57. package/src/components/sms/useSmsLive.js +307 -0
  58. package/src/components/styles/CallQueuePop.module.scss +646 -0
  59. package/src/components/styles/CallQueueSettings.module.scss +460 -0
  60. package/src/components/styles/ClientAuth.module.scss +229 -0
  61. package/src/components/styles/GenericDetail.module.scss +153 -26
  62. package/src/components/styles/GenericIndex.module.scss +13 -0
  63. package/src/components/styles/GenericMain.module.scss +19 -2
  64. package/src/components/styles/ImpersonateGate.module.scss +85 -0
  65. package/src/components/styles/Login.module.scss +13 -122
  66. package/src/components/styles/LogoutScreen.module.scss +89 -0
  67. package/src/components/styles/Navigation.module.scss +509 -223
  68. package/src/components/styles/Notification.module.scss +103 -0
  69. package/src/components/styles/Reset.module.scss +52 -186
  70. package/src/components/styles/Sms.module.scss +2382 -0
  71. package/src/components/styles/TableFilter.module.scss +118 -98
  72. package/src/components/styles/TwoFactorAuth.module.scss +115 -176
  73. package/src/components/styles/Vault.module.scss +1983 -0
  74. package/src/components/styles/Verify.module.scss +57 -180
  75. package/src/components/styles/_authBrandPanel.scss +163 -0
  76. package/src/components/styles/_authShell.scss +369 -0
  77. package/src/components/utils/buildEnv.js +67 -0
  78. package/src/components/utils/displayValue.js +94 -0
  79. package/src/components/utils/useDensity.js +345 -9
  80. package/src/components/vault/OtpChip.jsx +208 -0
  81. package/src/components/vault/PasswordGenerator.jsx +145 -0
  82. package/src/components/vault/QrScanner.jsx +326 -0
  83. package/src/components/vault/VaultAccessLog.jsx +225 -0
  84. package/src/components/vault/VaultConfirmPanel.jsx +113 -0
  85. package/src/components/vault/VaultEntryForm.jsx +740 -0
  86. package/src/components/vault/VaultManager.jsx +1000 -0
  87. package/src/components/vault/VaultQuickSearch.jsx +681 -0
  88. package/src/components/vault/qrDecode.js +257 -0
  89. package/src/components/vault/useDebouncedValue.js +22 -0
  90. package/src/components/vault/useVaultReveal.js +160 -0
  91. package/src/components/vault/vaultClipboard.js +33 -0
  92. package/src/components/vault/vaultEndpoints.js +40 -0
  93. package/src/components/vault/vaultFit.js +116 -0
  94. package/src/components/vault/vaultHelpers.js +547 -0
  95. package/src/components/vault/vaultNavigation.jsx +58 -0
  96. package/src/components/vault/vaultOtp.js +105 -0
  97. 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;