Skip to content

Commit 29d7b9f

Browse files
authored
Merge pull request #707 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents c61757e + ffc8745 commit 29d7b9f

File tree

1 file changed

+173
-45
lines changed

1 file changed

+173
-45
lines changed

src/pages/tenant/manage/drift.js

Lines changed: 173 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,42 @@ const ManageDriftPage = () => {
313313
return null;
314314
};
315315

316+
// Helper function to compare JSON objects and find differences
317+
const compareJsonObjects = (expected, current) => {
318+
if (!expected || !current) return null;
319+
320+
try {
321+
const expectedObj = typeof expected === "string" ? JSON.parse(expected) : expected;
322+
const currentObj = typeof current === "string" ? JSON.parse(current) : current;
323+
324+
// Deep comparison - if they're equal, return null (no diff)
325+
if (JSON.stringify(expectedObj) === JSON.stringify(currentObj)) {
326+
return null; // No differences
327+
}
328+
329+
// Find differences
330+
const differences = {};
331+
const allKeys = new Set([...Object.keys(expectedObj), ...Object.keys(currentObj)]);
332+
333+
allKeys.forEach((key) => {
334+
const expectedVal = expectedObj[key];
335+
const currentVal = currentObj[key];
336+
337+
if (JSON.stringify(expectedVal) !== JSON.stringify(currentVal)) {
338+
differences[key] = {
339+
expected: expectedVal,
340+
current: currentVal,
341+
};
342+
}
343+
});
344+
345+
return Object.keys(differences).length > 0 ? differences : null;
346+
} catch (e) {
347+
console.error("Error comparing JSON objects:", e);
348+
return null;
349+
}
350+
};
351+
316352
// Helper function to format policy objects for display
317353
const formatPolicyValue = (value) => {
318354
if (!value) return "N/A";
@@ -340,6 +376,21 @@ const ManageDriftPage = () => {
340376
// Helper function to create deviation items
341377
const createDeviationItems = (deviations, statusOverride = null) => {
342378
return (deviations || []).map((deviation, index) => {
379+
// Check if this should be skipped due to missing license
380+
const isLicenseSkipped = deviation.LicenseAvailable === false;
381+
382+
// Check if we have both ExpectedValue and CurrentValue for comparison
383+
let isActuallyCompliant = false;
384+
let jsonDifferences = null;
385+
386+
if (deviation.ExpectedValue && deviation.CurrentValue) {
387+
jsonDifferences = compareJsonObjects(deviation.ExpectedValue, deviation.CurrentValue);
388+
// If there are no differences, this is actually compliant
389+
if (jsonDifferences === null) {
390+
isActuallyCompliant = true;
391+
}
392+
}
393+
343394
// Prioritize standardDisplayName from drift data (which has user-friendly names for templates)
344395
// then fallback to standards.json lookup, then raw name
345396
const prettyName =
@@ -354,21 +405,46 @@ const ManageDriftPage = () => {
354405
deviation.standardDescription ||
355406
"No description available";
356407

408+
// Determine the actual status
409+
// If actually compliant (values match), mark as aligned regardless of input status
410+
// If license is skipped, mark as skipped
411+
// Otherwise use the provided status
412+
const actualStatus = isActuallyCompliant
413+
? "aligned"
414+
: isLicenseSkipped
415+
? "skipped"
416+
: statusOverride || deviation.Status || deviation.state;
417+
const actualStatusText = isActuallyCompliant
418+
? "Compliant"
419+
: isLicenseSkipped
420+
? "Skipped - No License Available"
421+
: getDeviationStatusText(actualStatus);
422+
423+
// For skipped items, show different expected/received values
424+
let displayExpectedValue = deviation.ExpectedValue || deviation.expectedValue;
425+
let displayReceivedValue = deviation.CurrentValue || deviation.receivedValue;
426+
427+
// If we have JSON differences, show only the differences
428+
if (jsonDifferences && !isLicenseSkipped && !isActuallyCompliant) {
429+
displayExpectedValue = JSON.stringify(jsonDifferences, null, 2);
430+
displayReceivedValue = "See differences in Expected column";
431+
}
432+
357433
return {
358434
id: statusOverride ? `${statusOverride}-${index + 1}` : `current-${index + 1}`,
359435
cardLabelBox: {
360-
cardLabelBoxHeader: getDeviationIcon(
361-
statusOverride || deviation.Status || deviation.state
362-
),
436+
cardLabelBoxHeader: getDeviationIcon(actualStatus),
363437
},
364438
text: prettyName,
365439
subtext: description,
366-
statusColor: getDeviationColor(statusOverride || deviation.Status || deviation.state),
367-
statusText: getDeviationStatusText(statusOverride || deviation.Status || deviation.state),
440+
statusColor: isLicenseSkipped ? "text.secondary" : getDeviationColor(actualStatus),
441+
statusText: actualStatusText,
368442
standardName: deviation.standardName, // Store the original standardName for action handlers
369443
receivedValue: deviation.receivedValue, // Store the original receivedValue for action handlers
370444
expectedValue: deviation.expectedValue, // Store the original expectedValue for action handlers
371445
originalDeviation: deviation, // Store the complete original deviation object for reference
446+
isLicenseSkipped: isLicenseSkipped, // Flag for filtering and disabling actions
447+
isActuallyCompliant: isActuallyCompliant, // Flag to move to compliant section
372448
children: (
373449
<Stack spacing={2} sx={{ p: 2 }}>
374450
{description && description !== "No description available" && (
@@ -377,49 +453,65 @@ const ManageDriftPage = () => {
377453
</Typography>
378454
)}
379455

380-
{(deviation.expectedValue && deviation.expectedValue !== "Compliant with template") ||
381-
deviation.receivedValue ? (
456+
{isLicenseSkipped && (
457+
<Box
458+
sx={{
459+
p: 1.5,
460+
bgcolor: "warning.lighter",
461+
borderRadius: 1,
462+
border: "1px solid",
463+
borderColor: "warning.main",
464+
}}
465+
>
466+
<Typography variant="body2" color="warning.dark" sx={{ fontWeight: 600 }}>
467+
⚠️ This standard was skipped because the required license is not available for
468+
this tenant.
469+
</Typography>
470+
</Box>
471+
)}
472+
473+
{(displayExpectedValue && displayExpectedValue !== "Compliant with template") ||
474+
displayReceivedValue ? (
382475
<Box sx={{ display: "flex", gap: 2, flexDirection: { xs: "column", md: "row" } }}>
383-
{deviation.expectedValue &&
384-
deviation.expectedValue !== "Compliant with template" && (
385-
<Box sx={{ flex: 1, minWidth: 0 }}>
476+
{displayExpectedValue && displayExpectedValue !== "Compliant with template" && (
477+
<Box sx={{ flex: 1, minWidth: 0 }}>
478+
<Typography
479+
variant="caption"
480+
sx={{
481+
fontWeight: 600,
482+
color: "text.secondary",
483+
textTransform: "uppercase",
484+
letterSpacing: 0.5,
485+
}}
486+
>
487+
{jsonDifferences ? "Differences" : "Expected"}
488+
</Typography>
489+
<Box
490+
sx={{
491+
mt: 0.5,
492+
p: 1.5,
493+
bgcolor: isActuallyCompliant ? "success.lighter" : "action.hover",
494+
borderRadius: 1,
495+
border: "1px solid",
496+
borderColor: isActuallyCompliant ? "success.main" : "divider",
497+
}}
498+
>
386499
<Typography
387-
variant="caption"
500+
variant="body2"
388501
sx={{
389-
fontWeight: 600,
390-
color: "text.secondary",
391-
textTransform: "uppercase",
392-
letterSpacing: 0.5,
502+
fontFamily: "monospace",
503+
fontSize: "0.8125rem",
504+
whiteSpace: "pre-wrap",
505+
wordBreak: "break-word",
393506
}}
394507
>
395-
Expected
508+
{displayExpectedValue}
396509
</Typography>
397-
<Box
398-
sx={{
399-
mt: 0.5,
400-
p: 1.5,
401-
bgcolor: "action.hover",
402-
borderRadius: 1,
403-
border: "1px solid",
404-
borderColor: "divider",
405-
}}
406-
>
407-
<Typography
408-
variant="body2"
409-
sx={{
410-
fontFamily: "monospace",
411-
fontSize: "0.8125rem",
412-
whiteSpace: "pre-wrap",
413-
wordBreak: "break-word",
414-
}}
415-
>
416-
{deviation.expectedValue}
417-
</Typography>
418-
</Box>
419510
</Box>
420-
)}
511+
</Box>
512+
)}
421513

422-
{deviation.receivedValue && (
514+
{displayReceivedValue && !jsonDifferences && (
423515
<Box sx={{ flex: 1, minWidth: 0 }}>
424516
<Typography
425517
variant="caption"
@@ -436,10 +528,10 @@ const ManageDriftPage = () => {
436528
sx={{
437529
mt: 0.5,
438530
p: 1.5,
439-
bgcolor: "action.hover",
531+
bgcolor: isActuallyCompliant ? "success.lighter" : "action.hover",
440532
borderRadius: 1,
441533
border: "1px solid",
442-
borderColor: "divider",
534+
borderColor: isActuallyCompliant ? "success.main" : "divider",
443535
}}
444536
>
445537
<Typography
@@ -451,7 +543,7 @@ const ManageDriftPage = () => {
451543
wordBreak: "break-word",
452544
}}
453545
>
454-
{formatPolicyValue(deviation.receivedValue)}
546+
{displayReceivedValue}
455547
</Typography>
456548
</Box>
457549
</Box>
@@ -524,6 +616,16 @@ const ManageDriftPage = () => {
524616
);
525617
const alignedStandardItems = createDeviationItems(processedDriftData.alignedStandards, "aligned");
526618

619+
// Separate items by their actual status
620+
const licenseSkippedItems = deviationItems.filter((item) => item.isLicenseSkipped);
621+
const compliantFromDeviations = deviationItems.filter((item) => item.isActuallyCompliant);
622+
const actualDeviationItems = deviationItems.filter(
623+
(item) => !item.isLicenseSkipped && !item.isActuallyCompliant
624+
);
625+
626+
// Combine compliant items from both sources
627+
const allAlignedItems = [...alignedStandardItems, ...compliantFromDeviations];
628+
527629
const handleMenuClick = (event, itemId) => {
528630
setAnchorEl((prev) => ({ ...prev, [itemId]: event.currentTarget }));
529631
};
@@ -776,7 +878,7 @@ const ManageDriftPage = () => {
776878
}, [templateId]);
777879

778880
// Add action buttons to each deviation item
779-
const deviationItemsWithActions = deviationItems.map((item) => {
881+
const deviationItemsWithActions = actualDeviationItems.map((item) => {
780882
// Check if this is a template that supports delete action
781883
const supportsDelete =
782884
(item.standardName?.includes("ConditionalAccessTemplate") ||
@@ -1011,7 +1113,8 @@ const ManageDriftPage = () => {
10111113
const filteredAcceptedItems = applyFilters(acceptedDeviationItemsWithActions);
10121114
const filteredCustomerSpecificItems = applyFilters(customerSpecificDeviationItemsWithActions);
10131115
const filteredDeniedItems = applyFilters(deniedDeviationItemsWithActions);
1014-
const filteredAlignedItems = applyFilters(alignedStandardItems);
1116+
const filteredAlignedItems = applyFilters(allAlignedItems);
1117+
const filteredLicenseSkippedItems = applyFilters(licenseSkippedItems);
10151118

10161119
// Helper function to render items grouped by category when category sort is active
10171120
const renderItemsByCategory = (items) => {
@@ -1255,6 +1358,12 @@ const ManageDriftPage = () => {
12551358
variant="outlined"
12561359
/>
12571360
</Box>
1361+
<Box display="flex" justifyContent="space-between" alignItems="center">
1362+
<Typography variant="body2" color="text.secondary">
1363+
Skipped (No License)
1364+
</Typography>
1365+
<Chip label={licenseSkippedItems.length} size="small" variant="outlined" />
1366+
</Box>
12581367
<Divider />
12591368
<Box display="flex" justifyContent="space-between" alignItems="center">
12601369
<Typography variant="body2" fontWeight={600}>
@@ -1458,6 +1567,25 @@ const ManageDriftPage = () => {
14581567
/>
14591568
</Box>
14601569
)}
1570+
1571+
{/* License Skipped Section - Always at the end */}
1572+
{filteredLicenseSkippedItems.length > 0 && (
1573+
<Box>
1574+
<Typography variant="h6" sx={{ mb: 2 }}>
1575+
Skipped - No License Available
1576+
</Typography>
1577+
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
1578+
These standards were skipped because the required licenses are not available
1579+
for this tenant.
1580+
</Typography>
1581+
<CippBannerListCard
1582+
items={filteredLicenseSkippedItems}
1583+
isCollapsible={true}
1584+
layout={"single"}
1585+
isFetching={driftApi.isFetching}
1586+
/>
1587+
</Box>
1588+
)}
14611589
</Stack>
14621590
</Grid>
14631591
</Grid>

0 commit comments

Comments
 (0)