@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
|
@@ -63,6 +63,10 @@ import {
|
|
|
63
63
|
X as CircleX,
|
|
64
64
|
} from 'lucide-react';
|
|
65
65
|
import { confirmDialog } from '../utils/ConfirmDialog';
|
|
66
|
+
import {
|
|
67
|
+
isBlankValue,
|
|
68
|
+
overviewFieldLabel,
|
|
69
|
+
} from '../utils/displayValue';
|
|
66
70
|
import OcrTemplateEnhanced from './OcrTemplateEnhanced';
|
|
67
71
|
|
|
68
72
|
import 'react-big-calendar/lib/css/react-big-calendar.css';
|
|
@@ -269,9 +273,15 @@ function GenericDetail({
|
|
|
269
273
|
/** Window Width State for Responsive Modal */
|
|
270
274
|
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
|
|
271
275
|
|
|
272
|
-
// Handle window resize for responsive modal
|
|
276
|
+
// Handle window resize for responsive modal — and for the grid height
|
|
277
|
+
// below, which is 65% of the window. `setWindowHeight` was never called,
|
|
278
|
+
// so a detail page opened on one screen and dragged to another (or a
|
|
279
|
+
// window resized at all) kept the grid it was born with.
|
|
273
280
|
useEffect(() => {
|
|
274
|
-
const handleResize = () =>
|
|
281
|
+
const handleResize = () => {
|
|
282
|
+
setWindowWidth(window.innerWidth);
|
|
283
|
+
setWindowHeight(window.innerHeight);
|
|
284
|
+
};
|
|
275
285
|
window.addEventListener('resize', handleResize);
|
|
276
286
|
return () => window.removeEventListener('resize', handleResize);
|
|
277
287
|
}, []);
|
|
@@ -2778,6 +2788,55 @@ function GenericDetail({
|
|
|
2778
2788
|
<span>
|
|
2779
2789
|
{section.title}
|
|
2780
2790
|
</span>
|
|
2791
|
+
{/* Optional right-hand meta on a section heading —
|
|
2792
|
+
the same item shape as `content`, rendered as
|
|
2793
|
+
quiet "label value" pairs. It is for the one or
|
|
2794
|
+
two facts that describe the whole card (a count,
|
|
2795
|
+
a last-updated stamp), which otherwise have to
|
|
2796
|
+
be spent as full-width fields among the real
|
|
2797
|
+
ones. */}
|
|
2798
|
+
{Array.isArray(
|
|
2799
|
+
section.meta
|
|
2800
|
+
) &&
|
|
2801
|
+
section.meta
|
|
2802
|
+
.length >
|
|
2803
|
+
0 && (
|
|
2804
|
+
<span
|
|
2805
|
+
className={
|
|
2806
|
+
styles.gridtxt__meta
|
|
2807
|
+
}
|
|
2808
|
+
>
|
|
2809
|
+
{section.meta.map(
|
|
2810
|
+
(
|
|
2811
|
+
metaItem,
|
|
2812
|
+
metaKey
|
|
2813
|
+
) => {
|
|
2814
|
+
const metaValue =
|
|
2815
|
+
renderValue(
|
|
2816
|
+
metaItem,
|
|
2817
|
+
data
|
|
2818
|
+
);
|
|
2819
|
+
|
|
2820
|
+
return (
|
|
2821
|
+
<span
|
|
2822
|
+
key={`meta-${activeTabConfig.id}-${sectionIndex}-${metaKey}`}
|
|
2823
|
+
>
|
|
2824
|
+
{metaItem.label
|
|
2825
|
+
? `${overviewFieldLabel(
|
|
2826
|
+
metaItem.label
|
|
2827
|
+
)} `
|
|
2828
|
+
: ''}
|
|
2829
|
+
{isBlankValue(
|
|
2830
|
+
metaValue
|
|
2831
|
+
)
|
|
2832
|
+
? '—'
|
|
2833
|
+
: metaValue}
|
|
2834
|
+
</span>
|
|
2835
|
+
);
|
|
2836
|
+
}
|
|
2837
|
+
)}
|
|
2838
|
+
</span>
|
|
2839
|
+
)}
|
|
2781
2840
|
</div>
|
|
2782
2841
|
{section.content &&
|
|
2783
2842
|
section.content
|
|
@@ -2791,41 +2850,76 @@ function GenericDetail({
|
|
|
2791
2850
|
(
|
|
2792
2851
|
item,
|
|
2793
2852
|
itemKey
|
|
2794
|
-
) =>
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
item.
|
|
2799
|
-
|
|
2800
|
-
?
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
<strong>
|
|
2812
|
-
{item.label
|
|
2813
|
-
? `${item.label}:`
|
|
2814
|
-
: null}
|
|
2815
|
-
</strong>
|
|
2816
|
-
)}
|
|
2817
|
-
{item.type !==
|
|
2853
|
+
) => {
|
|
2854
|
+
const liClass =
|
|
2855
|
+
item.size ===
|
|
2856
|
+
'full'
|
|
2857
|
+
? item.type ===
|
|
2858
|
+
'ocrTemplate'
|
|
2859
|
+
? styles[
|
|
2860
|
+
'fw-grid-item'
|
|
2861
|
+
]
|
|
2862
|
+
: `${styles['fw-grid-item']} ${styles.notecolor}`
|
|
2863
|
+
: null;
|
|
2864
|
+
|
|
2865
|
+
// An embedded editor owns its whole
|
|
2866
|
+
// cell — no stacked label, no
|
|
2867
|
+
// placeholder.
|
|
2868
|
+
if (
|
|
2869
|
+
item.type ===
|
|
2818
2870
|
'ocrTemplate'
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2871
|
+
) {
|
|
2872
|
+
return (
|
|
2873
|
+
<li
|
|
2874
|
+
key={`li-${item.id}-${itemKey}`}
|
|
2875
|
+
className={
|
|
2876
|
+
liClass
|
|
2877
|
+
}
|
|
2878
|
+
>
|
|
2879
|
+
{renderValue(
|
|
2880
|
+
item,
|
|
2881
|
+
data
|
|
2882
|
+
)}
|
|
2883
|
+
</li>
|
|
2884
|
+
);
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2887
|
+
const rendered =
|
|
2888
|
+
renderValue(
|
|
2889
|
+
item,
|
|
2890
|
+
data
|
|
2891
|
+
);
|
|
2892
|
+
|
|
2893
|
+
const hasHref =
|
|
2894
|
+
item
|
|
2822
2895
|
.href
|
|
2823
2896
|
?.url &&
|
|
2824
2897
|
item
|
|
2825
2898
|
.href
|
|
2826
2899
|
?.key &&
|
|
2827
|
-
item.type !==
|
|
2828
|
-
|
|
2900
|
+
item.type !==
|
|
2901
|
+
'link' &&
|
|
2902
|
+
item.type !==
|
|
2903
|
+
'file';
|
|
2904
|
+
|
|
2905
|
+
// A field with no value used to
|
|
2906
|
+
// render as a label followed by
|
|
2907
|
+
// nothing, which reads as a broken
|
|
2908
|
+
// page rather than as "not set" —
|
|
2909
|
+
// and an href wrapped that nothing
|
|
2910
|
+
// in a link with no text to click.
|
|
2911
|
+
const body =
|
|
2912
|
+
isBlankValue(
|
|
2913
|
+
rendered
|
|
2914
|
+
) ? (
|
|
2915
|
+
<span
|
|
2916
|
+
className={
|
|
2917
|
+
styles.overviewEmpty
|
|
2918
|
+
}
|
|
2919
|
+
>
|
|
2920
|
+
—
|
|
2921
|
+
</span>
|
|
2922
|
+
) : hasHref ? (
|
|
2829
2923
|
<Link
|
|
2830
2924
|
to={`${
|
|
2831
2925
|
item
|
|
@@ -2839,21 +2933,44 @@ function GenericDetail({
|
|
|
2839
2933
|
]
|
|
2840
2934
|
}`}
|
|
2841
2935
|
>
|
|
2842
|
-
{
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
)}
|
|
2936
|
+
{
|
|
2937
|
+
rendered
|
|
2938
|
+
}
|
|
2846
2939
|
</Link>
|
|
2847
2940
|
) : (
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2941
|
+
rendered
|
|
2942
|
+
);
|
|
2943
|
+
|
|
2944
|
+
return (
|
|
2945
|
+
<li
|
|
2946
|
+
key={`li-${item.id}-${itemKey}`}
|
|
2947
|
+
className={
|
|
2948
|
+
liClass
|
|
2949
|
+
}
|
|
2950
|
+
>
|
|
2951
|
+
{item.label && (
|
|
2952
|
+
<strong
|
|
2953
|
+
className={
|
|
2954
|
+
styles.overviewLabel
|
|
2955
|
+
}
|
|
2956
|
+
>
|
|
2957
|
+
{overviewFieldLabel(
|
|
2958
|
+
item.label
|
|
2959
|
+
)}
|
|
2960
|
+
</strong>
|
|
2961
|
+
)}
|
|
2962
|
+
<span
|
|
2963
|
+
className={
|
|
2964
|
+
styles.overviewValue
|
|
2965
|
+
}
|
|
2966
|
+
>
|
|
2967
|
+
{
|
|
2968
|
+
body
|
|
2969
|
+
}
|
|
2970
|
+
</span>
|
|
2971
|
+
</li>
|
|
2972
|
+
);
|
|
2973
|
+
}
|
|
2857
2974
|
)}
|
|
2858
2975
|
</ul>
|
|
2859
2976
|
)}
|
|
@@ -2869,17 +2986,14 @@ function GenericDetail({
|
|
|
2869
2986
|
{!inPlaceEditing ? (
|
|
2870
2987
|
<button
|
|
2871
2988
|
className={styles.btn}
|
|
2872
|
-
title=
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
'
|
|
2879
|
-
|
|
2880
|
-
minWidth: '92px',
|
|
2881
|
-
height: '42px',
|
|
2882
|
-
}}
|
|
2989
|
+
title={
|
|
2990
|
+
activeTabConfig.form
|
|
2991
|
+
.editLabel || 'Edit'
|
|
2992
|
+
}
|
|
2993
|
+
aria-label={
|
|
2994
|
+
activeTabConfig.form
|
|
2995
|
+
.editLabel || 'Edit'
|
|
2996
|
+
}
|
|
2883
2997
|
onClick={() => {
|
|
2884
2998
|
if (
|
|
2885
2999
|
activeTabConfig.form
|
|
@@ -2902,25 +3016,27 @@ function GenericDetail({
|
|
|
2902
3016
|
size={18}
|
|
2903
3017
|
strokeWidth={2.25}
|
|
2904
3018
|
/>
|
|
2905
|
-
<span>
|
|
3019
|
+
<span>
|
|
3020
|
+
{activeTabConfig.form
|
|
3021
|
+
.editLabel ||
|
|
3022
|
+
'Edit'}
|
|
3023
|
+
</span>
|
|
2906
3024
|
</button>
|
|
2907
3025
|
) : (
|
|
2908
3026
|
editingTabId ===
|
|
2909
3027
|
activeTabConfig.id && (
|
|
2910
3028
|
<>
|
|
3029
|
+
{/* Cancel is the way back out of editing, not a
|
|
3030
|
+
destructive act — it was painted in error red,
|
|
3031
|
+
the loudest thing on the page, next to the
|
|
3032
|
+
Save it competes with. Ghost secondary. */}
|
|
2911
3033
|
<button
|
|
2912
3034
|
className={
|
|
2913
|
-
styles.
|
|
3035
|
+
styles.btnSecondary
|
|
2914
3036
|
}
|
|
2915
3037
|
onClick={
|
|
2916
3038
|
cancelInPlaceEditing
|
|
2917
3039
|
}
|
|
2918
|
-
style={{
|
|
2919
|
-
backgroundColor:
|
|
2920
|
-
'#fff0f0',
|
|
2921
|
-
color: '#d32f2f',
|
|
2922
|
-
border: '1px solid #ffcdd2',
|
|
2923
|
-
}}
|
|
2924
3040
|
>
|
|
2925
3041
|
Cancel
|
|
2926
3042
|
</button>
|
|
@@ -3010,14 +3126,6 @@ function GenericDetail({
|
|
|
3010
3126
|
{activeTabConfig.export && (
|
|
3011
3127
|
<button
|
|
3012
3128
|
className={styles.btn}
|
|
3013
|
-
style={{
|
|
3014
|
-
display: 'inline-flex',
|
|
3015
|
-
alignItems: 'center',
|
|
3016
|
-
justifyContent: 'center',
|
|
3017
|
-
gap: '7px',
|
|
3018
|
-
minWidth: '92px',
|
|
3019
|
-
height: '42px',
|
|
3020
|
-
}}
|
|
3021
3129
|
onClick={() =>
|
|
3022
3130
|
handleExport(
|
|
3023
3131
|
activeTabConfig.export
|
|
@@ -3032,14 +3140,6 @@ function GenericDetail({
|
|
|
3032
3140
|
{activeTabConfig.customAction && (
|
|
3033
3141
|
<button
|
|
3034
3142
|
className={styles.btn}
|
|
3035
|
-
style={{
|
|
3036
|
-
display: 'inline-flex',
|
|
3037
|
-
alignItems: 'center',
|
|
3038
|
-
justifyContent: 'center',
|
|
3039
|
-
gap: '7px',
|
|
3040
|
-
minWidth: '92px',
|
|
3041
|
-
height: '42px',
|
|
3042
|
-
}}
|
|
3043
3143
|
onClick={() =>
|
|
3044
3144
|
handleCustomAction(
|
|
3045
3145
|
activeTabConfig.customAction
|
|
@@ -3057,14 +3157,6 @@ function GenericDetail({
|
|
|
3057
3157
|
<button
|
|
3058
3158
|
key={index}
|
|
3059
3159
|
className={styles.btn}
|
|
3060
|
-
style={{
|
|
3061
|
-
display: 'inline-flex',
|
|
3062
|
-
alignItems: 'center',
|
|
3063
|
-
justifyContent: 'center',
|
|
3064
|
-
gap: '7px',
|
|
3065
|
-
minWidth: '92px',
|
|
3066
|
-
height: '42px',
|
|
3067
|
-
}}
|
|
3068
3160
|
onClick={() =>
|
|
3069
3161
|
handleCustomAction(customAction)
|
|
3070
3162
|
}
|
|
@@ -3305,6 +3397,14 @@ function GenericDetail({
|
|
|
3305
3397
|
<Table
|
|
3306
3398
|
{...processedConfig}
|
|
3307
3399
|
ref={gridRef}
|
|
3400
|
+
/* Deliberately a
|
|
3401
|
+
fraction of the window,
|
|
3402
|
+
not the fill-the-window
|
|
3403
|
+
measurement: a detail
|
|
3404
|
+
page stacks several
|
|
3405
|
+
grids down one tab and
|
|
3406
|
+
they cannot all own the
|
|
3407
|
+
viewport. */
|
|
3308
3408
|
gridHeight={
|
|
3309
3409
|
windowHeight * 0.65
|
|
3310
3410
|
}
|
|
@@ -3854,6 +3954,17 @@ function GenericDetail({
|
|
|
3854
3954
|
setEditingTabId(null);
|
|
3855
3955
|
};
|
|
3856
3956
|
|
|
3957
|
+
/**
|
|
3958
|
+
* `page.tabLayout: "underline"` lays the tabs out as a horizontal strip
|
|
3959
|
+
* above the content instead of a rail down its left edge.
|
|
3960
|
+
*
|
|
3961
|
+
* The rail costs a fifth of the page width, which is worth paying when
|
|
3962
|
+
* there are seven tabs to keep in view and is not when there are two — a
|
|
3963
|
+
* two-item rail beside a wide panel reads as an empty column. Opt-in, so
|
|
3964
|
+
* every existing page keeps the rail it was designed against.
|
|
3965
|
+
*/
|
|
3966
|
+
const tabsUnderlined = page?.tabLayout === 'underline';
|
|
3967
|
+
|
|
3857
3968
|
return (
|
|
3858
3969
|
<>
|
|
3859
3970
|
<div className={styles.grid}>
|
|
@@ -4046,9 +4157,23 @@ function GenericDetail({
|
|
|
4046
4157
|
)}
|
|
4047
4158
|
|
|
4048
4159
|
<div className={styles.grid}>
|
|
4049
|
-
<div
|
|
4050
|
-
|
|
4051
|
-
|
|
4160
|
+
<div
|
|
4161
|
+
className={`${styles.grid__subrow} ${
|
|
4162
|
+
tabsUnderlined ? styles['grid__subrow--stacked'] : ''
|
|
4163
|
+
}`}
|
|
4164
|
+
>
|
|
4165
|
+
<div
|
|
4166
|
+
className={`${styles.grid__subnav} ${
|
|
4167
|
+
tabsUnderlined
|
|
4168
|
+
? styles['grid__subnav--underline']
|
|
4169
|
+
: ''
|
|
4170
|
+
}`}
|
|
4171
|
+
>
|
|
4172
|
+
<TableFilter
|
|
4173
|
+
filters={subnav}
|
|
4174
|
+
setFilters={setSubnav}
|
|
4175
|
+
variant={tabsUnderlined ? 'underline' : undefined}
|
|
4176
|
+
/>
|
|
4052
4177
|
</div>
|
|
4053
4178
|
<div className={styles.grid__subcontent}>
|
|
4054
4179
|
{config ? renderContent() : null}
|
|
@@ -16,26 +16,10 @@ import TableFilter from '../TableFilter';
|
|
|
16
16
|
import GenericGrid from './GenericGrid';
|
|
17
17
|
import GenericReportForm from './GenericReportForm';
|
|
18
18
|
import StandardModal from './StandardModal';
|
|
19
|
-
import { useDensity
|
|
19
|
+
import { useDensity } from '../utils/useDensity';
|
|
20
20
|
|
|
21
21
|
import styles from '../styles/GenericIndex.module.scss';
|
|
22
22
|
|
|
23
|
-
function useWindowDimensions() {
|
|
24
|
-
const [windowHeight, setWindowHeight] = useState(window.innerHeight);
|
|
25
|
-
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
|
|
26
|
-
|
|
27
|
-
useEffect(() => {
|
|
28
|
-
const handleResize = () => {
|
|
29
|
-
setWindowHeight(window.innerHeight);
|
|
30
|
-
setWindowWidth(window.innerWidth);
|
|
31
|
-
};
|
|
32
|
-
window.addEventListener('resize', handleResize);
|
|
33
|
-
return () => window.removeEventListener('resize', handleResize);
|
|
34
|
-
}, []);
|
|
35
|
-
|
|
36
|
-
return { windowHeight, windowWidth };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
23
|
/**
|
|
40
24
|
* Returns a copy of `filters` with the `show` flags adjusted so the subnav
|
|
41
25
|
* item (or child) whose id matches `tabId` is active, enabling deep-links
|
|
@@ -301,24 +285,21 @@ function GenericIndex({
|
|
|
301
285
|
const [customActionModalShow, setCustomActionModalShow] = useState(false);
|
|
302
286
|
const [customActionFormData, setCustomActionFormData] = useState({});
|
|
303
287
|
const [customActionConfig, setCustomActionConfig] = useState(null);
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
//
|
|
288
|
+
// Grid height is NOT set here any more.
|
|
289
|
+
//
|
|
290
|
+
// This used to measure the wrapper below and hand the result to <Table> as
|
|
291
|
+
// `gridHeight`. The wrapper contains the whole Table — the search/create
|
|
292
|
+
// bar and the colour legend as well as the grid — but `gridHeight` is
|
|
293
|
+
// applied to the datagrid alone, so every index page asked for the room
|
|
294
|
+
// below the search bar *and* rendered the search bar on top of it. The
|
|
295
|
+
// grid overflowed the window by the height of its own toolbar, the page
|
|
296
|
+
// grew a scrollbar and the pager sat below the fold (reported on /clients).
|
|
297
|
+
//
|
|
298
|
+
// The datagrid measures itself from `.dataGridContainer`, which starts
|
|
299
|
+
// below that toolbar, so leaving `gridHeight` unset is both correct and one
|
|
300
|
+
// code path instead of two. Pages that genuinely want a fixed height (a
|
|
301
|
+
// grid inside a modal, a secondary grid on a detail page) still pass one.
|
|
312
302
|
const { isLarge } = useDensity();
|
|
313
|
-
const gridWrapperRef = useRef(null);
|
|
314
|
-
const measuredGridHeight = useAvailableHeight(gridWrapperRef, {
|
|
315
|
-
enabled: isLarge,
|
|
316
|
-
recomputeKey: windowHeight,
|
|
317
|
-
});
|
|
318
|
-
const gridHeight =
|
|
319
|
-
isLarge && measuredGridHeight != null
|
|
320
|
-
? measuredGridHeight
|
|
321
|
-
: windowHeight * 0.8;
|
|
322
303
|
|
|
323
304
|
// Toggle a body-level "tablet-mode" class when this view opts in via
|
|
324
305
|
// setting.tabletMode. Lets consumer projects style the whole UI
|
|
@@ -686,7 +667,6 @@ function GenericIndex({
|
|
|
686
667
|
{...configWithStyle}
|
|
687
668
|
ref={gridRef}
|
|
688
669
|
hoverColor={configWithStyle.style?.hoverColor}
|
|
689
|
-
gridHeight={gridHeight}
|
|
690
670
|
setConfig={setConfig}
|
|
691
671
|
setTableData={setTableData}
|
|
692
672
|
setTotal={setTotal}
|
|
@@ -696,17 +676,11 @@ function GenericIndex({
|
|
|
696
676
|
/>
|
|
697
677
|
);
|
|
698
678
|
|
|
699
|
-
//
|
|
700
|
-
//
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
style={{ flex: '1 1 auto', minHeight: 0 }}
|
|
705
|
-
>
|
|
706
|
-
{table}
|
|
707
|
-
</div>
|
|
708
|
-
) : (
|
|
709
|
-
table
|
|
679
|
+
// A plain flex cell. It no longer measures anything — the grid
|
|
680
|
+
// measures itself — but it still has to be allowed to shrink, or
|
|
681
|
+
// the flex parent floors it at its content height.
|
|
682
|
+
return (
|
|
683
|
+
<div style={{ flex: '1 1 auto', minHeight: 0 }}>{table}</div>
|
|
710
684
|
);
|
|
711
685
|
};
|
|
712
686
|
|
|
@@ -724,7 +698,6 @@ function GenericIndex({
|
|
|
724
698
|
}, [
|
|
725
699
|
config,
|
|
726
700
|
subnav,
|
|
727
|
-
gridHeight,
|
|
728
701
|
isLarge,
|
|
729
702
|
userProfile,
|
|
730
703
|
setConfig,
|
|
@@ -35,6 +35,9 @@ const GenericMain = ({
|
|
|
35
35
|
// attribute that actually drives the CSS is written by GenericAuth, and
|
|
36
36
|
// nothing in here reads the value yet.
|
|
37
37
|
density = 'default',
|
|
38
|
+
// Passed straight to Navigation: per-id renderers for account actions.
|
|
39
|
+
// See the `renderers` note in Navigation.jsx.
|
|
40
|
+
navRenderers = {},
|
|
38
41
|
}) => {
|
|
39
42
|
const navigate = useNavigate();
|
|
40
43
|
const location = useLocation();
|
|
@@ -147,6 +150,7 @@ const GenericMain = ({
|
|
|
147
150
|
setSystemAuth={setSystemAuth}
|
|
148
151
|
themeType={themeType}
|
|
149
152
|
userProfile={userProfile}
|
|
153
|
+
renderers={navRenderers}
|
|
150
154
|
/>
|
|
151
155
|
</header>
|
|
152
156
|
{navigation.hasOwnProperty('quickActions') && (
|
|
@@ -177,6 +181,7 @@ const GenericMain = ({
|
|
|
177
181
|
setSystemAuth={setSystemAuth}
|
|
178
182
|
themeType={themeType}
|
|
179
183
|
userProfile={userProfile}
|
|
184
|
+
renderers={navRenderers}
|
|
180
185
|
>
|
|
181
186
|
<div className={styles['content-main']} id="app">
|
|
182
187
|
<div className={styles.paddingSimple}>
|
|
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|
|
3
3
|
import SectionGroupedReport from './SectionGroupedReport';
|
|
4
4
|
import './groupedReport.css';
|
|
5
5
|
|
|
6
|
-
const GroupedReportRenderer = ({ data, config, columns, onExport }) => {
|
|
6
|
+
const GroupedReportRenderer = ({ data, config, columns, onExport = null }) => {
|
|
7
7
|
const renderByType = () => {
|
|
8
8
|
switch (config?.type) {
|
|
9
9
|
case 'sections':
|
|
@@ -81,8 +81,4 @@ GroupedReportRenderer.propTypes = {
|
|
|
81
81
|
onExport: PropTypes.func
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
-
GroupedReportRenderer.defaultProps = {
|
|
85
|
-
onExport: null
|
|
86
|
-
};
|
|
87
|
-
|
|
88
84
|
export default GroupedReportRenderer;
|
|
@@ -138,10 +138,19 @@ const StandardModal = ({
|
|
|
138
138
|
borderRadius: '8px',
|
|
139
139
|
boxShadow: '0 10px 30px rgba(0, 0, 0, 0.3)',
|
|
140
140
|
maxHeight: isMobile || isTablet ? '85vh' : '90vh',
|
|
141
|
-
overflowY: 'auto'
|
|
142
|
-
...customStyles.modal
|
|
141
|
+
overflowY: 'auto'
|
|
143
142
|
};
|
|
144
143
|
|
|
144
|
+
/*
|
|
145
|
+
* customStyles.modal is spread LAST, after the size preset, because a
|
|
146
|
+
* caller that bothers to pass one is overriding the default rather
|
|
147
|
+
* than asking to be overridden by it. Spread before the preset (as it
|
|
148
|
+
* once was) the width/minWidth/maxWidth a size sets could never be
|
|
149
|
+
* touched - which is what the SMS compose modal needs: a form-shaped
|
|
150
|
+
* width, and a visible overflow so its templates menu can open past
|
|
151
|
+
* the modal's own edge.
|
|
152
|
+
*/
|
|
153
|
+
|
|
145
154
|
// Apply size-based styles with device considerations
|
|
146
155
|
switch (size) {
|
|
147
156
|
case 'small':
|
|
@@ -149,14 +158,16 @@ const StandardModal = ({
|
|
|
149
158
|
...baseStyle,
|
|
150
159
|
width: isMobile ? '95vw' : '400px',
|
|
151
160
|
minWidth: isMobile ? '300px' : '400px',
|
|
152
|
-
maxWidth: isMobile ? '95vw' : '600px'
|
|
161
|
+
maxWidth: isMobile ? '95vw' : '600px',
|
|
162
|
+
...customStyles.modal
|
|
153
163
|
};
|
|
154
164
|
case 'large':
|
|
155
165
|
return {
|
|
156
166
|
...baseStyle,
|
|
157
167
|
width: '95vw',
|
|
158
168
|
minWidth: isMobile ? '300px' : '800px',
|
|
159
|
-
maxWidth: isMobile ? '95vw' : '1600px'
|
|
169
|
+
maxWidth: isMobile ? '95vw' : '1600px',
|
|
170
|
+
...customStyles.modal
|
|
160
171
|
};
|
|
161
172
|
case 'medium':
|
|
162
173
|
default:
|
|
@@ -164,7 +175,8 @@ const StandardModal = ({
|
|
|
164
175
|
...baseStyle,
|
|
165
176
|
width: isMobile ? '95vw' : '80vw',
|
|
166
177
|
minWidth: isMobile ? '300px' : '600px',
|
|
167
|
-
maxWidth: isMobile ? '95vw' : '1200px'
|
|
178
|
+
maxWidth: isMobile ? '95vw' : '1200px',
|
|
179
|
+
...customStyles.modal
|
|
168
180
|
};
|
|
169
181
|
}
|
|
170
182
|
};
|
|
@@ -10,7 +10,7 @@ import styles from '../../styles/ReportSemantic.module.scss';
|
|
|
10
10
|
* Only business labels and descriptions are shown — the entity id is never
|
|
11
11
|
* rendered, because it is an opaque server handle.
|
|
12
12
|
*/
|
|
13
|
-
const SemanticEntityStep = ({ model, selectedEntity, onSelect }) => {
|
|
13
|
+
const SemanticEntityStep = ({ model = null, selectedEntity = '', onSelect }) => {
|
|
14
14
|
const entities = listEntities(model);
|
|
15
15
|
|
|
16
16
|
if (entities.length === 0) {
|
|
@@ -73,9 +73,4 @@ SemanticEntityStep.propTypes = {
|
|
|
73
73
|
onSelect: PropTypes.func.isRequired,
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
-
SemanticEntityStep.defaultProps = {
|
|
77
|
-
model: null,
|
|
78
|
-
selectedEntity: '',
|
|
79
|
-
};
|
|
80
|
-
|
|
81
76
|
export default SemanticEntityStep;
|