@visns-studio/visns-components 5.14.8 → 5.14.10

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.
@@ -0,0 +1,307 @@
1
+ /* Grouped Report Base Styles */
2
+ .grouped-report {
3
+ --group-header-bg-primary: #2c5282;
4
+ --group-header-bg-secondary: #4a5568;
5
+ --group-header-bg-success: #38a169;
6
+ --group-header-bg-warning: #d69e2e;
7
+ --group-header-bg-danger: #e53e3e;
8
+ --group-header-color: white;
9
+ --empty-row-bg-light: #f8f9fa;
10
+ --empty-row-bg-transparent: transparent;
11
+ --border-color: #dee2e6;
12
+ --border-color-light: #e2e8f0;
13
+ --text-muted: #6c757d;
14
+ }
15
+
16
+ .grouped-report * {
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ /* Report Header */
21
+ .grouped-report-header {
22
+ display: flex;
23
+ justify-content: space-between;
24
+ align-items: center;
25
+ margin-bottom: 1.5rem;
26
+ padding: 1rem;
27
+ background-color: #f8f9fa;
28
+ border-radius: 0.375rem;
29
+ border: 1px solid var(--border-color);
30
+ }
31
+
32
+ .grouped-report-summary {
33
+ display: flex;
34
+ align-items: center;
35
+ gap: 1rem;
36
+ }
37
+
38
+ .summary-text {
39
+ font-weight: 500;
40
+ color: var(--text-muted);
41
+ }
42
+
43
+ .grouped-report-actions {
44
+ display: flex;
45
+ gap: 0.5rem;
46
+ }
47
+
48
+ .btn-export {
49
+ background-color: #0d6efd;
50
+ color: white;
51
+ border: none;
52
+ padding: 0.5rem 1rem;
53
+ border-radius: 0.375rem;
54
+ cursor: pointer;
55
+ font-weight: 500;
56
+ transition: background-color 0.15s ease-in-out;
57
+ }
58
+
59
+ .btn-export:hover {
60
+ background-color: #0b5ed7;
61
+ }
62
+
63
+ /* Section-based Grouping */
64
+ .section-grouped-report {
65
+ width: 100%;
66
+ }
67
+
68
+ .grouped-report--sections .group-section {
69
+ margin-bottom: 2rem;
70
+ page-break-inside: avoid;
71
+ border: 1px solid var(--border-color);
72
+ border-radius: 0.5rem;
73
+ overflow: hidden;
74
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
75
+ }
76
+
77
+ /* Group Headers */
78
+ .group-header {
79
+ padding: 0.75rem 1rem;
80
+ font-weight: 600;
81
+ display: flex;
82
+ justify-content: space-between;
83
+ align-items: center;
84
+ color: var(--group-header-color);
85
+ }
86
+
87
+ .group-header-content {
88
+ display: flex;
89
+ justify-content: space-between;
90
+ align-items: center;
91
+ width: 100%;
92
+ }
93
+
94
+ .group-title {
95
+ font-size: 1rem;
96
+ margin: 0;
97
+ }
98
+
99
+ .group-stats {
100
+ font-size: 0.875rem;
101
+ font-weight: 400;
102
+ opacity: 0.9;
103
+ }
104
+
105
+ /* Group Header Styles */
106
+ .group-section--primary .group-header {
107
+ background-color: var(--group-header-bg-primary);
108
+ }
109
+
110
+ .group-section--secondary .group-header {
111
+ background-color: var(--group-header-bg-secondary);
112
+ }
113
+
114
+ .group-section--success .group-header {
115
+ background-color: var(--group-header-bg-success);
116
+ }
117
+
118
+ .group-section--warning .group-header {
119
+ background-color: var(--group-header-bg-warning);
120
+ }
121
+
122
+ .group-section--danger .group-header {
123
+ background-color: var(--group-header-bg-danger);
124
+ }
125
+
126
+ /* Group Content */
127
+ .group-content {
128
+ background-color: white;
129
+ }
130
+
131
+ .group-table,
132
+ .grouped-table {
133
+ width: 100%;
134
+ border-collapse: collapse;
135
+ margin: 0;
136
+ }
137
+
138
+ .group-table thead th,
139
+ .grouped-table thead th {
140
+ background-color: #f8f9fa;
141
+ color: #495057;
142
+ font-weight: 600;
143
+ padding: 0.75rem;
144
+ text-align: left;
145
+ border-bottom: 2px solid var(--border-color);
146
+ border-right: 1px solid var(--border-color-light);
147
+ font-size: 0.875rem;
148
+ text-transform: uppercase;
149
+ letter-spacing: 0.05em;
150
+ }
151
+
152
+ .group-table thead th:last-child,
153
+ .grouped-table thead th:last-child {
154
+ border-right: none;
155
+ }
156
+
157
+ .group-table thead th.required-column,
158
+ .grouped-table thead th.required-column {
159
+ background-color: #e3f2fd;
160
+ font-weight: 700;
161
+ }
162
+
163
+ .group-table tbody td,
164
+ .grouped-table tbody td {
165
+ padding: 0.75rem;
166
+ border-bottom: 1px solid var(--border-color-light);
167
+ border-right: 1px solid var(--border-color-light);
168
+ vertical-align: top;
169
+ font-size: 0.875rem;
170
+ }
171
+
172
+ .group-table tbody td:last-child,
173
+ .grouped-table tbody td:last-child {
174
+ border-right: none;
175
+ }
176
+
177
+ .group-table tbody td.required-column,
178
+ .grouped-table tbody td.required-column {
179
+ font-weight: 500;
180
+ }
181
+
182
+ /* Data Rows */
183
+ .data-row {
184
+ background-color: white;
185
+ transition: background-color 0.15s ease-in-out;
186
+ }
187
+
188
+ .data-row:hover {
189
+ background-color: #f8f9fa;
190
+ }
191
+
192
+ .data-row:nth-child(even) {
193
+ background-color: #fbfcfd;
194
+ }
195
+
196
+ .data-row:nth-child(even):hover {
197
+ background-color: #f1f3f4;
198
+ }
199
+
200
+ /* Empty Rows */
201
+ .empty-row td {
202
+ height: 2.5rem;
203
+ min-height: 2.5rem;
204
+ border-bottom: 1px solid var(--border-color-light);
205
+ }
206
+
207
+ .empty-row--light {
208
+ background-color: var(--empty-row-bg-light);
209
+ }
210
+
211
+ .empty-row--transparent {
212
+ background-color: var(--empty-row-bg-transparent);
213
+ }
214
+
215
+ .empty-row--striped:nth-child(even) {
216
+ background-color: #f8f9fa;
217
+ }
218
+
219
+ /* Empty State */
220
+ .grouped-report-empty {
221
+ text-align: center;
222
+ padding: 3rem;
223
+ color: var(--text-muted);
224
+ background-color: #f8f9fa;
225
+ border: 1px solid var(--border-color);
226
+ border-radius: 0.5rem;
227
+ }
228
+
229
+ .grouped-report-error {
230
+ text-align: center;
231
+ padding: 2rem;
232
+ color: #dc3545;
233
+ background-color: #f8d7da;
234
+ border: 1px solid #f5c6cb;
235
+ border-radius: 0.5rem;
236
+ }
237
+
238
+ /* Responsive Design */
239
+ @media (max-width: 768px) {
240
+ .grouped-report-header {
241
+ flex-direction: column;
242
+ gap: 1rem;
243
+ align-items: stretch;
244
+ }
245
+
246
+ .group-table,
247
+ .grouped-table {
248
+ font-size: 0.75rem;
249
+ }
250
+
251
+ .group-table thead th,
252
+ .group-table tbody td,
253
+ .grouped-table thead th,
254
+ .grouped-table tbody td {
255
+ padding: 0.5rem;
256
+ }
257
+
258
+ .group-header {
259
+ padding: 0.5rem;
260
+ }
261
+
262
+ .group-title {
263
+ font-size: 0.875rem;
264
+ }
265
+
266
+ .group-stats {
267
+ font-size: 0.75rem;
268
+ }
269
+ }
270
+
271
+ /* Print Styles */
272
+ @media print {
273
+ .grouped-report-actions {
274
+ display: none;
275
+ }
276
+
277
+ .group-section {
278
+ page-break-inside: avoid;
279
+ margin-bottom: 1rem;
280
+ border: 1px solid #000;
281
+ }
282
+
283
+ .group-header {
284
+ background-color: #000 !important;
285
+ color: white !important;
286
+ -webkit-print-color-adjust: exact;
287
+ print-color-adjust: exact;
288
+ }
289
+
290
+ .group-table,
291
+ .grouped-table {
292
+ border-collapse: collapse;
293
+ }
294
+
295
+ .group-table thead th,
296
+ .group-table tbody td,
297
+ .grouped-table thead th,
298
+ .grouped-table tbody td {
299
+ border: 1px solid #000;
300
+ }
301
+
302
+ .empty-row--light {
303
+ background-color: #f0f0f0 !important;
304
+ -webkit-print-color-adjust: exact;
305
+ print-color-adjust: exact;
306
+ }
307
+ }
@@ -0,0 +1,118 @@
1
+ export const getNestedValue = (obj, path) => {
2
+ if (typeof path === 'string') {
3
+ path = path.split('.');
4
+ }
5
+ return path.reduce((current, key) => current?.[key], obj) || '';
6
+ };
7
+
8
+ export const formatCellValue = (value, column) => {
9
+ if (value === null || value === undefined || value === '') {
10
+ return '';
11
+ }
12
+
13
+ switch (column.type) {
14
+ case 'date':
15
+ return new Date(value).toLocaleDateString();
16
+ case 'datetime':
17
+ return new Date(value).toLocaleString();
18
+ case 'decimal':
19
+ return parseFloat(value).toFixed(2);
20
+ case 'integer':
21
+ return parseInt(value).toLocaleString();
22
+ case 'currency':
23
+ return new Intl.NumberFormat('en-AU', {
24
+ style: 'currency',
25
+ currency: 'AUD'
26
+ }).format(value);
27
+ default:
28
+ return value.toString();
29
+ }
30
+ };
31
+
32
+ export const sortGroups = (groups, sortDirection = 'asc', customOrder = null) => {
33
+ if (customOrder) {
34
+ return groups.sort((a, b) => {
35
+ const aIndex = customOrder.indexOf(a.groupName);
36
+ const bIndex = customOrder.indexOf(b.groupName);
37
+
38
+ // If both found in custom order, sort by position
39
+ if (aIndex !== -1 && bIndex !== -1) {
40
+ return aIndex - bIndex;
41
+ }
42
+
43
+ // If only one found, prioritize it
44
+ if (aIndex !== -1) return -1;
45
+ if (bIndex !== -1) return 1;
46
+
47
+ // If neither found, sort alphabetically
48
+ return sortDirection === 'asc'
49
+ ? a.groupName.localeCompare(b.groupName)
50
+ : b.groupName.localeCompare(a.groupName);
51
+ });
52
+ }
53
+
54
+ return groups.sort((a, b) => {
55
+ return sortDirection === 'asc'
56
+ ? a.groupName.localeCompare(b.groupName)
57
+ : b.groupName.localeCompare(a.groupName);
58
+ });
59
+ };
60
+
61
+ export const processGroupData = (data, groupingConfig) => {
62
+ if (!data || !Array.isArray(data) || !groupingConfig?.enabled) {
63
+ return {
64
+ grouped: false,
65
+ data: data || []
66
+ };
67
+ }
68
+
69
+ const { groupByField, rowsPerGroup = 10, showEmptyRows = true } = groupingConfig;
70
+
71
+ // Group data by the specified field
72
+ const grouped = data.reduce((acc, row) => {
73
+ const groupValue = getNestedValue(row, groupByField) || 'Unassigned';
74
+ if (!acc[groupValue]) {
75
+ acc[groupValue] = [];
76
+ }
77
+ acc[groupValue].push(row);
78
+ return acc;
79
+ }, {});
80
+
81
+ // Process each group
82
+ const processedGroups = Object.keys(grouped).map(groupName => {
83
+ const rows = grouped[groupName];
84
+ const emptyRows = [];
85
+
86
+ if (showEmptyRows && rows.length < rowsPerGroup) {
87
+ const emptyRowsCount = rowsPerGroup - rows.length;
88
+ for (let i = 0; i < emptyRowsCount; i++) {
89
+ emptyRows.push({ __empty_row__: true, id: `empty_${groupName}_${i}` });
90
+ }
91
+ }
92
+
93
+ return {
94
+ groupName,
95
+ groupDisplayName: groupName,
96
+ rows,
97
+ emptyRows,
98
+ totalRows: rows.length,
99
+ maxRows: rowsPerGroup,
100
+ hasData: rows.length > 0
101
+ };
102
+ });
103
+
104
+ // Sort groups
105
+ const sortedGroups = sortGroups(
106
+ processedGroups,
107
+ groupingConfig.sortDirection,
108
+ groupingConfig.customGroupOrder
109
+ );
110
+
111
+ return {
112
+ grouped: true,
113
+ groupingConfig,
114
+ groups: sortedGroups,
115
+ totalGroups: sortedGroups.length,
116
+ totalRecords: data.length
117
+ };
118
+ };
@@ -3299,3 +3299,207 @@
3299
3299
  transform: translateY(0);
3300
3300
  }
