@visns-studio/visns-components 5.0.7 → 5.0.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/package.json
CHANGED
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
76
76
|
},
|
|
77
77
|
"name": "@visns-studio/visns-components",
|
|
78
|
-
"version": "5.0.
|
|
78
|
+
"version": "5.0.9",
|
|
79
79
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
80
80
|
"main": "src/index.js",
|
|
81
81
|
"files": [
|
|
@@ -842,6 +842,8 @@ function Form({
|
|
|
842
842
|
const fetchData = async () => {
|
|
843
843
|
let _formData = { ...formData };
|
|
844
844
|
|
|
845
|
+
console.info('aaa', formType, columnId);
|
|
846
|
+
|
|
845
847
|
if (formSettings.fields.length > 0) {
|
|
846
848
|
formSettings.fields.forEach((item) => {
|
|
847
849
|
const { id, value, type, parent, urlParam } = item;
|
|
@@ -883,23 +885,25 @@ function Form({
|
|
|
883
885
|
method,
|
|
884
886
|
payload = {};
|
|
885
887
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
888
|
+
if (ajaxSetting?.where) {
|
|
889
|
+
const getDataId = () => {
|
|
890
|
+
for (const item of ajaxSetting.where) {
|
|
891
|
+
if (
|
|
892
|
+
item.hasOwnProperty('urlParam') &&
|
|
893
|
+
item.urlParam === 'dataId'
|
|
894
|
+
) {
|
|
895
|
+
return item.value;
|
|
896
|
+
}
|
|
893
897
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
}
|
|
898
|
+
return 0;
|
|
899
|
+
};
|
|
900
|
+
}
|
|
897
901
|
|
|
898
902
|
if (
|
|
899
903
|
formSettings.hasOwnProperty('json') &&
|
|
900
904
|
formSettings.json === true
|
|
901
905
|
) {
|
|
902
|
-
let dataId = getDataId();
|
|
906
|
+
let dataId = paramValue || getDataId();
|
|
903
907
|
|
|
904
908
|
urlSuffix = '/json/get';
|
|
905
909
|
method = 'POST';
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
EyeOpen,
|
|
19
19
|
EyeSlashed,
|
|
20
20
|
File,
|
|
21
|
+
Pencil,
|
|
21
22
|
TrashCan,
|
|
22
23
|
} from 'akar-icons';
|
|
23
24
|
|
|
@@ -76,8 +77,6 @@ function GenericDetail({
|
|
|
76
77
|
if (activeTab) {
|
|
77
78
|
const activeTabConfig = tabs.find((t) => t.id === activeTab.id);
|
|
78
79
|
|
|
79
|
-
console.info(tabs);
|
|
80
|
-
|
|
81
80
|
if (type === 'view') {
|
|
82
81
|
setActiveStage(stage.id);
|
|
83
82
|
} else {
|
|
@@ -135,6 +134,14 @@ function GenericDetail({
|
|
|
135
134
|
}
|
|
136
135
|
};
|
|
137
136
|
|
|
137
|
+
const handleExport = (config) => {
|
|
138
|
+
if (config.url && config.key) {
|
|
139
|
+
window.open(`${config.url}/${data[config.key]}`);
|
|
140
|
+
} else {
|
|
141
|
+
toast.error('Export URL not found');
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
138
145
|
const handleCheckboxUpdate = async () => {
|
|
139
146
|
try {
|
|
140
147
|
if (currentConfigRef.current.functions.checkboxUpdate) {
|
|
@@ -1068,6 +1075,21 @@ function GenericDetail({
|
|
|
1068
1075
|
</button>
|
|
1069
1076
|
</div>
|
|
1070
1077
|
)}
|
|
1078
|
+
|
|
1079
|
+
{activeTabConfig.export && (
|
|
1080
|
+
<div className={styles.polActions}>
|
|
1081
|
+
<button
|
|
1082
|
+
className={styles.btn}
|
|
1083
|
+
onClick={() =>
|
|
1084
|
+
handleExport(
|
|
1085
|
+
activeTabConfig.export
|
|
1086
|
+
)
|
|
1087
|
+
}
|
|
1088
|
+
>
|
|
1089
|
+
Export
|
|
1090
|
+
</button>
|
|
1091
|
+
</div>
|
|
1092
|
+
)}
|
|
1071
1093
|
</>
|
|
1072
1094
|
);
|
|
1073
1095
|
case 'qrCode':
|
|
@@ -1298,6 +1320,8 @@ function GenericDetail({
|
|
|
1298
1320
|
} else {
|
|
1299
1321
|
return null;
|
|
1300
1322
|
}
|
|
1323
|
+
case 'scheduling':
|
|
1324
|
+
return renderSchedulingTable(activeTabConfig);
|
|
1301
1325
|
default:
|
|
1302
1326
|
return null;
|
|
1303
1327
|
}
|
|
@@ -1306,6 +1330,207 @@ function GenericDetail({
|
|
|
1306
1330
|
}
|
|
1307
1331
|
};
|
|
1308
1332
|
|
|
1333
|
+
const renderSchedulingTable = (config) => {
|
|
1334
|
+
const { columns, rows } = config;
|
|
1335
|
+
const scheduleData = data[rows.key]; // Access schedule data directly from the rows
|
|
1336
|
+
|
|
1337
|
+
// Group categories by their category (e.g., Concrete Work)
|
|
1338
|
+
const groupedCategories = groupCategoriesByType(scheduleData, rows);
|
|
1339
|
+
|
|
1340
|
+
const { total, nonTotal } = columns.reduce(
|
|
1341
|
+
(acc, column) => {
|
|
1342
|
+
if (column.total) {
|
|
1343
|
+
acc.total++;
|
|
1344
|
+
} else {
|
|
1345
|
+
acc.nonTotal++;
|
|
1346
|
+
}
|
|
1347
|
+
return acc;
|
|
1348
|
+
},
|
|
1349
|
+
{ total: 0, nonTotal: 0 }
|
|
1350
|
+
);
|
|
1351
|
+
|
|
1352
|
+
return (
|
|
1353
|
+
<div className={styles.gridtxt}>
|
|
1354
|
+
<div className={styles.gridtxt__header}>
|
|
1355
|
+
<span>{config.title || 'Project Scheduling'}</span>
|
|
1356
|
+
</div>
|
|
1357
|
+
|
|
1358
|
+
{groupedCategories.length > 0 ? (
|
|
1359
|
+
groupedCategories.map((group, index) => (
|
|
1360
|
+
<div key={index} className={styles.categoryGroup}>
|
|
1361
|
+
<h3>{group.category}</h3>
|
|
1362
|
+
<table className={styles.schedulingTable}>
|
|
1363
|
+
<thead>
|
|
1364
|
+
<tr>
|
|
1365
|
+
{columns.map((column) => (
|
|
1366
|
+
<th
|
|
1367
|
+
key={column.id}
|
|
1368
|
+
style={
|
|
1369
|
+
column.width
|
|
1370
|
+
? {
|
|
1371
|
+
width: column.width,
|
|
1372
|
+
textAlign:
|
|
1373
|
+
column.type ===
|
|
1374
|
+
'currency'
|
|
1375
|
+
? 'right'
|
|
1376
|
+
: 'left',
|
|
1377
|
+
}
|
|
1378
|
+
: {}
|
|
1379
|
+
}
|
|
1380
|
+
>
|
|
1381
|
+
{column.label}
|
|
1382
|
+
</th>
|
|
1383
|
+
))}
|
|
1384
|
+
<th style={{ width: '5%' }}></th>
|
|
1385
|
+
</tr>
|
|
1386
|
+
</thead>
|
|
1387
|
+
<tbody>
|
|
1388
|
+
{group.entries.map((category, idx) => (
|
|
1389
|
+
<tr key={`row-${idx}`}>
|
|
1390
|
+
{columns.map((column) => (
|
|
1391
|
+
<td
|
|
1392
|
+
key={`column-${idx}-${column.id}`}
|
|
1393
|
+
style={
|
|
1394
|
+
column.type ===
|
|
1395
|
+
'currency'
|
|
1396
|
+
? {
|
|
1397
|
+
textAlign:
|
|
1398
|
+
'right',
|
|
1399
|
+
}
|
|
1400
|
+
: {}
|
|
1401
|
+
}
|
|
1402
|
+
>
|
|
1403
|
+
{column.type === 'currency'
|
|
1404
|
+
? formatCurrency(
|
|
1405
|
+
category[
|
|
1406
|
+
column.id
|
|
1407
|
+
]
|
|
1408
|
+
)
|
|
1409
|
+
: category[column.id]
|
|
1410
|
+
?.label ||
|
|
1411
|
+
category[column.id] ||
|
|
1412
|
+
''}
|
|
1413
|
+
</td>
|
|
1414
|
+
))}
|
|
1415
|
+
<td>
|
|
1416
|
+
<span
|
|
1417
|
+
data-tooltip-id="system-tooltip"
|
|
1418
|
+
data-tooltip-content="Edit"
|
|
1419
|
+
style={{
|
|
1420
|
+
cursor: 'pointer',
|
|
1421
|
+
}}
|
|
1422
|
+
onClick={() =>
|
|
1423
|
+
modalOpen(
|
|
1424
|
+
'update',
|
|
1425
|
+
category.id
|
|
1426
|
+
)
|
|
1427
|
+
}
|
|
1428
|
+
>
|
|
1429
|
+
<Pencil
|
|
1430
|
+
size={18}
|
|
1431
|
+
strokeWidth={2}
|
|
1432
|
+
/>
|
|
1433
|
+
</span>
|
|
1434
|
+
</td>
|
|
1435
|
+
</tr>
|
|
1436
|
+
))}
|
|
1437
|
+
<tr>
|
|
1438
|
+
<td colSpan={nonTotal}>
|
|
1439
|
+
<strong>Total Cost</strong>
|
|
1440
|
+
</td>
|
|
1441
|
+
{columns.map((c) =>
|
|
1442
|
+
c.total ? (
|
|
1443
|
+
<td
|
|
1444
|
+
key={c.id}
|
|
1445
|
+
style={{
|
|
1446
|
+
textAlign: 'right',
|
|
1447
|
+
}}
|
|
1448
|
+
>
|
|
1449
|
+
{calculateTotalCost(
|
|
1450
|
+
group.entries,
|
|
1451
|
+
c.id
|
|
1452
|
+
)}
|
|
1453
|
+
</td>
|
|
1454
|
+
) : null
|
|
1455
|
+
)}
|
|
1456
|
+
<td></td>
|
|
1457
|
+
</tr>
|
|
1458
|
+
</tbody>
|
|
1459
|
+
</table>
|
|
1460
|
+
</div>
|
|
1461
|
+
))
|
|
1462
|
+
) : (
|
|
1463
|
+
<div className={styles.noDataMessage}>
|
|
1464
|
+
<p>No scheduling data has been added.</p>
|
|
1465
|
+
</div>
|
|
1466
|
+
)}
|
|
1467
|
+
|
|
1468
|
+
{config.form && (
|
|
1469
|
+
<div className={styles.polActions}>
|
|
1470
|
+
<button
|
|
1471
|
+
className={styles.btn}
|
|
1472
|
+
onClick={() => modalOpen('create', 0)}
|
|
1473
|
+
>
|
|
1474
|
+
Add
|
|
1475
|
+
</button>
|
|
1476
|
+
</div>
|
|
1477
|
+
)}
|
|
1478
|
+
</div>
|
|
1479
|
+
);
|
|
1480
|
+
};
|
|
1481
|
+
|
|
1482
|
+
// Function to group categories by their type (e.g., Concrete Work, Miscellaneous, etc.)
|
|
1483
|
+
const groupCategoriesByType = (data, rows) => {
|
|
1484
|
+
const grouped = {};
|
|
1485
|
+
|
|
1486
|
+
if (data) {
|
|
1487
|
+
data.forEach((category) => {
|
|
1488
|
+
const categoryType =
|
|
1489
|
+
category[rows.categoryKey.id]?.[rows.categoryKey.label] ||
|
|
1490
|
+
'Miscellaneous'; // Default to 'Miscellaneous' if no category is found
|
|
1491
|
+
if (!grouped[categoryType]) {
|
|
1492
|
+
grouped[categoryType] = {
|
|
1493
|
+
category: categoryType,
|
|
1494
|
+
entries: [],
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
grouped[categoryType].entries.push(category);
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
return Object.values(grouped).sort((a, b) =>
|
|
1502
|
+
a.category.localeCompare(b.category)
|
|
1503
|
+
); // Sort categories alphabetically
|
|
1504
|
+
};
|
|
1505
|
+
|
|
1506
|
+
// Function to calculate the total cost for a group
|
|
1507
|
+
const calculateTotalCost = (entries, id) => {
|
|
1508
|
+
let total = 0;
|
|
1509
|
+
entries.forEach((entry) => {
|
|
1510
|
+
// Parse the cost from the 'materials' field and remove non-numeric characters
|
|
1511
|
+
const cost = parseFloat(entry[id].replace(/[^0-9.-]+/g, '')) || 0;
|
|
1512
|
+
total += cost;
|
|
1513
|
+
});
|
|
1514
|
+
|
|
1515
|
+
// Format the total cost with commas and return it
|
|
1516
|
+
return `$${total.toLocaleString('en-AU', {
|
|
1517
|
+
minimumFractionDigits: 2,
|
|
1518
|
+
maximumFractionDigits: 2,
|
|
1519
|
+
})}`;
|
|
1520
|
+
};
|
|
1521
|
+
|
|
1522
|
+
// Function to format currency values with commas and a currency symbol
|
|
1523
|
+
const formatCurrency = (value) => {
|
|
1524
|
+
if (value) {
|
|
1525
|
+
const cost = parseFloat(value.replace(/[^0-9.-]+/g, '')) || 0; // Remove any non-numeric characters
|
|
1526
|
+
return `$${cost.toLocaleString('en-AU', {
|
|
1527
|
+
minimumFractionDigits: 2,
|
|
1528
|
+
maximumFractionDigits: 2,
|
|
1529
|
+
})}`;
|
|
1530
|
+
}
|
|
1531
|
+
return ''; // Return empty if value is not provided
|
|
1532
|
+
};
|
|
1533
|
+
|
|
1309
1534
|
/** General Hooks */
|
|
1310
1535
|
useEffect(() => {
|
|
1311
1536
|
const handleResize = () => {
|
|
@@ -1428,6 +1653,7 @@ function GenericDetail({
|
|
|
1428
1653
|
});
|
|
1429
1654
|
}
|
|
1430
1655
|
break;
|
|
1656
|
+
case 'scheduling':
|
|
1431
1657
|
case 'overview':
|
|
1432
1658
|
setConfig({});
|
|
1433
1659
|
if (activeTabConfig.form) {
|
|
@@ -1618,6 +1844,9 @@ function GenericDetail({
|
|
|
1618
1844
|
style={userProfile?.settings?.style || {}}
|
|
1619
1845
|
updateForm={setFormData}
|
|
1620
1846
|
userProfile={userProfile}
|
|
1847
|
+
paramValue={
|
|
1848
|
+
routeParams[urlParam] ? routeParams[urlParam] : 0
|
|
1849
|
+
}
|
|
1621
1850
|
/>
|
|
1622
1851
|
</Popup>
|
|
1623
1852
|
</>
|
|
@@ -620,3 +620,58 @@
|
|
|
620
620
|
color: rgba(var(--primary-rgb), 0.65);
|
|
621
621
|
}
|
|
622
622
|
}
|
|
623
|
+
|
|
624
|
+
.schedulingTable {
|
|
625
|
+
width: 100%;
|
|
626
|
+
border-collapse: collapse;
|
|
627
|
+
margin: 1em 0;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
.schedulingTable th,
|
|
631
|
+
.schedulingTable td {
|
|
632
|
+
padding: 0.75rem;
|
|
633
|
+
text-align: left;
|
|
634
|
+
border: 1px solid rgba(var(--primary-rgb), 0.1);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.schedulingTable th {
|
|
638
|
+
background-color: var(--secondary-color); /* Updated header color */
|
|
639
|
+
color: var(--background-color);
|
|
640
|
+
font-weight: bold;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.schedulingTable td {
|
|
644
|
+
background-color: rgba(var(--primary-rgb), 0.05);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.categoryGroup {
|
|
648
|
+
margin-top: 2rem;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
h3 {
|
|
652
|
+
font-size: 1.5em;
|
|
653
|
+
color: var(--primary-color);
|
|
654
|
+
font-weight: 700;
|
|
655
|
+
display: flex;
|
|
656
|
+
justify-content: space-between; /* Ensure space for the Add button */
|
|
657
|
+
align-items: center;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.categoryGroup table {
|
|
661
|
+
width: 100%;
|
|
662
|
+
border-collapse: collapse;
|
|
663
|
+
margin-top: 1rem;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
.categoryGroup td {
|
|
667
|
+
padding: 0.75rem;
|
|
668
|
+
border: 1px solid rgba(var(--primary-rgb), 0.1);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
.noDataMessage {
|
|
672
|
+
text-align: center;
|
|
673
|
+
color: var(--secondary-color);
|
|
674
|
+
font-size: 1.25em;
|
|
675
|
+
padding: 1em;
|
|
676
|
+
margin-top: 2em;
|
|
677
|
+
}
|