@visns-studio/visns-components 5.13.8 → 5.13.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.
- package/package.json +1 -1
- package/src/components/generic/GenericDetail.jsx +148 -4
- package/src/components/generic/GenericEditableTable.jsx +40 -29
- package/src/components/proposal/AgreementSignatureEditor.css +491 -0
- package/src/components/proposal/AgreementSignatureEditor.jsx +644 -0
- package/src/components/proposal/BrandingProfileCompanyInfo.css +213 -0
- package/src/components/proposal/BrandingProfileCompanyInfo.jsx +194 -0
- package/src/components/proposal/HeaderConfigEditor.css +412 -0
- package/src/components/proposal/HeaderConfigEditor.jsx +219 -0
- package/src/components/proposal/ProposalTemplatePreview.css +53 -53
- package/src/components/proposal/ProposalTemplateSectionManager.css +69 -50
- package/src/components/proposal/ProposalTemplateSectionManager.jsx +87 -8
- package/src/components/proposal/SectionEditor.css +54 -54
- package/src/components/proposal/SectionEditor.jsx +31 -3
- package/src/components/proposal/SectionTypeSelector.css +29 -43
- package/src/components/proposal/index.js +4 -1
- package/src/components/styles/GenericDetail.module.scss +104 -0
- package/src/utils/urlBuilder.js +5 -1
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.13.
|
|
91
|
+
"version": "5.13.10",
|
|
92
92
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
93
93
|
"main": "src/index.js",
|
|
94
94
|
"files": [
|
|
@@ -638,11 +638,46 @@ function GenericDetail({
|
|
|
638
638
|
}
|
|
639
639
|
};
|
|
640
640
|
|
|
641
|
-
const handleExport = ({ url, key }) => {
|
|
641
|
+
const handleExport = ({ url, key, method = 'GET' }) => {
|
|
642
642
|
if (!url) return toast.error('Export URL not found');
|
|
643
643
|
|
|
644
|
-
|
|
645
|
-
|
|
644
|
+
let exportUrl = url;
|
|
645
|
+
|
|
646
|
+
// Replace {id} placeholder with actual ID value
|
|
647
|
+
if (key && data[key]) {
|
|
648
|
+
exportUrl = url.replace('{id}', data[key]);
|
|
649
|
+
} else if (url.includes('{id}') && data.id) {
|
|
650
|
+
exportUrl = url.replace('{id}', data.id);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Legacy support: append key value to URL if no placeholder found
|
|
654
|
+
if (!url.includes('{id}') && key && data[key]) {
|
|
655
|
+
exportUrl = `${url}/${data[key]}`;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (method === 'POST') {
|
|
659
|
+
// Create a form to submit POST request
|
|
660
|
+
const form = document.createElement('form');
|
|
661
|
+
form.method = 'POST';
|
|
662
|
+
form.action = exportUrl;
|
|
663
|
+
form.target = '_blank';
|
|
664
|
+
|
|
665
|
+
// Add CSRF token if available
|
|
666
|
+
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
|
667
|
+
if (csrfToken) {
|
|
668
|
+
const csrfInput = document.createElement('input');
|
|
669
|
+
csrfInput.type = 'hidden';
|
|
670
|
+
csrfInput.name = '_token';
|
|
671
|
+
csrfInput.value = csrfToken;
|
|
672
|
+
form.appendChild(csrfInput);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
document.body.appendChild(form);
|
|
676
|
+
form.submit();
|
|
677
|
+
document.body.removeChild(form);
|
|
678
|
+
} else {
|
|
679
|
+
window.open(exportUrl);
|
|
680
|
+
}
|
|
646
681
|
};
|
|
647
682
|
|
|
648
683
|
const handleCustomAction = async (action) => {
|
|
@@ -1398,6 +1433,115 @@ function GenericDetail({
|
|
|
1398
1433
|
case 'total':
|
|
1399
1434
|
return renderTotal();
|
|
1400
1435
|
default:
|
|
1436
|
+
if (id === 'proposalDynamic') {
|
|
1437
|
+
return size === 'full' && truncatedValue
|
|
1438
|
+
? <div className={styles.proposalDynamicContainer}>
|
|
1439
|
+
<iframe
|
|
1440
|
+
srcDoc={`
|
|
1441
|
+
<!DOCTYPE html>
|
|
1442
|
+
<html>
|
|
1443
|
+
<head>
|
|
1444
|
+
<style>
|
|
1445
|
+
body {
|
|
1446
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
1447
|
+
font-size: 14px;
|
|
1448
|
+
line-height: 1.5;
|
|
1449
|
+
color: #333;
|
|
1450
|
+
margin: 0;
|
|
1451
|
+
padding: 16px;
|
|
1452
|
+
overflow-wrap: break-word;
|
|
1453
|
+
word-wrap: break-word;
|
|
1454
|
+
}
|
|
1455
|
+
h1, h2, h3, h4, h5, h6 {
|
|
1456
|
+
font-size: 1.2em;
|
|
1457
|
+
font-weight: bold;
|
|
1458
|
+
margin: 0.5em 0;
|
|
1459
|
+
padding: 0;
|
|
1460
|
+
line-height: 1.3;
|
|
1461
|
+
}
|
|
1462
|
+
p, div, span {
|
|
1463
|
+
margin: 0 0 0.5em 0;
|
|
1464
|
+
padding: 0;
|
|
1465
|
+
font-size: 14px;
|
|
1466
|
+
line-height: 1.5;
|
|
1467
|
+
}
|
|
1468
|
+
table {
|
|
1469
|
+
max-width: 100%;
|
|
1470
|
+
border-collapse: collapse;
|
|
1471
|
+
font-size: 14px;
|
|
1472
|
+
margin: 0.5em 0;
|
|
1473
|
+
}
|
|
1474
|
+
td, th {
|
|
1475
|
+
padding: 0.25em 0.5em;
|
|
1476
|
+
font-size: 14px;
|
|
1477
|
+
line-height: 1.3;
|
|
1478
|
+
}
|
|
1479
|
+
img {
|
|
1480
|
+
max-width: 100%;
|
|
1481
|
+
height: auto;
|
|
1482
|
+
display: block;
|
|
1483
|
+
margin: 0.5em 0;
|
|
1484
|
+
}
|
|
1485
|
+
</style>
|
|
1486
|
+
</head>
|
|
1487
|
+
<body>
|
|
1488
|
+
<br /><p>${truncatedValue}</p>
|
|
1489
|
+
</body>
|
|
1490
|
+
</html>
|
|
1491
|
+
`}
|
|
1492
|
+
style={{
|
|
1493
|
+
width: '100%',
|
|
1494
|
+
minHeight: '400px',
|
|
1495
|
+
border: 'none',
|
|
1496
|
+
backgroundColor: 'transparent'
|
|
1497
|
+
}}
|
|
1498
|
+
onLoad={(e) => {
|
|
1499
|
+
const iframe = e.target;
|
|
1500
|
+
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
|
1501
|
+
const height = doc.body.scrollHeight;
|
|
1502
|
+
iframe.style.height = height + 'px';
|
|
1503
|
+
}}
|
|
1504
|
+
/>
|
|
1505
|
+
</div>
|
|
1506
|
+
: <div className={styles.proposalDynamicContainer}>
|
|
1507
|
+
<iframe
|
|
1508
|
+
srcDoc={`
|
|
1509
|
+
<!DOCTYPE html>
|
|
1510
|
+
<html>
|
|
1511
|
+
<head>
|
|
1512
|
+
<style>
|
|
1513
|
+
body {
|
|
1514
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
1515
|
+
font-size: 14px;
|
|
1516
|
+
line-height: 1.5;
|
|
1517
|
+
color: #333;
|
|
1518
|
+
margin: 0;
|
|
1519
|
+
padding: 16px;
|
|
1520
|
+
overflow-wrap: break-word;
|
|
1521
|
+
word-wrap: break-word;
|
|
1522
|
+
}
|
|
1523
|
+
</style>
|
|
1524
|
+
</head>
|
|
1525
|
+
<body>
|
|
1526
|
+
${truncatedValue}
|
|
1527
|
+
</body>
|
|
1528
|
+
</html>
|
|
1529
|
+
`}
|
|
1530
|
+
style={{
|
|
1531
|
+
width: '100%',
|
|
1532
|
+
minHeight: '200px',
|
|
1533
|
+
border: 'none',
|
|
1534
|
+
backgroundColor: 'transparent'
|
|
1535
|
+
}}
|
|
1536
|
+
onLoad={(e) => {
|
|
1537
|
+
const iframe = e.target;
|
|
1538
|
+
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
|
1539
|
+
const height = doc.body.scrollHeight;
|
|
1540
|
+
iframe.style.height = height + 'px';
|
|
1541
|
+
}}
|
|
1542
|
+
/>
|
|
1543
|
+
</div>;
|
|
1544
|
+
}
|
|
1401
1545
|
return size === 'full' && truncatedValue
|
|
1402
1546
|
? parse(`<br /><p>${truncatedValue}</p>`)
|
|
1403
1547
|
: truncatedValue;
|
|
@@ -2528,7 +2672,7 @@ function GenericDetail({
|
|
|
2528
2672
|
)
|
|
2529
2673
|
}
|
|
2530
2674
|
>
|
|
2531
|
-
Export
|
|
2675
|
+
{activeTabConfig.export.label || 'Export'}
|
|
2532
2676
|
</button>
|
|
2533
2677
|
)}
|
|
2534
2678
|
|
|
@@ -370,7 +370,7 @@ const GenericEditableTable = ({
|
|
|
370
370
|
if (coloursValue) {
|
|
371
371
|
return `${coloursValue}40`; // Add 40% opacity
|
|
372
372
|
}
|
|
373
|
-
|
|
373
|
+
|
|
374
374
|
// Check for status color if no manual color is set
|
|
375
375
|
const statusColor = getStatusColor(entry);
|
|
376
376
|
return statusColor ? `${statusColor}40` : undefined; // Add 40% opacity
|
|
@@ -396,11 +396,10 @@ const GenericEditableTable = ({
|
|
|
396
396
|
return statusOption?.colour || undefined;
|
|
397
397
|
}
|
|
398
398
|
}
|
|
399
|
-
|
|
399
|
+
|
|
400
400
|
return undefined;
|
|
401
401
|
};
|
|
402
402
|
|
|
403
|
-
|
|
404
403
|
// Calculate total project cost (sum of all entries excluding margin row)
|
|
405
404
|
const calculateTotalProjectCost = (dataEntries) => {
|
|
406
405
|
const totalColIndex = columns.findIndex((c) => c.type === 'total');
|
|
@@ -425,8 +424,8 @@ const GenericEditableTable = ({
|
|
|
425
424
|
if (!whereConditions || !Array.isArray(whereConditions)) {
|
|
426
425
|
return true;
|
|
427
426
|
}
|
|
428
|
-
|
|
429
|
-
return whereConditions.every(condition => {
|
|
427
|
+
|
|
428
|
+
return whereConditions.every((condition) => {
|
|
430
429
|
const { id, value } = condition;
|
|
431
430
|
return row[id] === value;
|
|
432
431
|
});
|
|
@@ -472,9 +471,11 @@ const GenericEditableTable = ({
|
|
|
472
471
|
if (!data || !Array.isArray(data)) {
|
|
473
472
|
return 0;
|
|
474
473
|
}
|
|
475
|
-
|
|
476
|
-
const filteredData = data.filter(row =>
|
|
477
|
-
|
|
474
|
+
|
|
475
|
+
const filteredData = data.filter((row) =>
|
|
476
|
+
matchesWhereConditions(row, whereConditions)
|
|
477
|
+
);
|
|
478
|
+
|
|
478
479
|
return filteredData.reduce((sum, entry) => {
|
|
479
480
|
return sum + calculateTotal(entry, keys);
|
|
480
481
|
}, 0);
|
|
@@ -1294,7 +1295,7 @@ const GenericEditableTable = ({
|
|
|
1294
1295
|
textAlign: 'left',
|
|
1295
1296
|
width: '20%',
|
|
1296
1297
|
}}
|
|
1297
|
-
colSpan={
|
|
1298
|
+
colSpan={2}
|
|
1298
1299
|
>
|
|
1299
1300
|
Margin %
|
|
1300
1301
|
</th>
|
|
@@ -1307,6 +1308,7 @@ const GenericEditableTable = ({
|
|
|
1307
1308
|
textAlign: 'right',
|
|
1308
1309
|
width: '20%',
|
|
1309
1310
|
}}
|
|
1311
|
+
colSpan={2}
|
|
1310
1312
|
>
|
|
1311
1313
|
Total
|
|
1312
1314
|
</th>
|
|
@@ -1325,7 +1327,7 @@ const GenericEditableTable = ({
|
|
|
1325
1327
|
{/* Margin % input */}
|
|
1326
1328
|
<td
|
|
1327
1329
|
style={{ textAlign: 'center', padding: '8px' }}
|
|
1328
|
-
colSpan={
|
|
1330
|
+
colSpan={2}
|
|
1329
1331
|
>
|
|
1330
1332
|
<input
|
|
1331
1333
|
type="number"
|
|
@@ -1351,6 +1353,7 @@ const GenericEditableTable = ({
|
|
|
1351
1353
|
fontWeight: 'bold',
|
|
1352
1354
|
padding: '8px',
|
|
1353
1355
|
}}
|
|
1356
|
+
colSpan={2}
|
|
1354
1357
|
>
|
|
1355
1358
|
{totalWithMargin.toLocaleString('en-AU', {
|
|
1356
1359
|
style: 'currency',
|
|
@@ -1661,10 +1664,16 @@ const GenericEditableTable = ({
|
|
|
1661
1664
|
);
|
|
1662
1665
|
}
|
|
1663
1666
|
if (index === totalColIndex) {
|
|
1664
|
-
const totalColumn =
|
|
1667
|
+
const totalColumn =
|
|
1668
|
+
columns[totalColIndex];
|
|
1665
1669
|
const groupTotal =
|
|
1666
1670
|
group.entries
|
|
1667
|
-
.filter(entry =>
|
|
1671
|
+
.filter((entry) =>
|
|
1672
|
+
matchesWhereConditions(
|
|
1673
|
+
entry,
|
|
1674
|
+
totalColumn.where
|
|
1675
|
+
)
|
|
1676
|
+
)
|
|
1668
1677
|
.reduce(
|
|
1669
1678
|
(sum, entry) =>
|
|
1670
1679
|
sum +
|
|
@@ -1728,11 +1737,9 @@ const GenericEditableTable = ({
|
|
|
1728
1737
|
<td
|
|
1729
1738
|
key={column.id}
|
|
1730
1739
|
style={{
|
|
1731
|
-
width:
|
|
1732
|
-
column.width || 'auto',
|
|
1740
|
+
width: column.width || 'auto',
|
|
1733
1741
|
textAlign:
|
|
1734
|
-
column.type ===
|
|
1735
|
-
'currency'
|
|
1742
|
+
column.type === 'currency'
|
|
1736
1743
|
? 'right'
|
|
1737
1744
|
: 'left',
|
|
1738
1745
|
}}
|
|
@@ -1817,21 +1824,25 @@ const GenericEditableTable = ({
|
|
|
1817
1824
|
);
|
|
1818
1825
|
}
|
|
1819
1826
|
if (index === totalColIndex) {
|
|
1820
|
-
const totalColumn =
|
|
1827
|
+
const totalColumn =
|
|
1828
|
+
columns[totalColIndex];
|
|
1821
1829
|
const overallTotal =
|
|
1822
|
-
sortBySortOrder(
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
.filter(entry => matchesWhereConditions(entry, totalColumn.where))
|
|
1826
|
-
.reduce(
|
|
1827
|
-
(sum, entry) =>
|
|
1828
|
-
sum +
|
|
1829
|
-
calculateTotal(
|
|
1830
|
+
sortBySortOrder(localData)
|
|
1831
|
+
.filter((entry) =>
|
|
1832
|
+
matchesWhereConditions(
|
|
1830
1833
|
entry,
|
|
1831
|
-
totalColumn.
|
|
1832
|
-
)
|
|
1833
|
-
|
|
1834
|
-
|
|
1834
|
+
totalColumn.where
|
|
1835
|
+
)
|
|
1836
|
+
)
|
|
1837
|
+
.reduce(
|
|
1838
|
+
(sum, entry) =>
|
|
1839
|
+
sum +
|
|
1840
|
+
calculateTotal(
|
|
1841
|
+
entry,
|
|
1842
|
+
totalColumn.keys
|
|
1843
|
+
),
|
|
1844
|
+
0
|
|
1845
|
+
);
|
|
1835
1846
|
return (
|
|
1836
1847
|
<td key={col.id}>
|
|
1837
1848
|
{overallTotal.toLocaleString(
|