3301
3301
  }
3302
+
3303
+ /* Grouping Configuration Styles */
3304
+ .groupingSection {
3305
+ padding: 24px;
3306
+ background-color: var(--bg-color);
3307
+ border-radius: var(--radius);
3308
+ border: 1px solid var(--border-color);
3309
+ }
3310
+
3311
+ .groupingToggle {
3312
+ margin-bottom: 24px;
3313
+ padding: 20px;
3314
+ background-color: var(--tertiary-color);
3315
+ border-radius: var(--radius);
3316
+ border: 1px solid var(--border-color);
3317
+ }
3318
+
3319
+ .toggleLabel {
3320
+ display: flex;
3321
+ align-items: center;
3322
+ cursor: pointer;
3323
+ margin-bottom: 8px;
3324
+ font-weight: 500;
3325
+ color: var(--header-color);
3326
+ }
3327
+
3328
+ .toggleInput {
3329
+ margin-right: 12px;
3330
+ width: 18px;
3331
+ height: 18px;
3332
+ cursor: pointer;
3333
+ }
3334
+
3335
+ .toggleCheckbox {
3336
+ margin-right: 8px;
3337
+ }
3338
+
3339
+ .toggleText {
3340
+ font-size: 1rem;
3341
+ }
3342
+
3343
+ .helpText {
3344
+ margin: 8px 0 0 0;
3345
+ font-size: 0.875rem;
3346
+ color: var(--paragraph-color);
3347
+ line-height: 1.4;
3348
+ }
3349
+
3350
+ .groupingOptions {
3351
+ animation: fadeIn 0.3s ease-in-out;
3352
+ }
3353
+
3354
+ @keyframes fadeIn {
3355
+ from { opacity: 0; transform: translateY(-10px); }
3356
+ to { opacity: 1; transform: translateY(0); }
3357
+ }
3358
+
3359
+ .formLabel {
3360
+ display: block;
3361
+ margin-bottom: 6px;
3362
+ font-weight: 500;
3363
+ color: var(--header-color);
3364
+ font-size: 0.875rem;
3365
+ }
3366
+
3367
+ .fieldHelp {
3368
+ margin-top: 6px;
3369
+ font-size: 0.75rem;
3370
+ color: var(--paragraph-color);
3371
+ line-height: 1.3;
3372
+ }
3373
+
3374
+ .advancedGroupOptions {
3375
+ margin-top: 24px;
3376
+ padding: 20px;
3377
+ background-color: var(--tertiary-color);
3378
+ border-radius: var(--radius);
3379
+ border: 1px solid var(--border-color);
3380
+ }
3381
+
3382
+ .sectionTitle {
3383
+ margin: 0 0 16px 0;
3384
+ font-size: 1rem;
3385
+ font-weight: 600;
3386
+ color: var(--header-color);
3387
+ border-bottom: 1px solid var(--border-color);
3388
+ padding-bottom: 8px;
3389
+ }
3390
+
3391
+ .optionsGrid {
3392
+ display: grid;
3393
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
3394
+ gap: 16px;
3395
+ margin-bottom: 20px;
3396
+ }
3397
+
3398
+ .checkboxGrid {
3399
+ display: grid;
3400
+ grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
3401
+ gap: 12px;
3402
+ }
3403
+
3404
+ .checkboxLabel {
3405
+ display: flex;
3406
+ align-items: center;
3407
+ cursor: pointer;
3408
+ font-size: 0.875rem;
3409
+ color: var(--header-color);
3410
+
3411
+ input[type="checkbox"] {
3412
+ margin-right: 8px;
3413
+ width: 16px;
3414
+ height: 16px;
3415
+ cursor: pointer;
3416
+ }
3417
+ }
3418
+
3419
+ .checkboxText {
3420
+ line-height: 1.2;
3421
+ }
3422
+
3423
+ .groupingPreview {
3424
+ margin-top: 24px;
3425
+ padding: 16px;
3426
+ background-color: var(--bg-color);
3427
+ border-radius: var(--radius);
3428
+ border: 1px solid var(--primary-color);
3429
+ }
3430
+
3431
+ .previewBox {
3432
+ background-color: var(--tertiary-color);
3433
+ padding: 16px;
3434
+ border-radius: var(--radius);
3435
+ border: 1px solid var(--border-color);
3436
+
3437
+ p {
3438
+ margin: 0 0 8px 0;
3439
+ font-size: 0.875rem;
3440
+ color: var(--paragraph-color);
3441
+
3442
+ &:last-child {
3443
+ margin-bottom: 0;
3444
+ }
3445
+
3446
+ strong {
3447
+ color: var(--header-color);
3448
+ font-weight: 600;
3449
+ }
3450
+ }
3451
+ }
3452
+
3453
+ /* View Toggle Styles */
3454
+ .viewToggle {
3455
+ display: flex;
3456
+ align-items: center;
3457
+ gap: 8px;
3458
+ margin-left: 16px;
3459
+ padding: 8px 12px;
3460
+ background-color: var(--tertiary-color);
3461
+ border-radius: var(--radius);
3462
+ border: 1px solid var(--border-color);
3463
+ }
3464
+
3465
+ .viewLabel {
3466
+ font-size: 0.875rem;
3467
+ font-weight: 500;
3468
+ color: var(--header-color);
3469
+ margin-right: 4px;
3470
+ }
3471
+
3472
+ .viewToggle .btn {
3473
+ padding: 6px 12px;
3474
+ font-size: 0.8rem;
3475
+ border: 1px solid var(--border-color);
3476
+ background-color: var(--bg-color);
3477
+ color: var(--paragraph-color);
3478
+ margin: 0;
3479
+
3480
+ &:hover {
3481
+ background-color: var(--secondary-color);
3482
+ color: var(--header-color);
3483
+ }
3484
+
3485
+ &.active {
3486
+ background-color: var(--primary-color);
3487
+ color: white;
3488
+ border-color: var(--primary-color);
3489
+
3490
+ &:hover {
3491
+ background-color: var(--primary-dark-color);
3492
+ }
3493
+ }
3494
+ }
3495
+
3496
+ .previewActions {
3497
+ display: flex;
3498
+ align-items: center;
3499
+ justify-content: flex-start;
3500
+ margin-bottom: 20px;
3501
+ padding: 16px;
3502
+ background-color: var(--tertiary-color);
3503
+ border-radius: var(--radius);
3504
+ border: 1px solid var(--border-color);
3505
+ }
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,