@visns-studio/visns-components 5.14.7 → 5.14.9
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 +59 -0
- package/package.json +1 -1
- package/src/components/DataGrid.jsx +24 -47
- package/src/components/generic/GenericDashboard.jsx +42 -56
- package/src/components/generic/GenericDetail.jsx +85 -60
- package/src/components/generic/GenericIndex.jsx +22 -37
- package/src/components/generic/NotificationList.jsx +8 -24
- package/src/components/generic/StandardModal.jsx +119 -0
- package/src/components/styles/DataGrid.module.scss +3 -2
- package/src/components/styles/Form.module.scss +9 -2
- package/src/components/styles/GenericDetail.module.scss +24 -0
- package/src/components/styles/GenericIndex.module.scss +24 -0
- package/src/components/styles/Notification.module.scss +94 -98
- package/src/components/styles/NotificationList.module.scss +2 -30
- package/src/components/styles/SweetAlert.module.css +21 -2
- package/src/components/styles/global.css +0 -5
- package/src/index.js +2 -0
package/README.md
CHANGED
|
@@ -50,6 +50,7 @@ VISNS Components is a React-based UI component library that provides a set of re
|
|
|
50
50
|
- **Download** - File download functionality
|
|
51
51
|
- **Loader** - Loading indicators
|
|
52
52
|
- **Notification** - Toast notifications
|
|
53
|
+
- **StandardModal** - Reusable modal component with consistent styling
|
|
53
54
|
- **Call Popup** - Modal dialogs
|
|
54
55
|
- **QR Code** - QR code generation and printing
|
|
55
56
|
|
|
@@ -2312,6 +2313,64 @@ The QrCode component fetches QR code data from the specified URL and displays it
|
|
|
2312
2313
|
}
|
|
2313
2314
|
```
|
|
2314
2315
|
|
|
2316
|
+
#### StandardModal
|
|
2317
|
+
|
|
2318
|
+
A reusable modal component that standardizes the look and feel across all components. Uses the same styling as GenericIndex, GenericDetail, and DataGrid modals, providing consistent modal behavior throughout the application.
|
|
2319
|
+
|
|
2320
|
+
```jsx
|
|
2321
|
+
import { StandardModal } from '@visns-studio/visns-components';
|
|
2322
|
+
|
|
2323
|
+
// Basic usage (for custom content with header)
|
|
2324
|
+
<StandardModal
|
|
2325
|
+
isOpen={showModal}
|
|
2326
|
+
onClose={() => setShowModal(false)}
|
|
2327
|
+
title="Modal Title"
|
|
2328
|
+
showHeader={true}
|
|
2329
|
+
>
|
|
2330
|
+
<div>Your custom content here</div>
|
|
2331
|
+
</StandardModal>
|
|
2332
|
+
|
|
2333
|
+
// Usage with Form components (no header - Form provides its own)
|
|
2334
|
+
<StandardModal
|
|
2335
|
+
isOpen={modalShow}
|
|
2336
|
+
onClose={modalClose}
|
|
2337
|
+
closeOnDocumentClick={false}
|
|
2338
|
+
verticalAlign="top"
|
|
2339
|
+
>
|
|
2340
|
+
<Form
|
|
2341
|
+
closeModal={modalClose}
|
|
2342
|
+
formSettings={formData}
|
|
2343
|
+
// ... other Form props
|
|
2344
|
+
/>
|
|
2345
|
+
</StandardModal>
|
|
2346
|
+
```
|
|
2347
|
+
|
|
2348
|
+
**Props:**
|
|
2349
|
+
|
|
2350
|
+
- `isOpen` (boolean): Whether the modal is open
|
|
2351
|
+
- `onClose` (function): Function to call when modal should close
|
|
2352
|
+
- `title` (string): Modal title (only used if showHeader is true)
|
|
2353
|
+
- `children` (ReactNode): Modal content
|
|
2354
|
+
- `size` (string): Modal size - 'small' (400px), 'medium' (90vw, default), 'large' (95vw)
|
|
2355
|
+
- `closeOnDocumentClick` (boolean): Whether to close on outside click (default: true)
|
|
2356
|
+
- `closeOnEscape` (boolean): Whether to close on escape key (default: true)
|
|
2357
|
+
- `verticalAlign` (string): Vertical alignment - 'center' (default) or 'top'
|
|
2358
|
+
- `showHeader` (boolean): Whether to show modal header (default: false)
|
|
2359
|
+
- `customStyles` (object): Custom styles to override defaults
|
|
2360
|
+
|
|
2361
|
+
**Size Options:**
|
|
2362
|
+
|
|
2363
|
+
- **Small**: 400px width, ideal for simple dialogs and confirmations
|
|
2364
|
+
- **Medium** (default): 90vw width with 600px min and 1400px max, matches original modal sizing
|
|
2365
|
+
- **Large**: 95vw width with 800px min and 1600px max, for very large content
|
|
2366
|
+
|
|
2367
|
+
**Important Notes:**
|
|
2368
|
+
|
|
2369
|
+
- When wrapping Form components, use `showHeader={false}` (default) since Form components render their own headers
|
|
2370
|
+
- When creating custom modals with simple content, use `showHeader={true}` to display the title and close button
|
|
2371
|
+
- The component automatically handles responsive sizing and vertical alignment
|
|
2372
|
+
- Uses the same CSS classes as existing modals (modalwrap, modal, modal__header, modal__content)
|
|
2373
|
+
|
|
2315
2374
|
### Enhanced OCR Template Component
|
|
2316
2375
|
|
|
2317
2376
|
#### OcrTemplateEnhanced (v5.14.0+)
|
package/package.json
CHANGED
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
89
89
|
},
|
|
90
90
|
"name": "@visns-studio/visns-components",
|
|
91
|
-
"version": "5.14.
|
|
91
|
+
"version": "5.14.9",
|
|
92
92
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
93
93
|
"main": "src/index.js",
|
|
94
94
|
"files": [
|
|
@@ -30,7 +30,6 @@ import React, {
|
|
|
30
30
|
import { useNavigate } from 'react-router-dom';
|
|
31
31
|
import debounce from 'lodash.debounce';
|
|
32
32
|
import moment from 'moment';
|
|
33
|
-
import Popup from 'reactjs-popup';
|
|
34
33
|
import 'reactjs-popup/dist/index.css';
|
|
35
34
|
import parse from 'html-react-parser';
|
|
36
35
|
import { saveAs } from 'file-saver';
|
|
@@ -80,6 +79,7 @@ import CustomFetch from './Fetch';
|
|
|
80
79
|
import Download from './Download';
|
|
81
80
|
import Form from './Form';
|
|
82
81
|
import CellWithTooltip from './cells/CellWithTooltip';
|
|
82
|
+
import StandardModal from './generic/StandardModal';
|
|
83
83
|
import DataGridSearch from './controls/DataGridSearch';
|
|
84
84
|
import AutoRefreshControls from './controls/AutoRefreshControls';
|
|
85
85
|
import AudioPlayer from './controls/AudioPlayer';
|
|
@@ -3714,55 +3714,32 @@ const DataGrid = forwardRef(
|
|
|
3714
3714
|
)}
|
|
3715
3715
|
|
|
3716
3716
|
<AudioPlayer audioSource={audioSource} />
|
|
3717
|
-
<
|
|
3718
|
-
|
|
3717
|
+
<StandardModal
|
|
3718
|
+
isOpen={modalShow}
|
|
3719
3719
|
onClose={modalClose}
|
|
3720
|
+
title={formData.title || 'Edit Data'}
|
|
3720
3721
|
closeOnDocumentClick={false}
|
|
3721
|
-
|
|
3722
|
-
width: windowWidth < 768 ? '95vw' : '80vw',
|
|
3723
|
-
minWidth: windowWidth < 768 ? '95vw' : '600px',
|
|
3724
|
-
maxWidth: '1200px',
|
|
3725
|
-
...(formData.verticalAlign === 'top' && {
|
|
3726
|
-
position: 'fixed',
|
|
3727
|
-
top: '5vh',
|
|
3728
|
-
left: '50%',
|
|
3729
|
-
transform: 'translateX(-50%)',
|
|
3730
|
-
margin: '0',
|
|
3731
|
-
}),
|
|
3732
|
-
}}
|
|
3733
|
-
nested
|
|
3722
|
+
verticalAlign={formData.verticalAlign || 'center'}
|
|
3734
3723
|
>
|
|
3735
|
-
<
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
}
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
mapbox={mapbox}
|
|
3755
|
-
modalOpen={modalOpen}
|
|
3756
|
-
paramValue={paramValue}
|
|
3757
|
-
style={style}
|
|
3758
|
-
updateForm={setFormData}
|
|
3759
|
-
userProfile={userProfile}
|
|
3760
|
-
bulkEditMode={bulkEditMode}
|
|
3761
|
-
bulkEditGroupData={bulkEditGroupData}
|
|
3762
|
-
/>
|
|
3763
|
-
</div>
|
|
3764
|
-
</div>
|
|
3765
|
-
</Popup>
|
|
3724
|
+
<Form
|
|
3725
|
+
ajaxSetting={ajaxSetting}
|
|
3726
|
+
api={api}
|
|
3727
|
+
closeModal={modalClose}
|
|
3728
|
+
columnId={formId}
|
|
3729
|
+
fetchTable={handleReload}
|
|
3730
|
+
formSettings={formData}
|
|
3731
|
+
formType={formType}
|
|
3732
|
+
gridRef={gridRef}
|
|
3733
|
+
mapbox={mapbox}
|
|
3734
|
+
modalOpen={modalOpen}
|
|
3735
|
+
paramValue={paramValue}
|
|
3736
|
+
style={style}
|
|
3737
|
+
updateForm={setFormData}
|
|
3738
|
+
userProfile={userProfile}
|
|
3739
|
+
bulkEditMode={bulkEditMode}
|
|
3740
|
+
bulkEditGroupData={bulkEditGroupData}
|
|
3741
|
+
/>
|
|
3742
|
+
</StandardModal>
|
|
3766
3743
|
<GalleryModal
|
|
3767
3744
|
modalGalleryShow={modalGalleryShow}
|
|
3768
3745
|
modalGalleryClose={modalGalleryClose}
|
|
@@ -4,15 +4,15 @@ import { Link, useNavigate } from 'react-router-dom';
|
|
|
4
4
|
import { ResponsiveBar } from '@nivo/bar';
|
|
5
5
|
import { ResponsiveLine } from '@nivo/line';
|
|
6
6
|
import { ResponsivePie } from '@nivo/pie';
|
|
7
|
-
import Popup from 'reactjs-popup';
|
|
8
7
|
import dayjs from 'dayjs';
|
|
9
8
|
import parse from 'html-react-parser';
|
|
10
9
|
import { createSwapy } from 'swapy';
|
|
11
|
-
import {
|
|
10
|
+
import { Minus, Plus, Star, Trash2, Eye, RotateCcw, Clock, Database, BarChart3, AlertCircle } from 'lucide-react';
|
|
12
11
|
import { confirmDialog } from '../utils/ConfirmDialog';
|
|
13
12
|
|
|
14
13
|
import CustomFetch from '../Fetch';
|
|
15
14
|
import MultiSelect from '../MultiSelect';
|
|
15
|
+
import StandardModal from './StandardModal';
|
|
16
16
|
|
|
17
17
|
import styles from '../styles/GenericDashboard.module.scss';
|
|
18
18
|
|
|
@@ -1961,63 +1961,49 @@ function GenericDashboard({ setting, userProfile, dynamicDashboard = false }) {
|
|
|
1961
1961
|
)}
|
|
1962
1962
|
<div className={styles.grid} ref={container}>
|
|
1963
1963
|
{renderWidgets()}
|
|
1964
|
-
<
|
|
1965
|
-
|
|
1964
|
+
<StandardModal
|
|
1965
|
+
isOpen={showAddWidgetModal}
|
|
1966
1966
|
onClose={() => setShowAddWidgetModal(false)}
|
|
1967
|
-
|
|
1967
|
+
title="Add a Widget"
|
|
1968
|
+
showHeader={true}
|
|
1968
1969
|
>
|
|
1969
|
-
|
|
1970
|
-
<div
|
|
1971
|
-
<
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
>
|
|
2000
|
-
{widget.title}
|
|
2001
|
-
</option>
|
|
2002
|
-
))}
|
|
2003
|
-
</select>
|
|
2004
|
-
<button
|
|
2005
|
-
className={styles.addWidgetButton}
|
|
2006
|
-
onClick={handleAddWidget}
|
|
2007
|
-
disabled={!selectedAddWidgetId}
|
|
2008
|
-
>
|
|
2009
|
-
Add
|
|
2010
|
-
</button>
|
|
2011
|
-
</div>
|
|
2012
|
-
) : (
|
|
2013
|
-
<div className={styles.noWidgetsMessage}>
|
|
2014
|
-
No more widgets available
|
|
2015
|
-
</div>
|
|
2016
|
-
)}
|
|
2017
|
-
</div>
|
|
1970
|
+
{availableWidgets.length > 0 ? (
|
|
1971
|
+
<div>
|
|
1972
|
+
<select
|
|
1973
|
+
value={selectedAddWidgetId}
|
|
1974
|
+
onChange={(e) =>
|
|
1975
|
+
setSelectedAddWidgetId(
|
|
1976
|
+
e.target.value
|
|
1977
|
+
)
|
|
1978
|
+
}
|
|
1979
|
+
className={styles.addWidgetSelect}
|
|
1980
|
+
>
|
|
1981
|
+
<option value="">
|
|
1982
|
+
-- Select a widget --
|
|
1983
|
+
</option>
|
|
1984
|
+
{availableWidgets.map((widget) => (
|
|
1985
|
+
<option
|
|
1986
|
+
key={widget.id}
|
|
1987
|
+
value={widget.id}
|
|
1988
|
+
>
|
|
1989
|
+
{widget.title}
|
|
1990
|
+
</option>
|
|
1991
|
+
))}
|
|
1992
|
+
</select>
|
|
1993
|
+
<button
|
|
1994
|
+
className={styles.addWidgetButton}
|
|
1995
|
+
onClick={handleAddWidget}
|
|
1996
|
+
disabled={!selectedAddWidgetId}
|
|
1997
|
+
>
|
|
1998
|
+
ADD
|
|
1999
|
+
</button>
|
|
2018
2000
|
</div>
|
|
2019
|
-
|
|
2020
|
-
|
|
2001
|
+
) : (
|
|
2002
|
+
<div className={styles.noWidgetsMessage}>
|
|
2003
|
+
No more widgets available
|
|
2004
|
+
</div>
|
|
2005
|
+
)}
|
|
2006
|
+
</StandardModal>
|
|
2021
2007
|
</div>
|
|
2022
2008
|
</>
|
|
2023
2009
|
);
|
|
@@ -39,7 +39,6 @@ import { Calendar, momentLocalizer } from 'react-big-calendar';
|
|
|
39
39
|
import withDragAndDrop from 'react-big-calendar/lib/addons/dragAndDrop';
|
|
40
40
|
import moment from 'moment';
|
|
41
41
|
import parse from 'html-react-parser';
|
|
42
|
-
import Popup from 'reactjs-popup';
|
|
43
42
|
import { motion } from 'framer-motion';
|
|
44
43
|
import Dropzone from 'react-dropzone';
|
|
45
44
|
import truncate from 'truncate';
|
|
@@ -79,6 +78,7 @@ import MergeEntity from '../MergeEntity';
|
|
|
79
78
|
import MultiSelect from '../MultiSelect';
|
|
80
79
|
import QrCode from '../QrCode';
|
|
81
80
|
import StagePopupModal from './StagePopupModal';
|
|
81
|
+
import StandardModal from './StandardModal';
|
|
82
82
|
import Table from '../DataGrid';
|
|
83
83
|
import TableFilter from '../TableFilter';
|
|
84
84
|
import CategorizedDropZone from '../CategorizedDropZone';
|
|
@@ -242,7 +242,7 @@ function GenericDetail({
|
|
|
242
242
|
|
|
243
243
|
/** Fileupload states */
|
|
244
244
|
const [files, setFiles] = useState([]);
|
|
245
|
-
|
|
245
|
+
|
|
246
246
|
// Debug logging for files state changes
|
|
247
247
|
useEffect(() => {
|
|
248
248
|
console.log('GenericDetail: Files state changed:', {
|
|
@@ -250,7 +250,7 @@ function GenericDetail({
|
|
|
250
250
|
filesCount: files ? files.length : 0,
|
|
251
251
|
filesType: typeof files,
|
|
252
252
|
isArray: Array.isArray(files),
|
|
253
|
-
firstFile: files && files.length > 0 ? files[0] : null
|
|
253
|
+
firstFile: files && files.length > 0 ? files[0] : null,
|
|
254
254
|
});
|
|
255
255
|
}, [files]);
|
|
256
256
|
const [loadingProgress, setLoadingProgress] = useState(0);
|
|
@@ -275,6 +275,16 @@ function GenericDetail({
|
|
|
275
275
|
const [lightboxOpen, setLightboxOpen] = useState(false);
|
|
276
276
|
const [lightboxIndex, setLightboxIndex] = useState(0);
|
|
277
277
|
|
|
278
|
+
/** Window Width State for Responsive Modal */
|
|
279
|
+
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
|
|
280
|
+
|
|
281
|
+
// Handle window resize for responsive modal
|
|
282
|
+
useEffect(() => {
|
|
283
|
+
const handleResize = () => setWindowWidth(window.innerWidth);
|
|
284
|
+
window.addEventListener('resize', handleResize);
|
|
285
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
286
|
+
}, []);
|
|
287
|
+
|
|
278
288
|
// Gallery modal close function
|
|
279
289
|
const modalGalleryClose = () => {
|
|
280
290
|
setModalGalleryShow(false);
|
|
@@ -1437,7 +1447,7 @@ function GenericDetail({
|
|
|
1437
1447
|
return renderLink();
|
|
1438
1448
|
case 'ocrTemplate':
|
|
1439
1449
|
return (
|
|
1440
|
-
<OcrTemplateEnhanced
|
|
1450
|
+
<OcrTemplateEnhanced
|
|
1441
1451
|
id={id}
|
|
1442
1452
|
data={data}
|
|
1443
1453
|
setData={setData}
|
|
@@ -1754,14 +1764,20 @@ function GenericDetail({
|
|
|
1754
1764
|
// Helper function to get nested object value using dot notation
|
|
1755
1765
|
const getNestedValue = (obj, path) => {
|
|
1756
1766
|
if (!obj || !path) return null;
|
|
1757
|
-
return path
|
|
1758
|
-
|
|
1759
|
-
|
|
1767
|
+
return path
|
|
1768
|
+
.split('.')
|
|
1769
|
+
.reduce((current, key) => {
|
|
1770
|
+
return current &&
|
|
1771
|
+
current[key] !== undefined
|
|
1772
|
+
? current[key]
|
|
1773
|
+
: null;
|
|
1774
|
+
}, obj);
|
|
1760
1775
|
};
|
|
1761
1776
|
|
|
1762
1777
|
// Extract files using nested key support
|
|
1763
|
-
const files = activeTabConfig.key
|
|
1764
|
-
? getNestedValue(data, activeTabConfig.key) ||
|
|
1778
|
+
const files = activeTabConfig.key
|
|
1779
|
+
? getNestedValue(data, activeTabConfig.key) ||
|
|
1780
|
+
[]
|
|
1765
1781
|
: [];
|
|
1766
1782
|
|
|
1767
1783
|
const downloadFile = (file) => {
|
|
@@ -1874,7 +1890,9 @@ function GenericDetail({
|
|
|
1874
1890
|
|
|
1875
1891
|
// Create a fetchData function that refetches the main data
|
|
1876
1892
|
const handleFetchData = () => {
|
|
1877
|
-
console.log(
|
|
1893
|
+
console.log(
|
|
1894
|
+
'GenericDetail: DropZone fetchData called, refetching main data...'
|
|
1895
|
+
);
|
|
1878
1896
|
setDataReload(!dataReload);
|
|
1879
1897
|
};
|
|
1880
1898
|
|
|
@@ -1889,20 +1907,38 @@ function GenericDetail({
|
|
|
1889
1907
|
fetchData={handleFetchData}
|
|
1890
1908
|
files={files}
|
|
1891
1909
|
settings={{
|
|
1892
|
-
url: replaceDropzoneUrlParams(
|
|
1910
|
+
url: replaceDropzoneUrlParams(
|
|
1911
|
+
activeTabConfig.url
|
|
1912
|
+
),
|
|
1893
1913
|
method: 'PUT',
|
|
1894
|
-
type:
|
|
1914
|
+
type:
|
|
1915
|
+
activeTabConfig.filetype ===
|
|
1916
|
+
'image'
|
|
1917
|
+
? 'gallery'
|
|
1918
|
+
: 'list',
|
|
1895
1919
|
data: {
|
|
1896
|
-
file_relationship:
|
|
1920
|
+
file_relationship:
|
|
1921
|
+
activeTabConfig.file_relationship,
|
|
1897
1922
|
},
|
|
1898
|
-
folder:
|
|
1923
|
+
folder:
|
|
1924
|
+
activeTabConfig.folder ||
|
|
1925
|
+
'uploads',
|
|
1899
1926
|
filetype: activeTabConfig.filetype,
|
|
1900
|
-
deleteUrl:
|
|
1927
|
+
deleteUrl:
|
|
1928
|
+
replaceDropzoneUrlParams(
|
|
1929
|
+
activeTabConfig.deleteUrl
|
|
1930
|
+
) || '/ajax/files/delete',
|
|
1901
1931
|
showDescription: true,
|
|
1902
1932
|
}}
|
|
1903
|
-
url={replaceDropzoneUrlParams(
|
|
1933
|
+
url={replaceDropzoneUrlParams(
|
|
1934
|
+
activeTabConfig.url
|
|
1935
|
+
)}
|
|
1904
1936
|
urlKey={activeTabConfig.urlKey}
|
|
1905
|
-
deleteUrl={
|
|
1937
|
+
deleteUrl={
|
|
1938
|
+
replaceDropzoneUrlParams(
|
|
1939
|
+
activeTabConfig.deleteUrl
|
|
1940
|
+
) || '/ajax/files/delete'
|
|
1941
|
+
}
|
|
1906
1942
|
entityData={data}
|
|
1907
1943
|
routeParams={routeParams}
|
|
1908
1944
|
dataKey={activeTabConfig.key}
|
|
@@ -2623,19 +2659,27 @@ function GenericDetail({
|
|
|
2623
2659
|
className={
|
|
2624
2660
|
item.size ===
|
|
2625
2661
|
'full'
|
|
2626
|
-
? item.type ===
|
|
2627
|
-
|
|
2662
|
+
? item.type ===
|
|
2663
|
+
'ocrTemplate'
|
|
2664
|
+
? styles[
|
|
2665
|
+
'fw-grid-item'
|
|
2666
|
+
]
|
|
2628
2667
|
: `${styles['fw-grid-item']} ${styles.notecolor}`
|
|
2629
2668
|
: null
|
|
2630
2669
|
}
|
|
2631
2670
|
>
|
|
2632
|
-
{item.type !==
|
|
2671
|
+
{item.type !==
|
|
2672
|
+
'ocrTemplate' && (
|
|
2633
2673
|
<strong>
|
|
2634
2674
|
{item.label
|
|
2635
2675
|
? `${item.label}:`
|
|
2636
2676
|
: null}
|
|
2637
2677
|
</strong>
|
|
2638
|
-
)}
|
|
2678
|
+
)}
|
|
2679
|
+
{item.type !==
|
|
2680
|
+
'ocrTemplate'
|
|
2681
|
+
? ' '
|
|
2682
|
+
: ''}
|
|
2639
2683
|
{item
|
|
2640
2684
|
.href
|
|
2641
2685
|
?.url &&
|
|
@@ -3787,48 +3831,29 @@ function GenericDetail({
|
|
|
3787
3831
|
</div>
|
|
3788
3832
|
</div>
|
|
3789
3833
|
|
|
3790
|
-
<
|
|
3791
|
-
|
|
3834
|
+
<StandardModal
|
|
3835
|
+
isOpen={modalShow}
|
|
3792
3836
|
onClose={modalClose}
|
|
3837
|
+
title={formData.title || 'Edit Details'}
|
|
3793
3838
|
closeOnDocumentClick={false}
|
|
3794
|
-
|
|
3795
|
-
width: '80vw',
|
|
3796
|
-
maxWidth: '1200px',
|
|
3797
|
-
...(formData.verticalAlign === 'top' && {
|
|
3798
|
-
position: 'fixed',
|
|
3799
|
-
top: '5vh',
|
|
3800
|
-
left: '50%',
|
|
3801
|
-
transform: 'translateX(-50%)',
|
|
3802
|
-
margin: '0',
|
|
3803
|
-
}),
|
|
3804
|
-
}}
|
|
3839
|
+
verticalAlign={formData.verticalAlign || 'center'}
|
|
3805
3840
|
>
|
|
3806
|
-
<
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
}
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
userProfile={userProfile}
|
|
3823
|
-
paramValue={
|
|
3824
|
-
routeParams[urlParam]
|
|
3825
|
-
? routeParams[urlParam]
|
|
3826
|
-
: 0
|
|
3827
|
-
}
|
|
3828
|
-
/>
|
|
3829
|
-
</div>
|
|
3830
|
-
</div>
|
|
3831
|
-
</Popup>
|
|
3841
|
+
<Form
|
|
3842
|
+
closeModal={modalClose}
|
|
3843
|
+
columnId={formId}
|
|
3844
|
+
fetchTable={handleReload}
|
|
3845
|
+
formSettings={formData}
|
|
3846
|
+
formType={formType}
|
|
3847
|
+
style={userProfile?.settings?.style || {}}
|
|
3848
|
+
updateForm={setFormData}
|
|
3849
|
+
userProfile={userProfile}
|
|
3850
|
+
paramValue={
|
|
3851
|
+
routeParams[urlParam]
|
|
3852
|
+
? routeParams[urlParam]
|
|
3853
|
+
: 0
|
|
3854
|
+
}
|
|
3855
|
+
/>
|
|
3856
|
+
</StandardModal>
|
|
3832
3857
|
|
|
3833
3858
|
<StagePopupModal
|
|
3834
3859
|
show={showStagePopup}
|
|
@@ -7,7 +7,6 @@ import { saveAs } from 'file-saver';
|
|
|
7
7
|
import _ from 'lodash';
|
|
8
8
|
import parse from 'html-react-parser';
|
|
9
9
|
import moment from 'moment';
|
|
10
|
-
import Popup from 'reactjs-popup';
|
|
11
10
|
|
|
12
11
|
import CustomFetch from '../Fetch';
|
|
13
12
|
import Download from '../Download';
|
|
@@ -16,19 +15,24 @@ import Table from '../DataGrid';
|
|
|
16
15
|
import TableFilter from '../TableFilter';
|
|
17
16
|
import GenericGrid from './GenericGrid';
|
|
18
17
|
import GenericReportForm from './GenericReportForm';
|
|
18
|
+
import StandardModal from './StandardModal';
|
|
19
19
|
|
|
20
20
|
import styles from '../styles/GenericIndex.module.scss';
|
|
21
21
|
|
|
22
|
-
function
|
|
22
|
+
function useWindowDimensions() {
|
|
23
23
|
const [windowHeight, setWindowHeight] = useState(window.innerHeight);
|
|
24
|
+
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
|
|
24
25
|
|
|
25
26
|
useEffect(() => {
|
|
26
|
-
const handleResize = () =>
|
|
27
|
+
const handleResize = () => {
|
|
28
|
+
setWindowHeight(window.innerHeight);
|
|
29
|
+
setWindowWidth(window.innerWidth);
|
|
30
|
+
};
|
|
27
31
|
window.addEventListener('resize', handleResize);
|
|
28
32
|
return () => window.removeEventListener('resize', handleResize);
|
|
29
33
|
}, []);
|
|
30
34
|
|
|
31
|
-
return windowHeight;
|
|
35
|
+
return { windowHeight, windowWidth };
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
function useConfig(setting, tabs, filters, setRowsSelected, tableSetting) {
|
|
@@ -236,7 +240,7 @@ function GenericIndex({
|
|
|
236
240
|
const [customActionModalShow, setCustomActionModalShow] = useState(false);
|
|
237
241
|
const [customActionFormData, setCustomActionFormData] = useState({});
|
|
238
242
|
const [customActionConfig, setCustomActionConfig] = useState(null);
|
|
239
|
-
const windowHeight =
|
|
243
|
+
const { windowHeight, windowWidth } = useWindowDimensions();
|
|
240
244
|
const { config, setConfig, subnav, setSubnav } = useConfig(
|
|
241
245
|
setting,
|
|
242
246
|
setting.tabs,
|
|
@@ -1002,41 +1006,22 @@ function GenericIndex({
|
|
|
1002
1006
|
</div>
|
|
1003
1007
|
|
|
1004
1008
|
{/* Custom Action Modal */}
|
|
1005
|
-
<
|
|
1006
|
-
|
|
1009
|
+
<StandardModal
|
|
1010
|
+
isOpen={customActionModalShow}
|
|
1007
1011
|
onClose={closeCustomActionModal}
|
|
1012
|
+
title={customActionFormData.title || 'Custom Action'}
|
|
1008
1013
|
closeOnDocumentClick={false}
|
|
1009
|
-
|
|
1010
|
-
width: '80vw',
|
|
1011
|
-
maxWidth: '1200px',
|
|
1012
|
-
...(customActionFormData.verticalAlign === 'top' && {
|
|
1013
|
-
position: 'fixed',
|
|
1014
|
-
top: '5vh',
|
|
1015
|
-
left: '50%',
|
|
1016
|
-
transform: 'translateX(-50%)',
|
|
1017
|
-
margin: '0',
|
|
1018
|
-
}),
|
|
1019
|
-
}}
|
|
1014
|
+
verticalAlign={customActionFormData.verticalAlign || 'center'}
|
|
1020
1015
|
>
|
|
1021
|
-
<
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
closeModal={closeCustomActionModal}
|
|
1031
|
-
formSettings={customActionFormData}
|
|
1032
|
-
formType="create"
|
|
1033
|
-
fetchTable={handleCustomActionSubmit}
|
|
1034
|
-
style={userProfile?.settings?.style || {}}
|
|
1035
|
-
userProfile={userProfile}
|
|
1036
|
-
/>
|
|
1037
|
-
</div>
|
|
1038
|
-
</div>
|
|
1039
|
-
</Popup>
|
|
1016
|
+
<Form
|
|
1017
|
+
closeModal={closeCustomActionModal}
|
|
1018
|
+
formSettings={customActionFormData}
|
|
1019
|
+
formType="create"
|
|
1020
|
+
fetchTable={handleCustomActionSubmit}
|
|
1021
|
+
style={userProfile?.settings?.style || {}}
|
|
1022
|
+
userProfile={userProfile}
|
|
1023
|
+
/>
|
|
1024
|
+
</StandardModal>
|
|
1040
1025
|
</>
|
|
1041
1026
|
);
|
|
1042
1027
|
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import React, { useState, useCallback } from 'react';
|
|
2
|
-
import { useNavigate } from 'react-router-dom';
|
|
3
2
|
import Table from '../DataGrid';
|
|
4
|
-
import { Bell, Check
|
|
3
|
+
import { Bell, Check } from 'lucide-react';
|
|
5
4
|
import { toast } from 'react-toastify';
|
|
6
5
|
import CustomFetch from '../Fetch';
|
|
7
6
|
import styles from '../styles/NotificationList.module.scss';
|
|
8
7
|
|
|
9
8
|
const NotificationList = () => {
|
|
10
|
-
const navigate = useNavigate();
|
|
11
9
|
const [loading, setLoading] = useState(false);
|
|
12
10
|
|
|
13
11
|
const markAllAsRead = useCallback(() => {
|
|
@@ -50,25 +48,26 @@ const NotificationList = () => {
|
|
|
50
48
|
},
|
|
51
49
|
columns: [
|
|
52
50
|
{
|
|
53
|
-
id: 'data',
|
|
54
|
-
|
|
51
|
+
id: ['data'],
|
|
52
|
+
nameFrom: 'label',
|
|
55
53
|
label: 'Message',
|
|
56
|
-
type: '
|
|
57
|
-
|
|
54
|
+
type: 'relation',
|
|
55
|
+
minWidth: 300,
|
|
56
|
+
wordWrap: true,
|
|
58
57
|
},
|
|
59
58
|
{
|
|
60
59
|
id: 'created_at',
|
|
61
60
|
label: 'Created At',
|
|
62
61
|
type: 'datetime',
|
|
63
62
|
minWidth: 180,
|
|
64
|
-
maxWidth:
|
|
63
|
+
maxWidth: 200,
|
|
65
64
|
},
|
|
66
65
|
{
|
|
67
66
|
id: 'read_at',
|
|
68
67
|
label: 'Read At',
|
|
69
68
|
type: 'datetime',
|
|
70
69
|
minWidth: 180,
|
|
71
|
-
maxWidth:
|
|
70
|
+
maxWidth: 200,
|
|
72
71
|
},
|
|
73
72
|
],
|
|
74
73
|
settings: [],
|
|
@@ -111,21 +110,6 @@ const NotificationList = () => {
|
|
|
111
110
|
</div>
|
|
112
111
|
</div>
|
|
113
112
|
</div>
|
|
114
|
-
<div className={styles.grid}>
|
|
115
|
-
<div className={styles.grid__row}>
|
|
116
|
-
<div className={styles.grid__full}>
|
|
117
|
-
<div className={styles.backButtonContainer}>
|
|
118
|
-
<button
|
|
119
|
-
className={styles.backButton}
|
|
120
|
-
onClick={() => navigate('/')}
|
|
121
|
-
>
|
|
122
|
-
<ArrowLeft size={16} />
|
|
123
|
-
<span>Back to Dashboard</span>
|
|
124
|
-
</button>
|
|
125
|
-
</div>
|
|
126
|
-
</div>
|
|
127
|
-
</div>
|
|
128
|
-
</div>
|
|
129
113
|
</div>
|
|
130
114
|
);
|
|
131
115
|
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Popup from 'reactjs-popup';
|
|
3
|
+
import { X } from 'lucide-react';
|
|
4
|
+
import formStyles from '../styles/Form.module.scss';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* StandardModal Component
|
|
8
|
+
*
|
|
9
|
+
* A reusable modal component that standardizes the look and feel across all components.
|
|
10
|
+
* Uses the same styles as GenericIndex, GenericDetail, and DataGrid via Form.module.scss.
|
|
11
|
+
*
|
|
12
|
+
* @param {Object} props
|
|
13
|
+
* @param {boolean} props.isOpen - Whether the modal is open
|
|
14
|
+
* @param {Function} props.onClose - Function to call when modal should close
|
|
15
|
+
* @param {string} props.title - Modal title (only used if showHeader is true)
|
|
16
|
+
* @param {React.ReactNode} props.children - Modal content
|
|
17
|
+
* @param {string} props.size - Modal size: 'small', 'medium', 'large' (default: 'medium')
|
|
18
|
+
* @param {boolean} props.closeOnDocumentClick - Whether to close on outside click (default: true)
|
|
19
|
+
* @param {boolean} props.closeOnEscape - Whether to close on escape key (default: true)
|
|
20
|
+
* @param {string} props.verticalAlign - Vertical alignment: 'center', 'top' (default: 'center')
|
|
21
|
+
* @param {boolean} props.showHeader - Whether to show modal header (default: false, because Form components have their own headers)
|
|
22
|
+
* @param {Object} props.customStyles - Custom styles to override defaults
|
|
23
|
+
*/
|
|
24
|
+
const StandardModal = ({
|
|
25
|
+
isOpen,
|
|
26
|
+
onClose,
|
|
27
|
+
title,
|
|
28
|
+
children,
|
|
29
|
+
size = 'medium',
|
|
30
|
+
closeOnDocumentClick = true,
|
|
31
|
+
closeOnEscape = true,
|
|
32
|
+
verticalAlign = 'center',
|
|
33
|
+
showHeader = false,
|
|
34
|
+
customStyles = {}
|
|
35
|
+
}) => {
|
|
36
|
+
// Apply vertical alignment class if needed
|
|
37
|
+
const getModalClasses = () => {
|
|
38
|
+
let classes = formStyles.modal;
|
|
39
|
+
if (verticalAlign === 'top') {
|
|
40
|
+
classes += ' modal-top-aligned';
|
|
41
|
+
}
|
|
42
|
+
return classes;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Get size-based styles for the popup content
|
|
46
|
+
const getContentStyle = () => {
|
|
47
|
+
const baseStyle = {
|
|
48
|
+
...customStyles.modal
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Apply size-based styles
|
|
52
|
+
switch (size) {
|
|
53
|
+
case 'small':
|
|
54
|
+
return {
|
|
55
|
+
...baseStyle,
|
|
56
|
+
width: '400px',
|
|
57
|
+
minWidth: '400px',
|
|
58
|
+
maxWidth: '600px'
|
|
59
|
+
};
|
|
60
|
+
case 'large':
|
|
61
|
+
return {
|
|
62
|
+
...baseStyle,
|
|
63
|
+
width: '95vw',
|
|
64
|
+
minWidth: '800px',
|
|
65
|
+
maxWidth: '1600px'
|
|
66
|
+
};
|
|
67
|
+
case 'medium':
|
|
68
|
+
default:
|
|
69
|
+
// Use the same dimensions as modalWide class (original modal sizing)
|
|
70
|
+
return {
|
|
71
|
+
...baseStyle,
|
|
72
|
+
width: '90vw',
|
|
73
|
+
minWidth: '600px',
|
|
74
|
+
maxWidth: '1400px'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Popup
|
|
81
|
+
open={isOpen}
|
|
82
|
+
onClose={onClose}
|
|
83
|
+
modal
|
|
84
|
+
closeOnDocumentClick={closeOnDocumentClick}
|
|
85
|
+
closeOnEscape={closeOnEscape}
|
|
86
|
+
contentStyle={getContentStyle()}
|
|
87
|
+
>
|
|
88
|
+
<div className={formStyles.modalwrap}>
|
|
89
|
+
<div
|
|
90
|
+
className={getModalClasses()}
|
|
91
|
+
>
|
|
92
|
+
{showHeader && (
|
|
93
|
+
<div className={formStyles.modal__header} style={customStyles.header}>
|
|
94
|
+
<h1 style={customStyles.title}>{title}</h1>
|
|
95
|
+
<button
|
|
96
|
+
className={formStyles.modal__close}
|
|
97
|
+
onClick={onClose}
|
|
98
|
+
style={customStyles.closeButton}
|
|
99
|
+
type="button"
|
|
100
|
+
aria-label="Close modal"
|
|
101
|
+
>
|
|
102
|
+
<X strokeWidth={1} size={24} />
|
|
103
|
+
</button>
|
|
104
|
+
</div>
|
|
105
|
+
)}
|
|
106
|
+
{showHeader ? (
|
|
107
|
+
<div className={formStyles.modal__content} style={customStyles.content}>
|
|
108
|
+
{children}
|
|
109
|
+
</div>
|
|
110
|
+
) : (
|
|
111
|
+
children
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</Popup>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export default StandardModal;
|
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
grid-template-columns: 1fr 1fr 1fr 1fr;
|
|
12
12
|
gap: 1em;
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
/* Enhanced styling for wider modals */
|
|
16
|
+
.modal & form {
|
|
17
|
+
gap: 1.5em;
|
|
18
|
+
max-width: none;
|
|
19
|
+
}
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
.formItem {
|
|
@@ -238,9 +244,10 @@ input[type='file'] {
|
|
|
238
244
|
.modal__content {
|
|
239
245
|
width: 100%;
|
|
240
246
|
position: relative;
|
|
241
|
-
padding:
|
|
242
|
-
max-height:
|
|
247
|
+
padding: 1rem;
|
|
248
|
+
max-height: 85vh;
|
|
243
249
|
overflow-y: auto;
|
|
250
|
+
min-height: 400px;
|
|
244
251
|
|
|
245
252
|
.btn {
|
|
246
253
|
background: var(--primary-color);
|
|
@@ -945,3 +945,27 @@
|
|
|
945
945
|
}
|
|
946
946
|
}
|
|
947
947
|
|
|
948
|
+
// Modal styles (consistent with DataGrid)
|
|
949
|
+
.modalwrap {
|
|
950
|
+
background: var(--tertiary-color);
|
|
951
|
+
border-radius: 8px;
|
|
952
|
+
overflow: hidden;
|
|
953
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
.top--modal {
|
|
957
|
+
position: relative;
|
|
958
|
+
z-index: 1000;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
.modalWide {
|
|
962
|
+
width: 90vw;
|
|
963
|
+
max-width: 1400px;
|
|
964
|
+
min-width: 600px;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
.modal {
|
|
968
|
+
width: 100%;
|
|
969
|
+
position: relative;
|
|
970
|
+
}
|
|
971
|
+
|
|
@@ -1090,3 +1090,27 @@
|
|
|
1090
1090
|
filter: brightness(1.1);
|
|
1091
1091
|
}
|
|
1092
1092
|
}
|
|
1093
|
+
|
|
1094
|
+
// Modal styles (consistent with DataGrid)
|
|
1095
|
+
.modalwrap {
|
|
1096
|
+
background: var(--tertiary-color);
|
|
1097
|
+
border-radius: 8px;
|
|
1098
|
+
overflow: hidden;
|
|
1099
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
.top--modal {
|
|
1103
|
+
position: relative;
|
|
1104
|
+
z-index: 1000;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
.modalWide {
|
|
1108
|
+
width: 90vw;
|
|
1109
|
+
max-width: 1400px;
|
|
1110
|
+
min-width: 600px;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
.modal {
|
|
1114
|
+
width: 100%;
|
|
1115
|
+
position: relative;
|
|
1116
|
+
}
|
|
@@ -84,20 +84,22 @@
|
|
|
84
84
|
|
|
85
85
|
/* Notification Box Header */
|
|
86
86
|
.notiboxHeader {
|
|
87
|
-
display: flex;
|
|
88
|
-
align-items: center;
|
|
89
|
-
justify-content: space-between;
|
|
90
|
-
padding: 12px 16px;
|
|
91
|
-
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
|
92
|
-
gap:
|
|
87
|
+
display: flex !important;
|
|
88
|
+
align-items: center !important;
|
|
89
|
+
justify-content: space-between !important;
|
|
90
|
+
padding: 12px 16px !important;
|
|
91
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.06) !important;
|
|
92
|
+
gap: 12px !important;
|
|
93
|
+
min-height: 48px !important;
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
.notiboxHeader h3 {
|
|
96
|
-
margin: 0;
|
|
97
|
-
font-size: 16px;
|
|
98
|
-
font-weight: 600;
|
|
99
|
-
color:
|
|
100
|
-
flex-shrink: 0;
|
|
97
|
+
margin: 0 !important;
|
|
98
|
+
font-size: 16px !important;
|
|
99
|
+
font-weight: 600 !important;
|
|
100
|
+
color: #333 !important;
|
|
101
|
+
flex-shrink: 0 !important;
|
|
102
|
+
flex-grow: 1 !important;
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
/* Notification Scroll Wrap */
|
|
@@ -231,42 +233,41 @@
|
|
|
231
233
|
}
|
|
232
234
|
|
|
233
235
|
.notiDismiss {
|
|
234
|
-
position: absolute;
|
|
235
|
-
right: 12px;
|
|
236
|
-
top: 10px;
|
|
237
|
-
background:
|
|
238
|
-
border:
|
|
239
|
-
color: #
|
|
240
|
-
cursor: pointer;
|
|
241
|
-
padding:
|
|
242
|
-
display: flex;
|
|
243
|
-
align-items: center;
|
|
244
|
-
justify-content: center;
|
|
245
|
-
width: 24px;
|
|
246
|
-
height: 24px;
|
|
247
|
-
border-radius: 50
|
|
248
|
-
transition: all 0.2s;
|
|
249
|
-
opacity:
|
|
236
|
+
position: absolute !important;
|
|
237
|
+
right: 12px !important;
|
|
238
|
+
top: 10px !important;
|
|
239
|
+
background: #f8f9fa !important;
|
|
240
|
+
border: 1px solid #dee2e6 !important;
|
|
241
|
+
color: #6c757d !important;
|
|
242
|
+
cursor: pointer !important;
|
|
243
|
+
padding: 4px !important;
|
|
244
|
+
display: flex !important;
|
|
245
|
+
align-items: center !important;
|
|
246
|
+
justify-content: center !important;
|
|
247
|
+
width: 24px !important;
|
|
248
|
+
height: 24px !important;
|
|
249
|
+
border-radius: 50% !important;
|
|
250
|
+
transition: all 0.2s !important;
|
|
251
|
+
opacity: 1 !important;
|
|
252
|
+
visibility: visible !important;
|
|
253
|
+
z-index: 10 !important;
|
|
250
254
|
}
|
|
251
255
|
|
|
252
256
|
.notiContent:hover .notiDismiss {
|
|
253
|
-
|
|
257
|
+
background: #e9ecef !important;
|
|
258
|
+
color: #495057 !important;
|
|
259
|
+
border-color: #adb5bd !important;
|
|
254
260
|
}
|
|
255
261
|
|
|
256
262
|
.notiDismiss:hover {
|
|
257
|
-
background-color:
|
|
258
|
-
color: #
|
|
259
|
-
|
|
263
|
+
background-color: #dc3545 !important;
|
|
264
|
+
color: #ffffff !important;
|
|
265
|
+
border-color: #dc3545 !important;
|
|
260
266
|
}
|
|
261
267
|
|
|
262
268
|
.notiDismiss:focus {
|
|
263
|
-
outline: none;
|
|
264
|
-
box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.3);
|
|
265
|
-
background-color: rgba(220, 53, 69, 0.05);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
.notiDismiss:active {
|
|
269
|
-
transform: scale(0.95);
|
|
269
|
+
outline: none !important;
|
|
270
|
+
box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.3) !important;
|
|
270
271
|
}
|
|
271
272
|
|
|
272
273
|
/* Empty State */
|
|
@@ -322,46 +323,45 @@
|
|
|
322
323
|
|
|
323
324
|
/* Mark All as Read Button */
|
|
324
325
|
.markAllBtn {
|
|
325
|
-
background-color:
|
|
326
|
-
color:
|
|
327
|
-
padding:
|
|
328
|
-
border-radius:
|
|
329
|
-
border: none;
|
|
330
|
-
display: flex;
|
|
331
|
-
align-items: center;
|
|
332
|
-
justify-content: center;
|
|
333
|
-
gap:
|
|
334
|
-
font-size:
|
|
335
|
-
font-weight: 500;
|
|
336
|
-
cursor: pointer;
|
|
337
|
-
box-shadow: 0
|
|
338
|
-
transition: all 0.2s ease;
|
|
339
|
-
outline: none;
|
|
340
|
-
white-space: nowrap;
|
|
341
|
-
flex-shrink: 0;
|
|
342
|
-
|
|
343
|
-
|
|
326
|
+
background-color: #6c757d !important;
|
|
327
|
+
color: #ffffff !important;
|
|
328
|
+
padding: 6px 10px !important;
|
|
329
|
+
border-radius: 4px !important;
|
|
330
|
+
border: none !important;
|
|
331
|
+
display: inline-flex !important;
|
|
332
|
+
align-items: center !important;
|
|
333
|
+
justify-content: center !important;
|
|
334
|
+
gap: 4px !important;
|
|
335
|
+
font-size: 12px !important;
|
|
336
|
+
font-weight: 500 !important;
|
|
337
|
+
cursor: pointer !important;
|
|
338
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2) !important;
|
|
339
|
+
transition: all 0.2s ease !important;
|
|
340
|
+
outline: none !important;
|
|
341
|
+
white-space: nowrap !important;
|
|
342
|
+
flex-shrink: 0 !important;
|
|
343
|
+
min-width: 120px !important;
|
|
344
|
+
height: 32px !important;
|
|
345
|
+
line-height: 1.2 !important;
|
|
346
|
+
opacity: 1 !important;
|
|
347
|
+
visibility: visible !important;
|
|
348
|
+
position: relative !important;
|
|
349
|
+
z-index: 10 !important;
|
|
344
350
|
}
|
|
345
351
|
|
|
346
352
|
.markAllBtn:hover:not(:disabled) {
|
|
347
|
-
background-color:
|
|
348
|
-
box-shadow: 0
|
|
349
|
-
transform: translateY(-1px);
|
|
353
|
+
background-color: #5a6268 !important;
|
|
354
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25) !important;
|
|
350
355
|
}
|
|
351
356
|
|
|
352
357
|
.markAllBtn:focus:not(:disabled) {
|
|
353
|
-
box-shadow: 0 0 0
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
.markAllBtn:active:not(:disabled) {
|
|
357
|
-
transform: translateY(0);
|
|
358
|
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
358
|
+
box-shadow: 0 0 0 2px rgba(108, 117, 125, 0.4) !important;
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
.markAllBtn:disabled {
|
|
362
|
-
opacity: 0.6;
|
|
363
|
-
cursor: not-allowed;
|
|
364
|
-
background-color: #
|
|
362
|
+
opacity: 0.6 !important;
|
|
363
|
+
cursor: not-allowed !important;
|
|
364
|
+
background-color: #adb5bd !important;
|
|
365
365
|
}
|
|
366
366
|
|
|
367
367
|
/* Footer Actions */
|
|
@@ -374,41 +374,37 @@
|
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
.viewAllBtn {
|
|
377
|
-
background-color:
|
|
378
|
-
color:
|
|
379
|
-
font-size: 14px;
|
|
380
|
-
font-weight: 500;
|
|
381
|
-
cursor: pointer;
|
|
382
|
-
padding:
|
|
383
|
-
border-radius:
|
|
384
|
-
border: none;
|
|
385
|
-
width:
|
|
386
|
-
|
|
387
|
-
text-align: center;
|
|
388
|
-
transition: all 0.2s ease;
|
|
389
|
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.
|
|
390
|
-
outline: none;
|
|
391
|
-
position: relative;
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
display: block;
|
|
377
|
+
background-color: #6c757d !important;
|
|
378
|
+
color: #ffffff !important;
|
|
379
|
+
font-size: 14px !important;
|
|
380
|
+
font-weight: 500 !important;
|
|
381
|
+
cursor: pointer !important;
|
|
382
|
+
padding: 12px 24px !important;
|
|
383
|
+
border-radius: 6px !important;
|
|
384
|
+
border: none !important;
|
|
385
|
+
width: 100% !important;
|
|
386
|
+
max-width: 280px !important;
|
|
387
|
+
text-align: center !important;
|
|
388
|
+
transition: all 0.2s ease !important;
|
|
389
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) !important;
|
|
390
|
+
outline: none !important;
|
|
391
|
+
position: relative !important;
|
|
392
|
+
height: 44px !important;
|
|
393
|
+
opacity: 1 !important;
|
|
394
|
+
visibility: visible !important;
|
|
395
|
+
display: block !important;
|
|
396
|
+
line-height: 1.2 !important;
|
|
397
|
+
z-index: 10 !important;
|
|
396
398
|
}
|
|
397
399
|
|
|
398
400
|
.viewAllBtn:hover:not(:disabled) {
|
|
399
|
-
background-color:
|
|
400
|
-
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.
|
|
401
|
-
transform: translateY(-1px);
|
|
401
|
+
background-color: #5a6268 !important;
|
|
402
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25) !important;
|
|
402
403
|
}
|
|
403
404
|
|
|
404
405
|
.viewAllBtn:focus:not(:disabled) {
|
|
405
|
-
box-shadow: 0 0 0
|
|
406
|
-
outline: none;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
.viewAllBtn:active:not(:disabled) {
|
|
410
|
-
transform: translateY(0);
|
|
411
|
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
406
|
+
box-shadow: 0 0 0 2px rgba(108, 117, 125, 0.4) !important;
|
|
407
|
+
outline: none !important;
|
|
412
408
|
}
|
|
413
409
|
|
|
414
410
|
/* Bell Animation */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
.notificationListContainer {
|
|
2
2
|
width: 100%;
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
margin: 0;
|
|
4
|
+
padding: 0;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
.grid {
|
|
@@ -88,31 +88,3 @@
|
|
|
88
88
|
opacity: 0.6;
|
|
89
89
|
cursor: not-allowed;
|
|
90
90
|
}
|
|
91
|
-
|
|
92
|
-
.backButtonContainer {
|
|
93
|
-
display: flex;
|
|
94
|
-
justify-content: center;
|
|
95
|
-
padding: 16px 0;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
.backButton {
|
|
99
|
-
display: flex;
|
|
100
|
-
align-items: center;
|
|
101
|
-
gap: 8px;
|
|
102
|
-
background-color: var(--primary-color);
|
|
103
|
-
color: white;
|
|
104
|
-
border: none;
|
|
105
|
-
border-radius: 6px;
|
|
106
|
-
padding: 12px 24px;
|
|
107
|
-
font-size: 14px;
|
|
108
|
-
font-weight: 500;
|
|
109
|
-
cursor: pointer;
|
|
110
|
-
transition: all 0.2s ease;
|
|
111
|
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
112
|
-
min-width: 200px;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
.backButton:hover {
|
|
116
|
-
background-color: var(--primary-color-darker, #0056b3);
|
|
117
|
-
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
|
118
|
-
}
|
|
@@ -65,9 +65,28 @@
|
|
|
65
65
|
box-shadow: 0 0 0 2px rgba(107, 114, 128, 0.3) !important;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
/* Make the modal
|
|
68
|
+
/* Make the modal use full width like GenericIndex */
|
|
69
69
|
:global(.swal2-popup.swal2-modal) {
|
|
70
|
-
max-width:
|
|
70
|
+
max-width: 95%;
|
|
71
|
+
width: 95%;
|
|
72
|
+
min-width: 800px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Responsive adjustments for smaller screens */
|
|
76
|
+
@media (max-width: 1024px) {
|
|
77
|
+
:global(.swal2-popup.swal2-modal) {
|
|
78
|
+
max-width: 90%;
|
|
79
|
+
width: 90%;
|
|
80
|
+
min-width: 600px;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@media (max-width: 768px) {
|
|
85
|
+
:global(.swal2-popup.swal2-modal) {
|
|
86
|
+
max-width: 95%;
|
|
87
|
+
width: 95%;
|
|
88
|
+
min-width: 320px;
|
|
89
|
+
}
|
|
71
90
|
}
|
|
72
91
|
|
|
73
92
|
/* Reduce spacing between elements */
|
|
@@ -225,11 +225,6 @@ input[type]:not([type='search']):not([type='url']):not([type='hidden']):not(
|
|
|
225
225
|
backdrop-filter: blur(3px);
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
-
.modalWide {
|
|
229
|
-
width: 80vw !important;
|
|
230
|
-
max-width: 1200px !important;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
228
|
/* Top-aligned modal styles */
|
|
234
229
|
.modal-top-aligned {
|
|
235
230
|
max-height: 90vh !important;
|
package/src/index.js
CHANGED
|
@@ -72,6 +72,7 @@ import GenericSort from './components/generic/GenericSort';
|
|
|
72
72
|
import NotificationList from './components/generic/NotificationList';
|
|
73
73
|
import OcrTemplateEnhanced from './components/generic/OcrTemplateEnhanced';
|
|
74
74
|
import StagePopupModal from './components/generic/StagePopupModal';
|
|
75
|
+
import StandardModal from './components/generic/StandardModal';
|
|
75
76
|
|
|
76
77
|
/** Proposal Components */
|
|
77
78
|
import ProposalTemplateSectionManager from './components/proposal/ProposalTemplateSectionManager';
|
|
@@ -145,6 +146,7 @@ export {
|
|
|
145
146
|
showConfirmDialog,
|
|
146
147
|
SortableList,
|
|
147
148
|
StagePopupModal,
|
|
149
|
+
StandardModal,
|
|
148
150
|
Table,
|
|
149
151
|
TableFilter,
|
|
150
152
|
VariableInserter,
|