@visns-studio/visns-components 5.0.8 → 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 {
|
|
@@ -1321,6 +1320,8 @@ function GenericDetail({
|
|
|
1321
1320
|
} else {
|
|
1322
1321
|
return null;
|
|
1323
1322
|
}
|
|
1323
|
+
case 'scheduling':
|
|
1324
|
+
return renderSchedulingTable(activeTabConfig);
|
|
1324
1325
|
default:
|
|
1325
1326
|
return null;
|
|
1326
1327
|
}
|
|
@@ -1329,6 +1330,207 @@ function GenericDetail({
|
|
|
1329
1330
|
}
|
|
1330
1331
|
};
|
|
1331
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
|
+
|
|
1332
1534
|
/** General Hooks */
|
|
1333
1535
|
useEffect(() => {
|
|
1334
1536
|
const handleResize = () => {
|
|
@@ -1451,6 +1653,7 @@ function GenericDetail({
|
|
|
1451
1653
|
});
|
|
1452
1654
|
}
|
|
1453
1655
|
break;
|
|
1656
|
+
case 'scheduling':
|
|
1454
1657
|
case 'overview':
|
|
1455
1658
|
setConfig({});
|
|
1456
1659
|
if (activeTabConfig.form) {
|
|
@@ -1641,6 +1844,9 @@ function GenericDetail({
|
|
|
1641
1844
|
style={userProfile?.settings?.style || {}}
|
|
1642
1845
|
updateForm={setFormData}
|
|
1643
1846
|
userProfile={userProfile}
|
|
1847
|
+
paramValue={
|
|
1848
|
+
routeParams[urlParam] ? routeParams[urlParam] : 0
|
|
1849
|
+
}
|
|
1644
1850
|
/>
|
|
1645
1851
|
</Popup>
|
|
1646
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
|
+
}
|