@visns-studio/visns-components 5.15.2 → 5.15.4
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 +45 -1
- package/package.json +1 -1
- package/src/components/Call.jsx +6 -4
- package/src/components/DropZone.jsx +5 -4
- package/src/components/Field.jsx +9 -11
- package/src/components/QuickAction.jsx +5 -14
- package/src/components/generic/GenericFormBuilder.jsx +9 -9
- package/src/components/generic/GenericQuote.jsx +45 -66
- package/src/components/generic/StandardModal.jsx +103 -55
- package/src/components/modals/GalleryModal.jsx +9 -16
- package/src/components/styles/GenericQuote.module.scss +51 -11
package/README.md
CHANGED
|
@@ -9,10 +9,14 @@ A comprehensive React component library used by the VISNS Studio team for CRM an
|
|
|
9
9
|
|
|
10
10
|
VISNS Components is a React-based UI component library that provides a set of reusable, consistent, and customizable components for building web applications. It includes components for authentication, data grids, forms, navigation, and more, designed to work seamlessly together.
|
|
11
11
|
|
|
12
|
-
## Recent Updates (v5.15.
|
|
12
|
+
## Recent Updates (v5.15.3)
|
|
13
13
|
|
|
14
14
|
### Latest Enhancements
|
|
15
15
|
|
|
16
|
+
- **Complete StandardModal Migration**: All components now use StandardModal instead of reactjs-popup for consistent modal behavior across the entire library
|
|
17
|
+
- **HTML Content Support**: Terms and conditions now properly parse HTML content from TinyMCE editors with clean rendering
|
|
18
|
+
- **Improved Typography**: Refined font sizing in Terms and Conditions to match page typography hierarchy
|
|
19
|
+
- **Modal Component Refactoring**: StandardModal converted to pure React implementation with enhanced accessibility and mobile support
|
|
16
20
|
- **Enhanced JSON Table Field**: Improved json-table field type with better styling, user experience, and data editing capabilities
|
|
17
21
|
- **Dynamic Action Labels**: GenericDetail component now supports dynamic action labels and custom action buttons for better user interaction
|
|
18
22
|
- **Dependency Updates**: Updated lucide-react to version 0.536.0 for improved icon consistency and performance
|
|
@@ -3379,6 +3383,29 @@ npm run build
|
|
|
3379
3383
|
yarn build
|
|
3380
3384
|
```
|
|
3381
3385
|
|
|
3386
|
+
## Migration Notes
|
|
3387
|
+
|
|
3388
|
+
### StandardModal Migration (v5.15.3) - COMPLETED
|
|
3389
|
+
|
|
3390
|
+
We have successfully migrated all components from `reactjs-popup` to our standardized `StandardModal` component for improved consistency and better user experience.
|
|
3391
|
+
|
|
3392
|
+
**All Migrations Completed ✅:**
|
|
3393
|
+
- ✅ **GenericQuote**: Updated to use StandardModal for form modals
|
|
3394
|
+
- ✅ **StandardModal**: Refactored to pure React implementation with enhanced accessibility
|
|
3395
|
+
- ✅ **Field.jsx**: Migrated 2 Popup instances (childFormShow and canvasPopupOpen)
|
|
3396
|
+
- ✅ **GenericFormBuilder.jsx**: Migrated 2 Popup instances (modalShow and modalFormShow)
|
|
3397
|
+
- ✅ **DropZone.jsx**: Migrated 1 Popup instance (modalShow)
|
|
3398
|
+
- ✅ **modals/GalleryModal.jsx**: Migrated 1 Popup instance (modalGalleryShow) with custom overlay styling
|
|
3399
|
+
- ✅ **QuickAction.jsx**: Migrated 1 Popup instance (modalShow)
|
|
3400
|
+
- ✅ **Call.jsx**: Migrated 1 Popup instance (modalShow)
|
|
3401
|
+
|
|
3402
|
+
**Benefits of StandardModal:**
|
|
3403
|
+
- Consistent modal behavior across all components
|
|
3404
|
+
- Better keyboard navigation and accessibility
|
|
3405
|
+
- Unified styling and responsive design
|
|
3406
|
+
- No external dependency on reactjs-popup
|
|
3407
|
+
- Enhanced mobile support with virtual keyboard accommodation
|
|
3408
|
+
|
|
3382
3409
|
## Contributing
|
|
3383
3410
|
|
|
3384
3411
|
We welcome contributions! Please follow these steps:
|
|
@@ -3633,6 +3660,23 @@ All existing GenericQuote functionality remains unchanged:
|
|
|
3633
3660
|
|
|
3634
3661
|
Proposal features are **opt-in** and activated only when `proposalConfig.enabled: true` is set.
|
|
3635
3662
|
|
|
3663
|
+
**Recent Improvements (v5.15.3 & 2025 Updates):**
|
|
3664
|
+
- **StandardModal Integration**: Replaced reactjs-popup with StandardModal for consistent modal behavior and better maintainability
|
|
3665
|
+
- **HTML Content Rendering**: Terms and conditions now support rich HTML content from TinyMCE editors with proper parsing via `html-react-parser`
|
|
3666
|
+
- **Typography Optimization**: Refined font sizing (14px desktop, 12px mobile) for better visual hierarchy and page consistency
|
|
3667
|
+
- **Enhanced Styling**: Improved CSS for HTML elements including lists, paragraphs, and formatted text in terms sections
|
|
3668
|
+
- **Complete Modal Consistency**: All modals throughout the library now use the same StandardModal component
|
|
3669
|
+
|
|
3670
|
+
**Latest 2025 Enhancements:**
|
|
3671
|
+
- **Enhanced Content Editor Modal**: Increased modal dimensions (98% width, 95% height) for better TinyMCE integration
|
|
3672
|
+
- **Streamlined Section Management**: Removed unnecessary section type selector from Edit Proposal Content modal
|
|
3673
|
+
- **Maximized Editor Space**: TinyMCE editor height increased from 400px to 500px with improved container sizing
|
|
3674
|
+
- **Dynamic Template Integration**: Template section titles now properly populate from backend configuration instead of hardcoded values
|
|
3675
|
+
- **Improved Content Flow**: Fixed proposal content insertion ensuring custom content appears correctly in generated PDFs
|
|
3676
|
+
- **Professional PDF Layout**: Enhanced margin handling with 40px/50px margins for all sections (except cover page preservation)
|
|
3677
|
+
- **Branding Color System**: Automatic application of branding profile colors (H1=primary, H2=secondary, H3=accent)
|
|
3678
|
+
- **Robust PDF Generation**: Improved error handling and blank page prevention during proposal PDF creation
|
|
3679
|
+
|
|
3636
3680
|
**Advanced Usage Example:**
|
|
3637
3681
|
|
|
3638
3682
|
```jsx
|
package/package.json
CHANGED
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
91
91
|
},
|
|
92
92
|
"name": "@visns-studio/visns-components",
|
|
93
|
-
"version": "5.15.
|
|
93
|
+
"version": "5.15.4",
|
|
94
94
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
95
95
|
"main": "src/index.js",
|
|
96
96
|
"files": [
|
package/src/components/Call.jsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
|
-
import
|
|
2
|
+
import StandardModal from "./generic/StandardModal";
|
|
3
3
|
import moment from "moment";
|
|
4
4
|
|
|
5
5
|
import CustomFetch from "./Fetch";
|
|
@@ -124,12 +124,14 @@ function CallPop(props) {
|
|
|
124
124
|
</button>
|
|
125
125
|
</div>
|
|
126
126
|
</div>
|
|
127
|
-
<
|
|
128
|
-
|
|
127
|
+
<StandardModal
|
|
128
|
+
isOpen={modalShow}
|
|
129
129
|
onClose={() => {
|
|
130
130
|
setCallShow(false);
|
|
131
131
|
setModalShow(false);
|
|
132
132
|
}}
|
|
133
|
+
size="medium"
|
|
134
|
+
closeOnDocumentClick={false}
|
|
133
135
|
>
|
|
134
136
|
<div className="modalwrap">
|
|
135
137
|
<div className="grid">
|
|
@@ -210,7 +212,7 @@ function CallPop(props) {
|
|
|
210
212
|
</div>
|
|
211
213
|
</div>
|
|
212
214
|
</div>
|
|
213
|
-
</
|
|
215
|
+
</StandardModal>
|
|
214
216
|
</>
|
|
215
217
|
) : null}
|
|
216
218
|
</>
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from 'lucide-react';
|
|
16
16
|
import { toast } from 'react-toastify';
|
|
17
17
|
import { arrayMoveImmutable } from 'array-move';
|
|
18
|
-
import
|
|
18
|
+
import StandardModal from './generic/StandardModal';
|
|
19
19
|
import imageCompression from 'browser-image-compression';
|
|
20
20
|
|
|
21
21
|
import { confirmDialog } from './utils/ConfirmDialog';
|
|
@@ -637,9 +637,10 @@ function VisnsDropZone({ fetchData, files, settings, url, urlKey, deleteUrl, ent
|
|
|
637
637
|
</div>
|
|
638
638
|
) : null}
|
|
639
639
|
</div>
|
|
640
|
-
<
|
|
641
|
-
|
|
640
|
+
<StandardModal
|
|
641
|
+
isOpen={modalShow}
|
|
642
642
|
onClose={modalClose}
|
|
643
|
+
size="large"
|
|
643
644
|
closeOnDocumentClick={false}
|
|
644
645
|
>
|
|
645
646
|
<div className={`${styles.modalwrap} top--modal`}>
|
|
@@ -704,7 +705,7 @@ function VisnsDropZone({ fetchData, files, settings, url, urlKey, deleteUrl, ent
|
|
|
704
705
|
</div>
|
|
705
706
|
</div>
|
|
706
707
|
</div>
|
|
707
|
-
</
|
|
708
|
+
</StandardModal>
|
|
708
709
|
</>
|
|
709
710
|
);
|
|
710
711
|
}
|
package/src/components/Field.jsx
CHANGED
|
@@ -6,7 +6,7 @@ import _ from 'lodash';
|
|
|
6
6
|
import Toggle from 'react-toggle';
|
|
7
7
|
import moment from 'moment';
|
|
8
8
|
import parse from 'html-react-parser';
|
|
9
|
-
import
|
|
9
|
+
import StandardModal from './generic/StandardModal';
|
|
10
10
|
import { Sketch } from '@uiw/react-color';
|
|
11
11
|
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
|
12
12
|
import { NumericFormat, PatternFormat } from 'react-number-format';
|
|
@@ -3095,12 +3095,11 @@ function Field({
|
|
|
3095
3095
|
{renderFieldLabel()}
|
|
3096
3096
|
</label>
|
|
3097
3097
|
{renderAdditionalContainer()}
|
|
3098
|
-
<
|
|
3099
|
-
|
|
3098
|
+
<StandardModal
|
|
3099
|
+
isOpen={childFormShow}
|
|
3100
3100
|
onClose={childFormClose}
|
|
3101
|
+
size="large"
|
|
3101
3102
|
closeOnDocumentClick={false}
|
|
3102
|
-
overlayStyle={{ zIndex: 10001 }}
|
|
3103
|
-
contentStyle={{ zIndex: 10002 }}
|
|
3104
3103
|
>
|
|
3105
3104
|
<Form
|
|
3106
3105
|
ajaxSetting={childForm.ajaxSetting}
|
|
@@ -3118,13 +3117,12 @@ function Field({
|
|
|
3118
3117
|
updateForm={setChildForm}
|
|
3119
3118
|
userProfile={userProfile}
|
|
3120
3119
|
/>
|
|
3121
|
-
</
|
|
3122
|
-
<
|
|
3123
|
-
|
|
3120
|
+
</StandardModal>
|
|
3121
|
+
<StandardModal
|
|
3122
|
+
isOpen={canvasPopupOpen}
|
|
3124
3123
|
onClose={() => setCanvasPopupOpen(false)}
|
|
3124
|
+
size="large"
|
|
3125
3125
|
closeOnDocumentClick={false}
|
|
3126
|
-
overlayStyle={{ zIndex: 10001 }}
|
|
3127
|
-
contentStyle={{ zIndex: 10002 }}
|
|
3128
3126
|
>
|
|
3129
3127
|
<div className={`${styles.modalwrap} ${styles['top--modal']}`}>
|
|
3130
3128
|
<div className={styles.modal}>
|
|
@@ -3240,7 +3238,7 @@ function Field({
|
|
|
3240
3238
|
</div>
|
|
3241
3239
|
</div>
|
|
3242
3240
|
</div>
|
|
3243
|
-
</
|
|
3241
|
+
</StandardModal>
|
|
3244
3242
|
</div>
|
|
3245
3243
|
);
|
|
3246
3244
|
}
|
|
@@ -6,7 +6,7 @@ import React, {
|
|
|
6
6
|
useMemo,
|
|
7
7
|
} from 'react';
|
|
8
8
|
import { useNavigate } from 'react-router-dom';
|
|
9
|
-
import
|
|
9
|
+
import StandardModal from './generic/StandardModal';
|
|
10
10
|
import { Search, X, Zap } from 'lucide-react';
|
|
11
11
|
|
|
12
12
|
import CustomFetch from './Fetch';
|
|
@@ -408,20 +408,11 @@ const QuickAction = ({ setting }) => {
|
|
|
408
408
|
</div>
|
|
409
409
|
</div>
|
|
410
410
|
|
|
411
|
-
<
|
|
412
|
-
|
|
411
|
+
<StandardModal
|
|
412
|
+
isOpen={modalShow}
|
|
413
413
|
onClose={modalClose}
|
|
414
|
+
size="large"
|
|
414
415
|
closeOnDocumentClick={false}
|
|
415
|
-
contentStyle={{
|
|
416
|
-
width: '80vw',
|
|
417
|
-
maxWidth: '1200px',
|
|
418
|
-
minWidth: '600px',
|
|
419
|
-
padding: 0,
|
|
420
|
-
border: 'none',
|
|
421
|
-
borderRadius: '8px',
|
|
422
|
-
overflow: 'hidden',
|
|
423
|
-
maxHeight: '90vh',
|
|
424
|
-
}}
|
|
425
416
|
>
|
|
426
417
|
<Form
|
|
427
418
|
closeModal={modalClose}
|
|
@@ -431,7 +422,7 @@ const QuickAction = ({ setting }) => {
|
|
|
431
422
|
columnId={formId}
|
|
432
423
|
childDropdownCallback={childDropdownCallback}
|
|
433
424
|
/>
|
|
434
|
-
</
|
|
425
|
+
</StandardModal>
|
|
435
426
|
</>
|
|
436
427
|
);
|
|
437
428
|
};
|
|
@@ -2,7 +2,7 @@ import '../styles/global.css';
|
|
|
2
2
|
|
|
3
3
|
import React, { useEffect, useRef, useState } from 'react';
|
|
4
4
|
import { useParams } from 'react-router-dom';
|
|
5
|
-
import
|
|
5
|
+
import StandardModal from './StandardModal';
|
|
6
6
|
import { arrayMove } from '@dnd-kit/sortable';
|
|
7
7
|
import {
|
|
8
8
|
DndContext,
|
|
@@ -1222,12 +1222,12 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1222
1222
|
</div>
|
|
1223
1223
|
</div>
|
|
1224
1224
|
|
|
1225
|
-
<
|
|
1226
|
-
|
|
1225
|
+
<StandardModal
|
|
1226
|
+
isOpen={modalShow}
|
|
1227
1227
|
onClose={handleCloseModal}
|
|
1228
|
+
size="medium"
|
|
1228
1229
|
closeOnDocumentClick={false}
|
|
1229
1230
|
>
|
|
1230
|
-
{modalShow && <div className={styles.modalBackdrop} />}
|
|
1231
1231
|
<div className={styles.modalwrap}>
|
|
1232
1232
|
<div className={styles.modal}>
|
|
1233
1233
|
<div className={styles.modal__header}>
|
|
@@ -2254,14 +2254,14 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
2254
2254
|
</div>
|
|
2255
2255
|
</div>
|
|
2256
2256
|
</div>
|
|
2257
|
-
</
|
|
2257
|
+
</StandardModal>
|
|
2258
2258
|
|
|
2259
|
-
<
|
|
2260
|
-
|
|
2259
|
+
<StandardModal
|
|
2260
|
+
isOpen={modalFormShow}
|
|
2261
2261
|
onClose={handleCloseModalForm}
|
|
2262
|
+
size="medium"
|
|
2262
2263
|
closeOnDocumentClick={false}
|
|
2263
2264
|
>
|
|
2264
|
-
{modalFormShow && <div className={styles.modalBackdrop} />}
|
|
2265
2265
|
<div className={styles.modalwrap}>
|
|
2266
2266
|
<div className={styles.modal}>
|
|
2267
2267
|
<div className={styles.modal__header}>
|
|
@@ -2308,7 +2308,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
2308
2308
|
</div>
|
|
2309
2309
|
</div>
|
|
2310
2310
|
</div>
|
|
2311
|
-
</
|
|
2311
|
+
</StandardModal>
|
|
2312
2312
|
|
|
2313
2313
|
{/* React Tooltips */}
|
|
2314
2314
|
<Tooltip
|
|
@@ -2,11 +2,12 @@ import '../styles/global.css';
|
|
|
2
2
|
|
|
3
3
|
import React, { useState, useEffect } from 'react';
|
|
4
4
|
import { useParams, useNavigate } from 'react-router-dom';
|
|
5
|
-
import
|
|
5
|
+
import StandardModal from './StandardModal';
|
|
6
6
|
import moment from 'moment';
|
|
7
7
|
import get from 'lodash/get';
|
|
8
8
|
import { saveAs } from 'file-saver';
|
|
9
9
|
import { toast } from 'react-toastify';
|
|
10
|
+
import parse from 'html-react-parser';
|
|
10
11
|
import { arrayMoveImmutable } from 'array-move';
|
|
11
12
|
import { showConfirmDialog } from './ConfirmationDialog';
|
|
12
13
|
import { FileText, DollarSign, Clock, Eye, X, Edit3, FileEdit, Save, Settings } from 'lucide-react';
|
|
@@ -86,7 +87,6 @@ function GenericQuote({ logo, setting, urlParam, proposalConfig = null }) {
|
|
|
86
87
|
// Content editor state
|
|
87
88
|
const [showContentEditor, setShowContentEditor] = useState(false);
|
|
88
89
|
const [proposalContent, setProposalContent] = useState('');
|
|
89
|
-
const [contentSectionType, setContentSectionType] = useState('content');
|
|
90
90
|
|
|
91
91
|
// Modal state variables
|
|
92
92
|
const [modalShow, setModalShow] = useState(false);
|
|
@@ -1029,8 +1029,7 @@ function GenericQuote({ logo, setting, urlParam, proposalConfig = null }) {
|
|
|
1029
1029
|
`/ajax/quotes/${data.id}/proposal-content`,
|
|
1030
1030
|
'POST',
|
|
1031
1031
|
{
|
|
1032
|
-
content: proposalContent
|
|
1033
|
-
section_type: contentSectionType
|
|
1032
|
+
content: proposalContent
|
|
1034
1033
|
}
|
|
1035
1034
|
);
|
|
1036
1035
|
|
|
@@ -1569,22 +1568,6 @@ function GenericQuote({ logo, setting, urlParam, proposalConfig = null }) {
|
|
|
1569
1568
|
</button>
|
|
1570
1569
|
</div>
|
|
1571
1570
|
<div className={styles.contentEditorBody}>
|
|
1572
|
-
<div className={styles.editorSection}>
|
|
1573
|
-
<label className={styles.editorLabel}>
|
|
1574
|
-
<Settings size={16} />
|
|
1575
|
-
Section Type:
|
|
1576
|
-
<select
|
|
1577
|
-
value={contentSectionType}
|
|
1578
|
-
onChange={(e) => setContentSectionType(e.target.value)}
|
|
1579
|
-
className={styles.sectionTypeSelect}
|
|
1580
|
-
>
|
|
1581
|
-
<option value="content">Content Section</option>
|
|
1582
|
-
<option value="overview">Overview Section</option>
|
|
1583
|
-
<option value="solution">Solution Section</option>
|
|
1584
|
-
<option value="benefits">Benefits Section</option>
|
|
1585
|
-
</select>
|
|
1586
|
-
</label>
|
|
1587
|
-
</div>
|
|
1588
1571
|
<div className={styles.editorContainer}>
|
|
1589
1572
|
<Editor
|
|
1590
1573
|
apiKey="qagffr3pkuv17a8on1afax661irst1hbr4e6tbv888sz91jc"
|
|
@@ -1592,7 +1575,7 @@ function GenericQuote({ logo, setting, urlParam, proposalConfig = null }) {
|
|
|
1592
1575
|
onEditorChange={handleContentChange}
|
|
1593
1576
|
init={{
|
|
1594
1577
|
height: '100%',
|
|
1595
|
-
min_height:
|
|
1578
|
+
min_height: 500,
|
|
1596
1579
|
resize: false,
|
|
1597
1580
|
menubar: true,
|
|
1598
1581
|
plugins: [
|
|
@@ -1988,61 +1971,57 @@ function GenericQuote({ logo, setting, urlParam, proposalConfig = null }) {
|
|
|
1988
1971
|
Terms and Conditions
|
|
1989
1972
|
</h3>
|
|
1990
1973
|
<div className={styles.termsContent}>
|
|
1991
|
-
{data.terms_and_condition}
|
|
1974
|
+
{parse(data.terms_and_condition || '')}
|
|
1992
1975
|
</div>
|
|
1993
1976
|
</div>
|
|
1994
1977
|
)}
|
|
1995
1978
|
</div>
|
|
1996
1979
|
|
|
1997
1980
|
{/* Form Modal */}
|
|
1998
|
-
<
|
|
1999
|
-
|
|
1981
|
+
<StandardModal
|
|
1982
|
+
isOpen={modalShow}
|
|
2000
1983
|
onClose={modalClose}
|
|
1984
|
+
size="large"
|
|
2001
1985
|
closeOnDocumentClick={false}
|
|
2002
|
-
contentStyle={{ width: '80vw', maxWidth: '1200px' }}
|
|
2003
1986
|
>
|
|
2004
|
-
<
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
);
|
|
2028
|
-
}
|
|
2029
|
-
}
|
|
2030
|
-
};
|
|
2031
|
-
fetchData();
|
|
2032
|
-
}}
|
|
2033
|
-
formSettings={formData}
|
|
2034
|
-
formType={formType}
|
|
2035
|
-
style={form.style || {}}
|
|
2036
|
-
updateForm={setFormData}
|
|
2037
|
-
paramValue={
|
|
2038
|
-
routeParams[urlParam]
|
|
2039
|
-
? routeParams[urlParam]
|
|
2040
|
-
: 0
|
|
1987
|
+
<Form
|
|
1988
|
+
closeModal={modalClose}
|
|
1989
|
+
columnId={formId}
|
|
1990
|
+
fetchTable={() => {
|
|
1991
|
+
// Reload the quote data after form submission
|
|
1992
|
+
const fetchData = async () => {
|
|
1993
|
+
if (
|
|
1994
|
+
routeParams[urlParam] &&
|
|
1995
|
+
routeParams[urlParam] > 0
|
|
1996
|
+
) {
|
|
1997
|
+
try {
|
|
1998
|
+
const res = await CustomFetch(
|
|
1999
|
+
`${page.fetchUrl}/${routeParams[urlParam]}`,
|
|
2000
|
+
'GET',
|
|
2001
|
+
{}
|
|
2002
|
+
);
|
|
2003
|
+
setData(res.data);
|
|
2004
|
+
} catch (error) {
|
|
2005
|
+
console.error(
|
|
2006
|
+
'Error fetching data:',
|
|
2007
|
+
error
|
|
2008
|
+
);
|
|
2009
|
+
}
|
|
2041
2010
|
}
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2011
|
+
};
|
|
2012
|
+
fetchData();
|
|
2013
|
+
}}
|
|
2014
|
+
formSettings={formData}
|
|
2015
|
+
formType={formType}
|
|
2016
|
+
style={form.style || {}}
|
|
2017
|
+
updateForm={setFormData}
|
|
2018
|
+
paramValue={
|
|
2019
|
+
routeParams[urlParam]
|
|
2020
|
+
? routeParams[urlParam]
|
|
2021
|
+
: 0
|
|
2022
|
+
}
|
|
2023
|
+
/>
|
|
2024
|
+
</StandardModal>
|
|
2046
2025
|
</div>
|
|
2047
2026
|
);
|
|
2048
2027
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import Popup from 'reactjs-popup';
|
|
1
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
3
2
|
import { X } from 'lucide-react';
|
|
4
3
|
import formStyles from '../styles/Form.module.scss';
|
|
5
4
|
|
|
@@ -67,6 +66,37 @@ const StandardModal = ({
|
|
|
67
66
|
window.addEventListener('resize', handleResize);
|
|
68
67
|
return () => window.removeEventListener('resize', handleResize);
|
|
69
68
|
}, []);
|
|
69
|
+
|
|
70
|
+
// Handle keyboard events
|
|
71
|
+
const handleKeyDown = useCallback((e) => {
|
|
72
|
+
if (!isOpen) return;
|
|
73
|
+
|
|
74
|
+
if (e.key === 'Escape' && closeOnEscape) {
|
|
75
|
+
onClose();
|
|
76
|
+
}
|
|
77
|
+
}, [isOpen, closeOnEscape, onClose]);
|
|
78
|
+
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (isOpen) {
|
|
81
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
82
|
+
document.body.style.overflow = 'hidden';
|
|
83
|
+
} else {
|
|
84
|
+
document.body.style.overflow = 'unset';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return () => {
|
|
88
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
89
|
+
document.body.style.overflow = 'unset';
|
|
90
|
+
};
|
|
91
|
+
}, [isOpen, handleKeyDown]);
|
|
92
|
+
|
|
93
|
+
// Handle backdrop click
|
|
94
|
+
const handleBackdropClick = (e) => {
|
|
95
|
+
if (closeOnDocumentClick && e.target === e.currentTarget) {
|
|
96
|
+
onClose();
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
70
100
|
// Apply vertical alignment class if needed
|
|
71
101
|
const getModalClasses = () => {
|
|
72
102
|
let classes = formStyles.modal;
|
|
@@ -76,32 +106,47 @@ const StandardModal = ({
|
|
|
76
106
|
return classes;
|
|
77
107
|
};
|
|
78
108
|
|
|
79
|
-
// Get
|
|
80
|
-
const
|
|
109
|
+
// Get overlay styles
|
|
110
|
+
const getOverlayStyle = () => {
|
|
81
111
|
const baseStyle = {
|
|
82
|
-
|
|
112
|
+
position: 'fixed',
|
|
113
|
+
top: 0,
|
|
114
|
+
left: 0,
|
|
115
|
+
right: 0,
|
|
116
|
+
bottom: 0,
|
|
117
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
118
|
+
display: 'flex',
|
|
119
|
+
alignItems: verticalAlign === 'top' ? 'flex-start' : 'center',
|
|
120
|
+
justifyContent: 'center',
|
|
121
|
+
zIndex: 1000,
|
|
122
|
+
padding: '20px',
|
|
123
|
+
...customStyles.overlay
|
|
83
124
|
};
|
|
84
125
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (isMobile || isTablet) {
|
|
88
|
-
// Position modal higher on mobile/tablet to accommodate virtual keyboard
|
|
89
|
-
positioningStyle = {
|
|
90
|
-
top: '5%', // Position higher (5% from top)
|
|
91
|
-
left: '50%', // Center horizontally
|
|
92
|
-
transform: 'translateX(-50%)', // Only center horizontally, no vertical transform
|
|
93
|
-
maxHeight: '85vh', // Limit height to prevent overflow
|
|
94
|
-
overflowY: 'auto', // Allow scrolling within modal if needed
|
|
95
|
-
position: 'fixed' // Ensure fixed positioning for proper centering
|
|
96
|
-
};
|
|
126
|
+
if (verticalAlign === 'top') {
|
|
127
|
+
baseStyle.paddingTop = '5%';
|
|
97
128
|
}
|
|
98
129
|
|
|
130
|
+
return baseStyle;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Get size-based styles for the modal content with device-aware positioning
|
|
134
|
+
const getContentStyle = () => {
|
|
135
|
+
const baseStyle = {
|
|
136
|
+
position: 'relative',
|
|
137
|
+
backgroundColor: 'white',
|
|
138
|
+
borderRadius: '8px',
|
|
139
|
+
boxShadow: '0 10px 30px rgba(0, 0, 0, 0.3)',
|
|
140
|
+
maxHeight: isMobile || isTablet ? '85vh' : '90vh',
|
|
141
|
+
overflowY: 'auto',
|
|
142
|
+
...customStyles.modal
|
|
143
|
+
};
|
|
144
|
+
|
|
99
145
|
// Apply size-based styles with device considerations
|
|
100
146
|
switch (size) {
|
|
101
147
|
case 'small':
|
|
102
148
|
return {
|
|
103
149
|
...baseStyle,
|
|
104
|
-
...positioningStyle,
|
|
105
150
|
width: isMobile ? '95vw' : '400px',
|
|
106
151
|
minWidth: isMobile ? '300px' : '400px',
|
|
107
152
|
maxWidth: isMobile ? '95vw' : '600px'
|
|
@@ -109,7 +154,6 @@ const StandardModal = ({
|
|
|
109
154
|
case 'large':
|
|
110
155
|
return {
|
|
111
156
|
...baseStyle,
|
|
112
|
-
...positioningStyle,
|
|
113
157
|
width: '95vw',
|
|
114
158
|
minWidth: isMobile ? '300px' : '800px',
|
|
115
159
|
maxWidth: isMobile ? '95vw' : '1600px'
|
|
@@ -118,51 +162,55 @@ const StandardModal = ({
|
|
|
118
162
|
default:
|
|
119
163
|
return {
|
|
120
164
|
...baseStyle,
|
|
121
|
-
|
|
122
|
-
width: isMobile ? '95vw' : '90vw',
|
|
165
|
+
width: isMobile ? '95vw' : '80vw',
|
|
123
166
|
minWidth: isMobile ? '300px' : '600px',
|
|
124
|
-
maxWidth: isMobile ? '95vw' : '
|
|
167
|
+
maxWidth: isMobile ? '95vw' : '1200px'
|
|
125
168
|
};
|
|
126
169
|
}
|
|
127
170
|
};
|
|
128
171
|
|
|
172
|
+
if (!isOpen) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
129
176
|
return (
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
modal
|
|
134
|
-
closeOnDocumentClick={closeOnDocumentClick}
|
|
135
|
-
closeOnEscape={closeOnEscape}
|
|
136
|
-
contentStyle={getContentStyle()}
|
|
177
|
+
<div
|
|
178
|
+
style={getOverlayStyle()}
|
|
179
|
+
onClick={handleBackdropClick}
|
|
137
180
|
>
|
|
138
|
-
<div
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
style={customStyles.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
181
|
+
<div
|
|
182
|
+
style={getContentStyle()}
|
|
183
|
+
onClick={(e) => e.stopPropagation()}
|
|
184
|
+
>
|
|
185
|
+
<div className={formStyles.modalwrap}>
|
|
186
|
+
<div
|
|
187
|
+
className={getModalClasses()}
|
|
188
|
+
>
|
|
189
|
+
{showHeader && (
|
|
190
|
+
<div className={formStyles.modal__header} style={customStyles.header}>
|
|
191
|
+
<h1 style={customStyles.title}>{title}</h1>
|
|
192
|
+
<button
|
|
193
|
+
className={formStyles.modal__close}
|
|
194
|
+
onClick={onClose}
|
|
195
|
+
style={customStyles.closeButton}
|
|
196
|
+
type="button"
|
|
197
|
+
aria-label="Close modal"
|
|
198
|
+
>
|
|
199
|
+
<X strokeWidth={1} size={24} />
|
|
200
|
+
</button>
|
|
201
|
+
</div>
|
|
202
|
+
)}
|
|
203
|
+
{showHeader ? (
|
|
204
|
+
<div className={formStyles.modal__content} style={customStyles.content}>
|
|
205
|
+
{children}
|
|
206
|
+
</div>
|
|
207
|
+
) : (
|
|
208
|
+
children
|
|
209
|
+
)}
|
|
210
|
+
</div>
|
|
163
211
|
</div>
|
|
164
212
|
</div>
|
|
165
|
-
</
|
|
213
|
+
</div>
|
|
166
214
|
);
|
|
167
215
|
};
|
|
168
216
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
|
-
import
|
|
2
|
+
import StandardModal from '../generic/StandardModal';
|
|
3
3
|
import { toast } from 'react-toastify';
|
|
4
4
|
import imageCompression from 'browser-image-compression';
|
|
5
5
|
// import Vapor from 'laravel-vapor'; // Commented out to avoid build issues
|
|
@@ -169,22 +169,15 @@ const GalleryModal = ({
|
|
|
169
169
|
|
|
170
170
|
return (
|
|
171
171
|
<>
|
|
172
|
-
<
|
|
173
|
-
|
|
172
|
+
<StandardModal
|
|
173
|
+
isOpen={modalGalleryShow}
|
|
174
174
|
onClose={modalGalleryClose}
|
|
175
|
+
size="large"
|
|
175
176
|
closeOnDocumentClick={false}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
padding: 0,
|
|
181
|
-
border: 'none',
|
|
182
|
-
borderRadius: '8px',
|
|
183
|
-
overflow: 'hidden',
|
|
184
|
-
maxHeight: '90vh',
|
|
185
|
-
}}
|
|
186
|
-
overlayStyle={{
|
|
187
|
-
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
177
|
+
customStyles={{
|
|
178
|
+
overlay: {
|
|
179
|
+
backgroundColor: 'rgba(0, 0, 0, 0.7)'
|
|
180
|
+
}
|
|
188
181
|
}}
|
|
189
182
|
>
|
|
190
183
|
<div className={styles.modalwrap}>
|
|
@@ -390,7 +383,7 @@ const GalleryModal = ({
|
|
|
390
383
|
</div>
|
|
391
384
|
</div>
|
|
392
385
|
</div>
|
|
393
|
-
</
|
|
386
|
+
</StandardModal>
|
|
394
387
|
|
|
395
388
|
{/* Lightbox for full-size image viewing */}
|
|
396
389
|
<Lightbox
|
|
@@ -246,10 +246,10 @@
|
|
|
246
246
|
background: white;
|
|
247
247
|
border-radius: 12px;
|
|
248
248
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
|
249
|
-
width:
|
|
250
|
-
max-width:
|
|
251
|
-
height:
|
|
252
|
-
max-height:
|
|
249
|
+
width: 98%;
|
|
250
|
+
max-width: 1600px;
|
|
251
|
+
height: 95vh;
|
|
252
|
+
max-height: 95vh;
|
|
253
253
|
display: flex;
|
|
254
254
|
flex-direction: column;
|
|
255
255
|
overflow: hidden;
|
|
@@ -313,7 +313,7 @@
|
|
|
313
313
|
|
|
314
314
|
.contentEditorBody {
|
|
315
315
|
flex: 1;
|
|
316
|
-
padding:
|
|
316
|
+
padding: 12px;
|
|
317
317
|
overflow: hidden;
|
|
318
318
|
background: var(--tertiary-color);
|
|
319
319
|
display: flex;
|
|
@@ -374,7 +374,8 @@
|
|
|
374
374
|
flex: 1; /* Take all remaining space */
|
|
375
375
|
display: flex;
|
|
376
376
|
flex-direction: column;
|
|
377
|
-
min-height:
|
|
377
|
+
min-height: 500px; /* Ensure minimum height for editor */
|
|
378
|
+
height: 100%; /* Fill available height */
|
|
378
379
|
|
|
379
380
|
/* TinyMCE overrides */
|
|
380
381
|
:global(.tox-tinymce) {
|
|
@@ -1061,10 +1062,49 @@
|
|
|
1061
1062
|
}
|
|
1062
1063
|
|
|
1063
1064
|
.termsContent {
|
|
1064
|
-
font-size:
|
|
1065
|
-
line-height: 1.
|
|
1065
|
+
font-size: 14px;
|
|
1066
|
+
line-height: 1.4;
|
|
1066
1067
|
color: var(--paragraph-color);
|
|
1067
|
-
|
|
1068
|
+
|
|
1069
|
+
/* Reset styles to avoid conflicts with TinyMCE HTML content */
|
|
1070
|
+
white-space: normal;
|
|
1071
|
+
|
|
1072
|
+
/* Basic styling for TinyMCE HTML elements */
|
|
1073
|
+
ul, ol {
|
|
1074
|
+
margin: 8px 0;
|
|
1075
|
+
padding-left: 20px;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
li {
|
|
1079
|
+
margin: 4px 0;
|
|
1080
|
+
padding: 0;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
p {
|
|
1084
|
+
margin: 8px 0;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
strong, b {
|
|
1088
|
+
font-weight: 600;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
em, i {
|
|
1092
|
+
font-style: italic;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/* Remove excessive spacing from nested elements */
|
|
1096
|
+
ul ul, ol ol, ul ol, ol ul {
|
|
1097
|
+
margin: 2px 0;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
/* Ensure proper spacing for the first and last elements */
|
|
1101
|
+
> *:first-child {
|
|
1102
|
+
margin-top: 0;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
> *:last-child {
|
|
1106
|
+
margin-bottom: 0;
|
|
1107
|
+
}
|
|
1068
1108
|
}
|
|
1069
1109
|
|
|
1070
1110
|
.quoteTableSection {
|
|
@@ -1387,7 +1427,7 @@
|
|
|
1387
1427
|
}
|
|
1388
1428
|
|
|
1389
1429
|
.termsContent {
|
|
1390
|
-
font-size:
|
|
1391
|
-
line-height: 1.
|
|
1430
|
+
font-size: 12px;
|
|
1431
|
+
line-height: 1.3;
|
|
1392
1432
|
}
|
|
1393
1433
|
}
|