@visns-studio/visns-components 5.13.0 → 5.13.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 +82 -1
- package/package.json +1 -1
- package/src/components/generic/GenericEditableTable.jsx +250 -35
package/README.md
CHANGED
|
@@ -36,6 +36,7 @@ VISNS Components is a React-based UI component library that provides a set of re
|
|
|
36
36
|
- **Dropzone** - File upload with drag and drop support
|
|
37
37
|
- **AsyncSelect** - Asynchronous data loading for select components
|
|
38
38
|
- **Creatable Select** - Select components that allow creating new options
|
|
39
|
+
- **GenericEditableTable** - Interactive table component with inline editing, color-coded columns, and multiple data types
|
|
39
40
|
|
|
40
41
|
### Navigation
|
|
41
42
|
|
|
@@ -328,9 +329,10 @@ The DataGrid component utilizes a modular column renderer system with 28 special
|
|
|
328
329
|
- `currency` - Formatted monetary values
|
|
329
330
|
- `date` - Date formatting with validation
|
|
330
331
|
- `datetime` - Date and time formatting
|
|
332
|
+
- `html5_date` - HTML5 native date picker input
|
|
331
333
|
- `arrayCount` - Count of array elements
|
|
332
334
|
- `colour` - Color picker display
|
|
333
|
-
- `dropdown` - Interactive dropdown selections
|
|
335
|
+
- `dropdown` - Interactive dropdown selections with optional background color support
|
|
334
336
|
- `image` - Image display from file URLs
|
|
335
337
|
- `file` - File download links with icons
|
|
336
338
|
- `icons` - Multiple icon display
|
|
@@ -355,6 +357,79 @@ The DataGrid component utilizes a modular column renderer system with 28 special
|
|
|
355
357
|
|
|
356
358
|
All column renderers are exported from `@visns-studio/visns-components` and can be used individually or as part of the DataGrid component.
|
|
357
359
|
|
|
360
|
+
#### Dropdown Column Color Support
|
|
361
|
+
|
|
362
|
+
The dropdown column type now supports automatic background color application based on the selected option's `colour` property. This feature works in both DataGrid and GenericEditableTable components.
|
|
363
|
+
|
|
364
|
+
**Configuration Example:**
|
|
365
|
+
```jsx
|
|
366
|
+
{
|
|
367
|
+
id: 'status',
|
|
368
|
+
label: 'Status',
|
|
369
|
+
type: 'dropdown',
|
|
370
|
+
options: [
|
|
371
|
+
{ id: 'Pending', label: 'Pending', colour: '#FFE5B4' },
|
|
372
|
+
{ id: 'Approved', label: 'Approved', colour: '#B4E5D1' },
|
|
373
|
+
{ id: 'Rejected', label: 'Rejected', colour: '#FFCDD2' },
|
|
374
|
+
{ id: 'Disputed', label: 'Disputed', colour: '#E1F5FE' }
|
|
375
|
+
],
|
|
376
|
+
width: '10%'
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**Features:**
|
|
381
|
+
- **Automatic Color Application**: Cells automatically display the background color of the selected option
|
|
382
|
+
- **Total Column Integration**: Total columns inherit the same background color as the status column in the same row
|
|
383
|
+
- **Consistent Styling**: Black text color is automatically applied for optimal contrast
|
|
384
|
+
- **Flexible Configuration**: Works with both static column options and dynamic dropdown data
|
|
385
|
+
- **Cross-Component Support**: Available in both DataGrid and GenericEditableTable components
|
|
386
|
+
|
|
387
|
+
**Example Usage in GenericEditableTable:**
|
|
388
|
+
```jsx
|
|
389
|
+
<GenericEditableTable
|
|
390
|
+
schedulingConfig={{
|
|
391
|
+
columns: [
|
|
392
|
+
{
|
|
393
|
+
id: 'status',
|
|
394
|
+
label: 'Status',
|
|
395
|
+
type: 'dropdown',
|
|
396
|
+
options: [
|
|
397
|
+
{ id: 'Pending', label: 'Pending', colour: '#FFE5B4' },
|
|
398
|
+
{ id: 'Approved', label: 'Approved', colour: '#B4E5D1' }
|
|
399
|
+
]
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
id: 'total',
|
|
403
|
+
label: 'Total',
|
|
404
|
+
type: 'total',
|
|
405
|
+
keys: ['amount', 'tax'] // Will inherit status color
|
|
406
|
+
}
|
|
407
|
+
]
|
|
408
|
+
}}
|
|
409
|
+
/>
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
#### HTML5 Date Type
|
|
413
|
+
|
|
414
|
+
The `html5_date` column type provides native HTML5 date picker functionality in GenericEditableTable components.
|
|
415
|
+
|
|
416
|
+
**Features:**
|
|
417
|
+
- **Native Date Picker**: Uses browser's built-in date picker for consistent UX
|
|
418
|
+
- **Cross-browser Support**: Works across all modern browsers
|
|
419
|
+
- **Consistent Styling**: Matches existing date field styling
|
|
420
|
+
- **Read-only Support**: Respects column read-only configuration
|
|
421
|
+
- **Change Integration**: Fully integrated with existing field change handlers
|
|
422
|
+
|
|
423
|
+
**Configuration Example:**
|
|
424
|
+
```jsx
|
|
425
|
+
{
|
|
426
|
+
id: 'start_date',
|
|
427
|
+
label: 'Start Date',
|
|
428
|
+
type: 'html5_date',
|
|
429
|
+
width: '15%'
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
358
433
|
#### Intelligent Relationship Sorting
|
|
359
434
|
|
|
360
435
|
The DataGrid component includes intelligent sorting capabilities that automatically detect and enable sorting for relationship and JSON fields. This feature works seamlessly with the backend `HasRelationshipSorting` trait.
|
|
@@ -629,6 +704,12 @@ You can add summary calculations to grouped data:
|
|
|
629
704
|
|
|
630
705
|
The DataGrid component now supports powerful bulk editing capabilities through group-based operations, allowing users to efficiently manage multiple items simultaneously.
|
|
631
706
|
|
|
707
|
+
**New in v5.13.0:**
|
|
708
|
+
- **Dropdown Column Color Support**: Automatic background color application for dropdown columns based on option colors
|
|
709
|
+
- **Total Column Color Integration**: Total columns now inherit background colors from associated status columns
|
|
710
|
+
- **HTML5 Date Type**: Added native HTML5 date picker support for GenericEditableTable components
|
|
711
|
+
- **Enhanced GenericEditableTable**: Improved column type support and consistent styling across components
|
|
712
|
+
|
|
632
713
|
#### How Group Bulk Edit Works
|
|
633
714
|
|
|
634
715
|
When data is grouped using the `groupBy` or `defaultGroupBy` properties, each group header can display action icons for common operations. The new bulk edit functionality adds an "edit" icon that opens a form modal for updating all items in the group simultaneously.
|
package/package.json
CHANGED
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
88
88
|
},
|
|
89
89
|
"name": "@visns-studio/visns-components",
|
|
90
|
-
"version": "5.13.
|
|
90
|
+
"version": "5.13.1",
|
|
91
91
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
92
92
|
"main": "src/index.js",
|
|
93
93
|
"files": [
|
|
@@ -214,6 +214,7 @@ const GenericEditableTable = ({
|
|
|
214
214
|
const { columns, rows, form, title } = schedulingConfig;
|
|
215
215
|
const hasCategory = rows && rows.categoryKey; // Only group if defined
|
|
216
216
|
const hasColourColumn = columns.some((col) => col.id === 'colours');
|
|
217
|
+
const hasMarginRow = rows && rows.marginRow;
|
|
217
218
|
|
|
218
219
|
// dataSets: each item => { created_at: string, data: array of rows }
|
|
219
220
|
const [dataSets, setDataSets] = useState([]);
|
|
@@ -228,6 +229,59 @@ const GenericEditableTable = ({
|
|
|
228
229
|
// Row selection state for copy functionality
|
|
229
230
|
const [selectedRows, setSelectedRows] = useState(new Set());
|
|
230
231
|
|
|
232
|
+
// Margin percentage state for Builder Margin row
|
|
233
|
+
const [marginPercentage, setMarginPercentage] = useState(0);
|
|
234
|
+
|
|
235
|
+
// Handle margin percentage change
|
|
236
|
+
const handleMarginPercentageChange = (percentage) => {
|
|
237
|
+
setMarginPercentage(percentage);
|
|
238
|
+
|
|
239
|
+
// Update or create margin row in localData
|
|
240
|
+
setLocalData((prevData) => {
|
|
241
|
+
const updatedData = [...prevData];
|
|
242
|
+
const marginRowIndex = updatedData.findIndex(
|
|
243
|
+
(entry) => entry.description === rows.marginRow
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const totalProjectCost = calculateTotalProjectCost(updatedData);
|
|
247
|
+
const marginAmount = calculateMarginAmount(
|
|
248
|
+
totalProjectCost,
|
|
249
|
+
percentage
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
const marginRowData = {
|
|
253
|
+
id: 'margin-row',
|
|
254
|
+
description: rows.marginRow,
|
|
255
|
+
category: rows.marginRow,
|
|
256
|
+
sort_order: 9999, // Ensure it's always at the bottom
|
|
257
|
+
margin_percentage: percentage,
|
|
258
|
+
[columns.find((col) => col.type === 'total')?.id]: marginAmount,
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
if (marginRowIndex !== -1) {
|
|
262
|
+
// Update existing margin row
|
|
263
|
+
updatedData[marginRowIndex] = {
|
|
264
|
+
...updatedData[marginRowIndex],
|
|
265
|
+
...marginRowData,
|
|
266
|
+
};
|
|
267
|
+
} else {
|
|
268
|
+
// Add new margin row
|
|
269
|
+
updatedData.push(marginRowData);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Update the active dataset
|
|
273
|
+
const updatedDataSets = [...dataSets];
|
|
274
|
+
updatedDataSets[activeDatasetIndex] = {
|
|
275
|
+
...updatedDataSets[activeDatasetIndex],
|
|
276
|
+
data: updatedData,
|
|
277
|
+
};
|
|
278
|
+
setDataSets(updatedDataSets);
|
|
279
|
+
debouncedUpdate(updatedDataSets);
|
|
280
|
+
|
|
281
|
+
return updatedData;
|
|
282
|
+
});
|
|
283
|
+
};
|
|
284
|
+
|
|
231
285
|
// On mount or when 'data' changes, read from data[schedulingConfig.rows.key] and handle both new & old formats
|
|
232
286
|
useEffect(() => {
|
|
233
287
|
const raw = data && data[schedulingConfig.rows.key];
|
|
@@ -287,12 +341,22 @@ const GenericEditableTable = ({
|
|
|
287
341
|
sort_order: entry.sort_order ?? index + 1,
|
|
288
342
|
}));
|
|
289
343
|
setLocalData(sortBySortOrder(enrichedData));
|
|
344
|
+
|
|
345
|
+
// Initialize margin percentage from existing margin row
|
|
346
|
+
if (hasMarginRow) {
|
|
347
|
+
const marginRow = enrichedData.find(
|
|
348
|
+
(entry) => entry.description === rows.marginRow
|
|
349
|
+
);
|
|
350
|
+
if (marginRow && marginRow.margin_percentage) {
|
|
351
|
+
setMarginPercentage(marginRow.margin_percentage);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
290
354
|
} else {
|
|
291
355
|
setLocalData([]);
|
|
292
356
|
}
|
|
293
357
|
// Clear selected rows when dataset changes
|
|
294
358
|
setSelectedRows(new Set());
|
|
295
|
-
}, [dataSets, activeDatasetIndex]);
|
|
359
|
+
}, [dataSets, activeDatasetIndex, hasMarginRow, rows.marginRow]);
|
|
296
360
|
|
|
297
361
|
// Helper to sort by sort_order
|
|
298
362
|
const sortBySortOrder = (dataArray) =>
|
|
@@ -311,48 +375,58 @@ const GenericEditableTable = ({
|
|
|
311
375
|
if (column.type === 'dropdown' && column.options) {
|
|
312
376
|
const selectedValue = entry[column.id];
|
|
313
377
|
if (!selectedValue) return undefined;
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
columnOptions: column.options
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
const selectedOption = column.options.find(option => option.id === selectedValue);
|
|
323
|
-
console.log('Found selected option:', selectedOption);
|
|
324
|
-
|
|
378
|
+
|
|
379
|
+
const selectedOption = column.options.find(
|
|
380
|
+
(option) => option.id === selectedValue
|
|
381
|
+
);
|
|
382
|
+
|
|
325
383
|
return selectedOption?.colour || undefined;
|
|
326
384
|
}
|
|
327
|
-
|
|
385
|
+
|
|
328
386
|
// For total columns, look for a status column with color information
|
|
329
387
|
if (column.type === 'total') {
|
|
330
388
|
// Find the status column (typically named 'status' and has dropdown type with options)
|
|
331
|
-
const statusColumn = columns.find(
|
|
332
|
-
col
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
389
|
+
const statusColumn = columns.find(
|
|
390
|
+
(col) =>
|
|
391
|
+
col.type === 'dropdown' &&
|
|
392
|
+
col.options &&
|
|
393
|
+
col.options.some((option) => option.colour) &&
|
|
394
|
+
(col.id === 'status' ||
|
|
395
|
+
col.id.toLowerCase().includes('status'))
|
|
336
396
|
);
|
|
337
|
-
|
|
397
|
+
|
|
338
398
|
if (statusColumn) {
|
|
339
399
|
const statusValue = entry[statusColumn.id];
|
|
340
400
|
if (statusValue) {
|
|
341
|
-
const statusOption = statusColumn.options.find(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
statusColumnId: statusColumn.id,
|
|
345
|
-
statusValue,
|
|
346
|
-
statusOption
|
|
347
|
-
});
|
|
401
|
+
const statusOption = statusColumn.options.find(
|
|
402
|
+
(option) => option.id === statusValue
|
|
403
|
+
);
|
|
348
404
|
return statusOption?.colour || undefined;
|
|
349
405
|
}
|
|
350
406
|
}
|
|
351
407
|
}
|
|
352
|
-
|
|
408
|
+
|
|
353
409
|
return undefined;
|
|
354
410
|
};
|
|
355
411
|
|
|
412
|
+
// Calculate total project cost (sum of all entries excluding margin row)
|
|
413
|
+
const calculateTotalProjectCost = (dataEntries) => {
|
|
414
|
+
const totalColIndex = columns.findIndex((c) => c.type === 'total');
|
|
415
|
+
if (totalColIndex === -1) return 0;
|
|
416
|
+
|
|
417
|
+
const totalColumn = columns[totalColIndex];
|
|
418
|
+
return dataEntries
|
|
419
|
+
.filter((entry) => entry.description !== rows.marginRow) // Exclude margin row
|
|
420
|
+
.reduce(
|
|
421
|
+
(sum, entry) => sum + calculateTotal(entry, totalColumn.keys),
|
|
422
|
+
0
|
|
423
|
+
);
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
// Calculate margin amount based on percentage
|
|
427
|
+
const calculateMarginAmount = (totalProjectCost, percentage) => {
|
|
428
|
+
return totalProjectCost * (percentage / 100);
|
|
429
|
+
};
|
|
356
430
|
|
|
357
431
|
// Calculate total for columns of type 'total'
|
|
358
432
|
const calculateTotal = (entry, keys) => {
|
|
@@ -1111,6 +1185,126 @@ const GenericEditableTable = ({
|
|
|
1111
1185
|
}
|
|
1112
1186
|
};
|
|
1113
1187
|
|
|
1188
|
+
// Render Builder Margin row - should appear after all categories
|
|
1189
|
+
const renderMarginRow = () => {
|
|
1190
|
+
if (!hasMarginRow) return null;
|
|
1191
|
+
|
|
1192
|
+
const totalProjectCost = calculateTotalProjectCost(localData);
|
|
1193
|
+
const marginAmount = calculateMarginAmount(
|
|
1194
|
+
totalProjectCost,
|
|
1195
|
+
marginPercentage
|
|
1196
|
+
);
|
|
1197
|
+
const totalWithMargin = totalProjectCost + marginAmount;
|
|
1198
|
+
|
|
1199
|
+
return (
|
|
1200
|
+
<>
|
|
1201
|
+
{/* Margin row category header */}
|
|
1202
|
+
<tr className={styles.categoryRow}>
|
|
1203
|
+
<td
|
|
1204
|
+
colSpan={columns.length + 2}
|
|
1205
|
+
style={{
|
|
1206
|
+
backgroundColor: '#e9ecef',
|
|
1207
|
+
fontWeight: 'bold',
|
|
1208
|
+
padding: '8px 12px',
|
|
1209
|
+
}}
|
|
1210
|
+
>
|
|
1211
|
+
{rows.marginRow}
|
|
1212
|
+
</td>
|
|
1213
|
+
</tr>
|
|
1214
|
+
|
|
1215
|
+
{/* Margin row column headers - only show Description, Margin %, and Total */}
|
|
1216
|
+
<tr>
|
|
1217
|
+
{/* Description column - larger width */}
|
|
1218
|
+
<th
|
|
1219
|
+
style={{
|
|
1220
|
+
color: 'white',
|
|
1221
|
+
fontWeight: 'bold',
|
|
1222
|
+
padding: '8px',
|
|
1223
|
+
textAlign: 'left',
|
|
1224
|
+
width: '60%',
|
|
1225
|
+
}}
|
|
1226
|
+
colSpan={6}
|
|
1227
|
+
>
|
|
1228
|
+
Description
|
|
1229
|
+
</th>
|
|
1230
|
+
{/* Margin % column - smaller width */}
|
|
1231
|
+
<th
|
|
1232
|
+
style={{
|
|
1233
|
+
color: 'white',
|
|
1234
|
+
fontWeight: 'bold',
|
|
1235
|
+
padding: '8px',
|
|
1236
|
+
textAlign: 'left',
|
|
1237
|
+
width: '20%',
|
|
1238
|
+
}}
|
|
1239
|
+
colSpan={3}
|
|
1240
|
+
>
|
|
1241
|
+
Margin %
|
|
1242
|
+
</th>
|
|
1243
|
+
{/* Total column */}
|
|
1244
|
+
<th
|
|
1245
|
+
style={{
|
|
1246
|
+
color: 'white',
|
|
1247
|
+
fontWeight: 'bold',
|
|
1248
|
+
padding: '8px',
|
|
1249
|
+
textAlign: 'right',
|
|
1250
|
+
width: '20%',
|
|
1251
|
+
}}
|
|
1252
|
+
>
|
|
1253
|
+
Total
|
|
1254
|
+
</th>
|
|
1255
|
+
<th></th>
|
|
1256
|
+
</tr>
|
|
1257
|
+
|
|
1258
|
+
{/* Margin row content */}
|
|
1259
|
+
<tr style={{ backgroundColor: '#f8f9fa' }}>
|
|
1260
|
+
{/* Description */}
|
|
1261
|
+
<td
|
|
1262
|
+
style={{ fontWeight: 'bold', padding: '8px' }}
|
|
1263
|
+
colSpan={6}
|
|
1264
|
+
>
|
|
1265
|
+
{rows.marginRow}
|
|
1266
|
+
</td>
|
|
1267
|
+
{/* Margin % input */}
|
|
1268
|
+
<td
|
|
1269
|
+
style={{ textAlign: 'center', padding: '8px' }}
|
|
1270
|
+
colSpan={3}
|
|
1271
|
+
>
|
|
1272
|
+
<input
|
|
1273
|
+
type="number"
|
|
1274
|
+
value={marginPercentage}
|
|
1275
|
+
onChange={(e) =>
|
|
1276
|
+
handleMarginPercentageChange(
|
|
1277
|
+
parseFloat(e.target.value) || 0
|
|
1278
|
+
)
|
|
1279
|
+
}
|
|
1280
|
+
placeholder="% margin"
|
|
1281
|
+
style={{
|
|
1282
|
+
width: '80px',
|
|
1283
|
+
padding: '6px',
|
|
1284
|
+
border: '1px solid #ccc',
|
|
1285
|
+
borderRadius: '3px',
|
|
1286
|
+
}}
|
|
1287
|
+
/>
|
|
1288
|
+
</td>
|
|
1289
|
+
{/* Total (subtotal of all categories + margin) */}
|
|
1290
|
+
<td
|
|
1291
|
+
style={{
|
|
1292
|
+
textAlign: 'right',
|
|
1293
|
+
fontWeight: 'bold',
|
|
1294
|
+
padding: '8px',
|
|
1295
|
+
}}
|
|
1296
|
+
>
|
|
1297
|
+
{totalWithMargin.toLocaleString('en-AU', {
|
|
1298
|
+
style: 'currency',
|
|
1299
|
+
currency: 'AUD',
|
|
1300
|
+
})}
|
|
1301
|
+
</td>
|
|
1302
|
+
<td></td>
|
|
1303
|
+
</tr>
|
|
1304
|
+
</>
|
|
1305
|
+
);
|
|
1306
|
+
};
|
|
1307
|
+
|
|
1114
1308
|
// Inline form for adding a new row
|
|
1115
1309
|
const renderNewRowForm = (groupId = null) => {
|
|
1116
1310
|
const newRowData = hasCategory ? newEntries[groupId] || {} : newEntry;
|
|
@@ -1292,7 +1486,11 @@ const GenericEditableTable = ({
|
|
|
1292
1486
|
>
|
|
1293
1487
|
{renderRowCheckbox(entry)}
|
|
1294
1488
|
{columns.map((column) => {
|
|
1295
|
-
const cellBgColor =
|
|
1489
|
+
const cellBgColor =
|
|
1490
|
+
getCellBackground(
|
|
1491
|
+
entry,
|
|
1492
|
+
column
|
|
1493
|
+
);
|
|
1296
1494
|
return (
|
|
1297
1495
|
<td
|
|
1298
1496
|
key={column.id}
|
|
@@ -1302,8 +1500,11 @@ const GenericEditableTable = ({
|
|
|
1302
1500
|
'currency'
|
|
1303
1501
|
? 'right'
|
|
1304
1502
|
: 'left',
|
|
1305
|
-
backgroundColor:
|
|
1306
|
-
|
|
1503
|
+
backgroundColor:
|
|
1504
|
+
cellBgColor,
|
|
1505
|
+
color: cellBgColor
|
|
1506
|
+
? '#000'
|
|
1507
|
+
: 'inherit',
|
|
1307
1508
|
}}
|
|
1308
1509
|
>
|
|
1309
1510
|
{renderScheduledTableField(
|
|
@@ -1446,6 +1647,9 @@ const GenericEditableTable = ({
|
|
|
1446
1647
|
)}
|
|
1447
1648
|
</React.Fragment>
|
|
1448
1649
|
))}
|
|
1650
|
+
|
|
1651
|
+
{/* Render Builder Margin row */}
|
|
1652
|
+
{renderMarginRow()}
|
|
1449
1653
|
</tbody>
|
|
1450
1654
|
</table>
|
|
1451
1655
|
) : (
|
|
@@ -1474,18 +1678,26 @@ const GenericEditableTable = ({
|
|
|
1474
1678
|
>
|
|
1475
1679
|
{renderRowCheckbox(entry)}
|
|
1476
1680
|
{columns.map((column) => {
|
|
1477
|
-
const cellBgColor = getCellBackground(
|
|
1681
|
+
const cellBgColor = getCellBackground(
|
|
1682
|
+
entry,
|
|
1683
|
+
column
|
|
1684
|
+
);
|
|
1478
1685
|
return (
|
|
1479
1686
|
<td
|
|
1480
1687
|
key={column.id}
|
|
1481
1688
|
style={{
|
|
1482
|
-
width:
|
|
1689
|
+
width:
|
|
1690
|
+
column.width || 'auto',
|
|
1483
1691
|
textAlign:
|
|
1484
|
-
column.type ===
|
|
1692
|
+
column.type ===
|
|
1693
|
+
'currency'
|
|
1485
1694
|
? 'right'
|
|
1486
1695
|
: 'left',
|
|
1487
|
-
backgroundColor:
|
|
1488
|
-
|
|
1696
|
+
backgroundColor:
|
|
1697
|
+
cellBgColor,
|
|
1698
|
+
color: cellBgColor
|
|
1699
|
+
? '#000'
|
|
1700
|
+
: 'inherit',
|
|
1489
1701
|
}}
|
|
1490
1702
|
>
|
|
1491
1703
|
{renderScheduledTableField(
|
|
@@ -1600,6 +1812,9 @@ const GenericEditableTable = ({
|
|
|
1600
1812
|
<td></td>
|
|
1601
1813
|
</tr>
|
|
1602
1814
|
)}
|
|
1815
|
+
|
|
1816
|
+
{/* Render Builder Margin row */}
|
|
1817
|
+
{renderMarginRow()}
|
|
1603
1818
|
</tbody>
|
|
1604
1819
|
</table>
|
|
1605
1820
|
)}
|