diff --git a/.github/workflows/Node_Project_Check.yml b/.github/workflows/Node_Project_Check.yml
index b135c589ea7b..c83519a6b16f 100644
--- a/.github/workflows/Node_Project_Check.yml
+++ b/.github/workflows/Node_Project_Check.yml
@@ -1,31 +1,37 @@
----
name: NodeJS Project Check
+
on:
pull_request:
branches:
- main
- dev
+
concurrency:
- group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.ref }}
- cancel-in-progress: false
+ group: NodeJS Project Check ${{ github.event_name == 'pull_request' && format('PR:{0}', github.event.pull_request.number) || format('Branch:{0}', github.ref_name) }}
+ cancel-in-progress: true
+
jobs:
install-build:
- if: github.repository_owner == 'KelvinTegelaar'
- name: NPM Install and Build
+ #if: github.repository_owner == 'KelvinTegelaar'
+ name: Install, Build, and Test
runs-on: ubuntu-latest
+
strategy:
matrix:
- node-version: [16.x]
- os: [ubuntu-latest]
+ node-version: [22.x]
+
steps:
- - uses: actions/checkout@v2
- - name: Use Node.js ${{ matrix.node-version }}
- uses: actions/setup-node@v2
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- - name: Install and Build Test
- run: |
- npm install --legacy-peer-deps
- npm run build
- env:
- CI: true
+ cache: npm
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build
+ run: npm run build
diff --git a/.github/workflows/label_sponsor_requests.yml b/.github/workflows/label_sponsor_requests.yml
index 479cad06c728..b974cf66776f 100644
--- a/.github/workflows/label_sponsor_requests.yml
+++ b/.github/workflows/label_sponsor_requests.yml
@@ -1,4 +1,3 @@
----
name: Label Issues
on:
issues:
@@ -14,4 +13,6 @@ jobs:
- name: Sponsor Labels
uses: JasonEtco/is-sponsor-label-action@v1.2.0
with:
- label: 'Sponsor Request'
+ label: 'Sponsor Priority'
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/src/api/ApiCall.jsx b/src/api/ApiCall.jsx
index e4d9a366908c..a2d7d0396ce1 100644
--- a/src/api/ApiCall.jsx
+++ b/src/api/ApiCall.jsx
@@ -50,7 +50,7 @@ export function ApiGetCall(props) {
title: `${
error.config?.params?.tenantFilter ? error.config?.params?.tenantFilter : ""
} Error`,
- })
+ }),
);
}
return returnRetry;
@@ -211,7 +211,7 @@ export function ApiPostCall({ relatedQueryKeys, onResult }) {
if (!query.queryKey || !query.queryKey[0]) return false;
const queryKeyStr = String(query.queryKey[0]);
const matches = wildcardPatterns.some((pattern) =>
- queryKeyStr.startsWith(pattern)
+ queryKeyStr.startsWith(pattern),
);
// Debug logging for each query check
@@ -220,7 +220,7 @@ export function ApiPostCall({ relatedQueryKeys, onResult }) {
queryKey: query.queryKey,
queryKeyStr,
matchedPattern: wildcardPatterns.find((pattern) =>
- queryKeyStr.startsWith(pattern)
+ queryKeyStr.startsWith(pattern),
),
});
}
@@ -252,8 +252,9 @@ export function ApiGetCallWithPagination({
waiting = true,
}) {
const dispatch = useDispatch();
+ const queryClient = useQueryClient();
const MAX_RETRIES = retry;
- const HTTP_STATUS_TO_NOT_RETRY = [401, 403, 404];
+ const HTTP_STATUS_TO_NOT_RETRY = [302, 401, 403, 404, 500];
const retryFn = (failureCount, error) => {
let returnRetry = true;
@@ -261,6 +262,12 @@ export function ApiGetCallWithPagination({
returnRetry = false;
}
if (isAxiosError(error) && HTTP_STATUS_TO_NOT_RETRY.includes(error.response?.status ?? 0)) {
+ if (
+ error.response?.status === 302 &&
+ error.response?.headers.get("location").includes("/.auth/login/aad")
+ ) {
+ queryClient.invalidateQueries({ queryKey: ["authmecipp"] });
+ }
returnRetry = false;
}
@@ -270,7 +277,7 @@ export function ApiGetCallWithPagination({
message: getCippError(error),
title: "Error",
toastError: error,
- })
+ }),
);
}
return returnRetry;
diff --git a/src/components/CippComponents/CippAddTestReportDrawer.jsx b/src/components/CippComponents/CippAddTestReportDrawer.jsx
index d7ee1646656f..6ea7d84ea0bb 100644
--- a/src/components/CippComponents/CippAddTestReportDrawer.jsx
+++ b/src/components/CippComponents/CippAddTestReportDrawer.jsx
@@ -43,13 +43,13 @@ export const CippAddTestReportDrawer = ({ buttonText = "Create custom report" })
const createReport = ApiPostCall({
urlFromData: true,
- relatedQueryKeys: ["ListTestReports"],
+ relatedQueryKeys: "ListTestReports",
});
// Fetch available tests for the form
const availableTestsApi = ApiGetCall({
url: "/api/ListAvailableTests",
- queryKey: ["ListAvailableTests"],
+ queryKey: "ListAvailableTests",
});
const availableTests = availableTestsApi.data || { IdentityTests: [], DevicesTests: [] };
diff --git a/src/components/CippWizard/CippSAMDeploy.jsx b/src/components/CippWizard/CippSAMDeploy.jsx
index 2cb619fef7aa..d38d0f66ddf2 100644
--- a/src/components/CippWizard/CippSAMDeploy.jsx
+++ b/src/components/CippWizard/CippSAMDeploy.jsx
@@ -92,7 +92,10 @@ export const CippSAMDeploy = (props) => {
here
-
(Temporary) Global Administrator permissions for the CIPP Service Account
+
+ An account with at minimum: Application Administrator
+ User Administrator
+
Multi-factor authentication enabled for the CIPP Service Account, with no trusted
locations or other exclusions.
diff --git a/src/data/alerts.json b/src/data/alerts.json
index dd6d8b6cc065..22a8f06ca072 100644
--- a/src/data/alerts.json
+++ b/src/data/alerts.json
@@ -331,5 +331,39 @@
"label": "Alert on quarantine release requests",
"recommendedRunInterval": "30m",
"description": "Monitors for user requests to release quarantined messages and provides a CIPP-native alternative to the external email forwarding method. This helps MSPs maintain secure configurations while getting timely notifications about quarantine activity. Links to the tenant's quarantine page are provided in alerts."
+ },
+ {
+ "name": "SecureScore",
+ "label": "Alert on a low Secure Score",
+ "recommendedRunInterval": "1d",
+ "requiresInput": true,
+ "multipleInput": true,
+ "inputs": [
+ {
+ "inputType": "autoComplete",
+ "inputLabel": "Threshold type absolute number or percent",
+ "inputName": "ThresholdType",
+ "creatable": false,
+ "multiple": false,
+ "options": [
+ {
+ "label": "Percent",
+ "value": "percent"
+ },
+ {
+ "label": "Absolute",
+ "value": "absolute"
+ }
+ ],
+ "required": true
+ },
+ {
+ "inputType": "number",
+ "inputLabel": "Threshold Value (below this will trigger the alert)",
+ "inputName": "InputValue",
+ "required": true
+ }
+ ],
+ "description": "Monitors Secure Score and alerts when it falls below the specified threshold (absolute or percent value). Helps identify security gaps and areas for improvement."
}
]
diff --git a/src/layouts/config.js b/src/layouts/config.js
index d735b015c94c..82e68a157e9c 100644
--- a/src/layouts/config.js
+++ b/src/layouts/config.js
@@ -719,7 +719,12 @@ export const nativeMenuItems = [
{
title: "Mailbox Permissions",
path: "/email/reports/mailbox-permissions",
- permissions: ["Exchange.Mailbox.Read"],
+ permissions: ["Exchange.Mailbox.*"],
+ },
+ {
+ title: "Calendar Permissions",
+ path: "/email/reports/calendar-permissions",
+ permissions: ["Exchange.Mailbox.*"],
},
{
title: "Anti-Phishing Filters",
diff --git a/src/pages/cipp/logs/index.js b/src/pages/cipp/logs/index.js
index 18f8ad68174c..41eac2225d46 100644
--- a/src/pages/cipp/logs/index.js
+++ b/src/pages/cipp/logs/index.js
@@ -37,7 +37,7 @@ const pageTitle = "Logbook Results";
const actions = [
{
label: "View Log Entry",
- link: "/cipp/logs/logentry?logentry=[RowKey]",
+ link: "/cipp/logs/logentry?logentry=[RowKey]&dateFilter=[DateFilter]",
icon: ,
color: "primary",
},
@@ -100,14 +100,14 @@ const Page = () => {
setStartDate(
data.startDate
? new Date(data.startDate * 1000).toISOString().split("T")[0].replace(/-/g, "")
- : null
+ : null,
);
// Format end date if available
setEndDate(
data.endDate
? new Date(data.endDate * 1000).toISOString().split("T")[0].replace(/-/g, "")
- : null
+ : null,
);
// Set username filter if available
@@ -117,7 +117,7 @@ const Page = () => {
setSeverity(
data.severity && data.severity.length > 0
? data.severity.map((item) => item.value).join(",")
- : null
+ : null,
);
// Close the accordion after applying filters
@@ -157,13 +157,13 @@ const Page = () => {
<>
{startDate
? new Date(
- startDate.replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3") + "T00:00:00"
+ startDate.replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3") + "T00:00:00",
).toLocaleDateString()
: new Date().toLocaleDateString()}
{startDate && endDate ? " - " : ""}
{endDate
? new Date(
- endDate.replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3") + "T00:00:00"
+ endDate.replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3") + "T00:00:00",
).toLocaleDateString()
: ""}
>
diff --git a/src/pages/cipp/logs/logentry.js b/src/pages/cipp/logs/logentry.js
index 2dbb4a23a5a7..175dcc915aba 100644
--- a/src/pages/cipp/logs/logentry.js
+++ b/src/pages/cipp/logs/logentry.js
@@ -11,12 +11,13 @@ import { getCippTranslation } from "../../../utils/get-cipp-translation";
const Page = () => {
const router = useRouter();
- const { logentry } = router.query;
+ const { logentry, dateFilter } = router.query;
const logRequest = ApiGetCall({
url: `/api/Listlogs`,
data: {
logentryid: logentry,
+ dateFilter: dateFilter,
},
queryKey: `GetLogEntry-${logentry}`,
waiting: !!logentry,
@@ -44,12 +45,12 @@ const Page = () => {
logData.Severity === "CRITICAL"
? "error"
: logData.Severity === "Error"
- ? "error"
- : logData.Severity === "Warn"
- ? "warning"
- : logData.Severity === "Info"
- ? "info"
- : "default"
+ ? "error"
+ : logData.Severity === "Warn"
+ ? "warning"
+ : logData.Severity === "Info"
+ ? "info"
+ : "default"
}
variant="filled"
/>
diff --git a/src/pages/email/reports/calendar-permissions/index.js b/src/pages/email/reports/calendar-permissions/index.js
new file mode 100644
index 000000000000..d945e29b8c10
--- /dev/null
+++ b/src/pages/email/reports/calendar-permissions/index.js
@@ -0,0 +1,117 @@
+import { Layout as DashboardLayout } from "/src/layouts/index.js";
+import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx";
+import { useState } from "react";
+import {
+ Button,
+ FormControlLabel,
+ Switch,
+ Alert,
+ SvgIcon,
+ IconButton,
+ Tooltip,
+} from "@mui/material";
+import { useSettings } from "../../../../hooks/use-settings";
+import { Stack } from "@mui/system";
+import { Sync, Info } from "@mui/icons-material";
+import { useDialog } from "../../../../hooks/use-dialog";
+import { CippApiDialog } from "../../../../components/CippComponents/CippApiDialog";
+
+const Page = () => {
+ const [byUser, setByUser] = useState(true);
+ const currentTenant = useSettings().currentTenant;
+ const syncDialog = useDialog();
+
+ const isAllTenants = currentTenant === "AllTenants";
+
+ const columns = byUser
+ ? [
+ ...(isAllTenants ? ["Tenant"] : []),
+ "User",
+ "UserMailboxType",
+ "Permissions",
+ "MailboxCacheTimestamp",
+ "PermissionCacheTimestamp",
+ ]
+ : [
+ ...(isAllTenants ? ["Tenant"] : []),
+ "CalendarUPN",
+ "CalendarDisplayName",
+ "CalendarType",
+ "Permissions",
+ "MailboxCacheTimestamp",
+ "PermissionCacheTimestamp",
+ ];
+
+ // Compute apiData based on byUser directly (no useState needed)
+ const apiData = {
+ UseReportDB: true,
+ ByUser: byUser,
+ };
+
+ const pageActions = [
+
+
+
+
+
+
+
+ setByUser(e.target.checked)} color="primary" />
+ }
+ label="Group by User"
+ labelPlacement="start"
+ />
+ ,
+ ];
+
+ return (
+ <>
+ {currentTenant && currentTenant !== "" ? (
+
+ ) : (
+ Please select a tenant to view calendar permissions.
+ )}
+
+ >
+ );
+};
+
+Page.getLayout = (page) => {page};
+
+export default Page;
diff --git a/src/pages/tenant/administration/alert-configuration/alert.jsx b/src/pages/tenant/administration/alert-configuration/alert.jsx
index 91d671113f6b..a156d6086093 100644
--- a/src/pages/tenant/administration/alert-configuration/alert.jsx
+++ b/src/pages/tenant/administration/alert-configuration/alert.jsx
@@ -958,6 +958,16 @@ const AlertWizard = () => {
name={commandValue.value?.inputName}
formControl={formControl}
label={commandValue.value?.inputLabel}
+ required={commandValue.value?.required || false}
+ {...(commandValue.value?.inputType === 'autoComplete'
+ ? {
+ options: commandValue.value?.options,
+ creatable: commandValue.value?.creatable || true,
+ multiple: commandValue.value?.multiple || true,
+ }
+ : {}
+ )
+ }
/>
)}
{commandValue?.value?.multipleInput &&
@@ -974,6 +984,16 @@ const AlertWizard = () => {
name={input.inputName}
formControl={formControl}
label={input.inputLabel}
+ required={input.required || false}
+ {...(input.inputType === 'autoComplete'
+ ? {
+ options: input.options,
+ creatable: input.creatable ?? true,
+ multiple: input.multiple ?? true,
+ }
+ : {}
+ )
+ }
/>
diff --git a/src/pages/tenant/manage/applied-standards.js b/src/pages/tenant/manage/applied-standards.js
index 5b6539767fce..ef11f5e33e34 100644
--- a/src/pages/tenant/manage/applied-standards.js
+++ b/src/pages/tenant/manage/applied-standards.js
@@ -35,6 +35,7 @@ import {
NotificationImportant,
Construction,
Schedule,
+ Check,
} from "@mui/icons-material";
import standards from "/src/data/standards.json";
import { CippApiDialog } from "../../../components/CippComponents/CippApiDialog";
@@ -91,7 +92,7 @@ const Page = () => {
// Selected template object (safe lookup)
const selectedTemplate = useMemo(
() => templates.find((t) => t.GUID === templateId),
- [templates, templateId]
+ [templates, templateId],
);
// Run the report once
@@ -123,7 +124,7 @@ const Page = () => {
useEffect(() => {
if (templateId && templateDetails.isSuccess && templateDetails.data) {
const selectedTemplate = templateDetails.data.find(
- (template) => template.GUID === templateId
+ (template) => template.GUID === templateId,
);
if (selectedTemplate && comparisonApi.isSuccess && comparisonApi.data) {
@@ -157,30 +158,55 @@ const Page = () => {
const itemTemplateId = expandedTemplate.GUID;
const standardId = `standards.IntuneTemplate.${itemTemplateId}`;
const standardInfo = standards.find(
- (s) => s.name === `standards.IntuneTemplate`
+ (s) => s.name === `standards.IntuneTemplate`,
);
// Find the tenant's value for this specific template
const currentTenantStandard = currentTenantData.find(
- (s) => s.standardId === standardId
+ (s) => s.standardId === standardId,
);
// Get the standard object and its value from the tenant object
const standardObject = currentTenantObj?.[standardId];
const directStandardValue = standardObject?.Value;
- // Determine compliance status
+ // Determine compliance status - match main logic
let isCompliant = false;
- // For IntuneTemplate, the value is true if compliant, or an object with comparison data if not compliant
- if (directStandardValue === true) {
- isCompliant = true;
- } else if (
- directStandardValue !== undefined &&
- typeof directStandardValue !== "object"
+ // FIRST: Check if CurrentValue and ExpectedValue exist and match
+ if (
+ standardObject?.CurrentValue !== undefined &&
+ standardObject?.ExpectedValue !== undefined
) {
+ const sortedCurrent =
+ typeof standardObject.CurrentValue === "object" &&
+ standardObject.CurrentValue !== null
+ ? Object.keys(standardObject.CurrentValue)
+ .sort()
+ .reduce((obj, key) => {
+ obj[key] = standardObject.CurrentValue[key];
+ return obj;
+ }, {})
+ : standardObject.CurrentValue;
+ const sortedExpected =
+ typeof standardObject.ExpectedValue === "object" &&
+ standardObject.ExpectedValue !== null
+ ? Object.keys(standardObject.ExpectedValue)
+ .sort()
+ .reduce((obj, key) => {
+ obj[key] = standardObject.ExpectedValue[key];
+ return obj;
+ }, {})
+ : standardObject.ExpectedValue;
+ isCompliant =
+ JSON.stringify(sortedCurrent) === JSON.stringify(sortedExpected);
+ }
+ // SECOND: Check if Value is explicitly true
+ else if (directStandardValue === true) {
isCompliant = true;
- } else if (currentTenantStandard) {
+ }
+ // THIRD: Fall back to currentTenantStandard
+ else if (currentTenantStandard) {
isCompliant = currentTenantStandard.value === true;
}
@@ -220,8 +246,8 @@ const Page = () => {
complianceStatus: isOverridden
? "Overridden"
: isCompliant
- ? "Compliant"
- : "Non-Compliant",
+ ? "Compliant"
+ : "Non-Compliant",
isOverridden,
overridingTemplateId: isOverridden ? tenantTemplateId : null,
overridingTemplateName,
@@ -258,12 +284,12 @@ const Page = () => {
if (itemTemplateId) {
const standardId = `standards.IntuneTemplate.${itemTemplateId}`;
const standardInfo = standards.find(
- (s) => s.name === `standards.IntuneTemplate`
+ (s) => s.name === `standards.IntuneTemplate`,
);
// Find the tenant's value for this specific template
const currentTenantStandard = currentTenantData.find(
- (s) => s.standardId === standardId
+ (s) => s.standardId === standardId,
);
// Get the standard object and its value from the tenant object
@@ -320,8 +346,8 @@ const Page = () => {
complianceStatus: isOverridden
? "Overridden"
: isCompliant
- ? "Compliant"
- : "Non-Compliant",
+ ? "Compliant"
+ : "Non-Compliant",
isOverridden,
overridingTemplateId: isOverridden ? tenantTemplateId : null,
overridingTemplateName,
@@ -374,12 +400,12 @@ const Page = () => {
const itemTemplateId = expandedTemplate.GUID;
const standardId = `standards.ConditionalAccessTemplate.${itemTemplateId}`;
const standardInfo = standards.find(
- (s) => s.name === `standards.ConditionalAccessTemplate`
+ (s) => s.name === `standards.ConditionalAccessTemplate`,
);
// Find the tenant's value for this specific template
const currentTenantStandard = currentTenantData.find(
- (s) => s.standardId === standardId
+ (s) => s.standardId === standardId,
);
const standardObject = currentTenantObj?.[standardId];
const directStandardValue = standardObject?.Value;
@@ -390,10 +416,40 @@ const Page = () => {
: null;
let isCompliant = false;
- // For ConditionalAccessTemplate, the value is true if compliant, or an object with comparison data if not compliant
- if (directStandardValue === true) {
+ // FIRST: Check if CurrentValue and ExpectedValue exist and match
+ if (
+ standardObject?.CurrentValue !== undefined &&
+ standardObject?.ExpectedValue !== undefined
+ ) {
+ const sortedCurrent =
+ typeof standardObject.CurrentValue === "object" &&
+ standardObject.CurrentValue !== null
+ ? Object.keys(standardObject.CurrentValue)
+ .sort()
+ .reduce((obj, key) => {
+ obj[key] = standardObject.CurrentValue[key];
+ return obj;
+ }, {})
+ : standardObject.CurrentValue;
+ const sortedExpected =
+ typeof standardObject.ExpectedValue === "object" &&
+ standardObject.ExpectedValue !== null
+ ? Object.keys(standardObject.ExpectedValue)
+ .sort()
+ .reduce((obj, key) => {
+ obj[key] = standardObject.ExpectedValue[key];
+ return obj;
+ }, {})
+ : standardObject.ExpectedValue;
+ isCompliant =
+ JSON.stringify(sortedCurrent) === JSON.stringify(sortedExpected);
+ }
+ // SECOND: Check if Value is explicitly true
+ else if (directStandardValue === true) {
isCompliant = true;
- } else {
+ }
+ // THIRD: Otherwise not compliant
+ else {
isCompliant = false;
}
@@ -423,8 +479,8 @@ const Page = () => {
complianceStatus: isOverridden
? "Overridden"
: isCompliant
- ? "Compliant"
- : "Non-Compliant",
+ ? "Compliant"
+ : "Non-Compliant",
complianceDetails:
standardInfo?.docsDescription || standardInfo?.helpText || "",
standardDescription: standardInfo?.helpText || "",
@@ -461,12 +517,12 @@ const Page = () => {
if (itemTemplateId) {
const standardId = `standards.ConditionalAccessTemplate.${itemTemplateId}`;
const standardInfo = standards.find(
- (s) => s.name === `standards.ConditionalAccessTemplate`
+ (s) => s.name === `standards.ConditionalAccessTemplate`,
);
// Find the tenant's value for this specific template
const currentTenantStandard = currentTenantData.find(
- (s) => s.standardId === standardId
+ (s) => s.standardId === standardId,
);
const standardObject = currentTenantObj?.[standardId];
const directStandardValue = standardObject?.Value;
@@ -509,8 +565,8 @@ const Page = () => {
complianceStatus: isOverridden
? "Overridden"
: isCompliant
- ? "Compliant"
- : "Non-Compliant",
+ ? "Compliant"
+ : "Non-Compliant",
complianceDetails:
standardInfo?.docsDescription || standardInfo?.helpText || "",
standardDescription: standardInfo?.helpText || "",
@@ -553,7 +609,7 @@ const Page = () => {
// Find the tenant's value for this template
const currentTenantStandard = currentTenantData.find(
- (s) => s.standardId === standardId
+ (s) => s.standardId === standardId,
);
const standardObject = currentTenantObj?.[standardId];
const directStandardValue = standardObject?.Value;
@@ -633,8 +689,8 @@ const Page = () => {
complianceStatus: isOverridden
? "Overridden"
: isCompliant
- ? "Compliant"
- : "Non-Compliant",
+ ? "Compliant"
+ : "Non-Compliant",
complianceDetails: standardInfo?.docsDescription || standardInfo?.helpText || "",
standardDescription: standardInfo?.helpText || "",
standardImpact: standardInfo?.impact || "Medium Impact",
@@ -674,44 +730,119 @@ const Page = () => {
actions.filter(
(action) =>
action?.value.toLowerCase() === "report" ||
- action?.value.toLowerCase() === "remediate"
+ action?.value.toLowerCase() === "remediate",
).length > 0;
// Find the tenant's value for this standard
const currentTenantStandard = currentTenantData.find(
- (s) => s.standardId === standardId
+ (s) => s.standardId === standardId,
);
// Check if the standard is directly in the tenant object (like "standards.AuditLog": {...})
const standardIdWithoutPrefix = standardId.replace("standards.", "");
const standardObject = currentTenantObj?.[standardId];
+ console.log(
+ "standardId:",
+ standardId,
+ "includes IntuneTag:",
+ standardId.includes("IntuneTag"),
+ );
+
+ // Debug logging for Intune tags
+ if (standardId.includes("IntuneTag") || standardId.includes("intuneTag")) {
+ console.log(`[${standardId}] standardObject:`, {
+ standardObject,
+ hasCurrentValue: standardObject?.CurrentValue !== undefined,
+ hasExpectedValue: standardObject?.ExpectedValue !== undefined,
+ Value: standardObject?.Value,
+ CurrentValue: standardObject?.CurrentValue,
+ ExpectedValue: standardObject?.ExpectedValue,
+ });
+ }
+
// Extract the actual value from the standard object (new data structure includes .Value property)
const directStandardValue = standardObject?.Value;
- // Determine compliance - use backend's logic: Value === true OR CurrentValue === ExpectedValue
+ // Determine compliance - prioritize Value field, then CurrentValue/ExpectedValue comparison
let isCompliant = false;
let reportingDisabled = !reportingEnabled;
- if (directStandardValue === true) {
- // Boolean true means compliant
+ // Helper function to compare values, handling arrays with order-independent comparison
+ const compareValues = (val1, val2) => {
+ // If both are arrays, compare as sets (order-independent)
+ if (Array.isArray(val1) && Array.isArray(val2)) {
+ if (val1.length !== val2.length) return false;
+ // Sort both arrays by their JSON representation for consistent comparison
+ const sorted1 = [...val1].sort((a, b) =>
+ JSON.stringify(a).localeCompare(JSON.stringify(b)),
+ );
+ const sorted2 = [...val2].sort((a, b) =>
+ JSON.stringify(a).localeCompare(JSON.stringify(b)),
+ );
+ return JSON.stringify(sorted1) === JSON.stringify(sorted2);
+ }
+ // For objects, sort keys to ensure consistent comparison
+ if (
+ typeof val1 === "object" &&
+ val1 !== null &&
+ typeof val2 === "object" &&
+ val2 !== null
+ ) {
+ const sortedVal1 = Object.keys(val1)
+ .sort()
+ .reduce((obj, key) => {
+ obj[key] = val1[key];
+ return obj;
+ }, {});
+ const sortedVal2 = Object.keys(val2)
+ .sort()
+ .reduce((obj, key) => {
+ obj[key] = val2[key];
+ return obj;
+ }, {});
+ return JSON.stringify(sortedVal1) === JSON.stringify(sortedVal2);
+ }
+ // Otherwise use standard JSON comparison
+ return JSON.stringify(val1) === JSON.stringify(val2);
+ };
+
+ // FIRST: Check if CurrentValue and ExpectedValue exist and match (most reliable)
+ if (
+ standardObject?.CurrentValue !== undefined &&
+ standardObject?.ExpectedValue !== undefined
+ ) {
+ isCompliant = compareValues(
+ standardObject.CurrentValue,
+ standardObject.ExpectedValue,
+ );
+ // Debug logging for Intune tags
+ if (standardId.includes("IntuneTag") || standardId.includes("intuneTag")) {
+ console.log(`[${standardId}] Comparing CurrentValue vs ExpectedValue:`, {
+ CurrentValue: standardObject.CurrentValue,
+ ExpectedValue: standardObject.ExpectedValue,
+ isCompliant,
+ currentJSON: JSON.stringify(standardObject.CurrentValue),
+ expectedJSON: JSON.stringify(standardObject.ExpectedValue),
+ areEqual:
+ JSON.stringify(standardObject.CurrentValue) ===
+ JSON.stringify(standardObject.ExpectedValue),
+ });
+ }
+ }
+ // SECOND: Check if Value is explicitly true (compliant) or false (non-compliant)
+ else if (directStandardValue === true) {
isCompliant = true;
- } else if (standardObject?.CurrentValue && standardObject?.ExpectedValue) {
- // Compare CurrentValue and ExpectedValue (backend's comparison logic)
- isCompliant =
- JSON.stringify(standardObject.CurrentValue) ===
- JSON.stringify(standardObject.ExpectedValue);
- } else if (standardObject?.CurrentValue && standardObject?.ExpectedValue) {
- // Compare CurrentValue and ExpectedValue (backend's comparison logic)
- isCompliant =
- JSON.stringify(standardObject.CurrentValue) ===
- JSON.stringify(standardObject.ExpectedValue);
- } else if (directStandardValue !== undefined) {
- // For non-boolean values, use strict equality
+ } else if (directStandardValue === false) {
+ isCompliant = false;
+ }
+ // THIRD: For other non-boolean, non-null values, use strict equality with template settings
+ else if (directStandardValue !== undefined && directStandardValue !== null) {
isCompliant =
JSON.stringify(directStandardValue) === JSON.stringify(standardSettings);
- } else if (currentTenantStandard) {
- // Fall back to the previous logic if the standard is not directly in the tenant object
+ }
+ // FOURTH: Fall back to the previous logic if the standard is not directly in the tenant object
+ else if (currentTenantStandard) {
if (typeof standardSettings === "boolean" && standardSettings === true) {
isCompliant = currentTenantStandard.value === true;
} else {
@@ -725,8 +856,8 @@ const Page = () => {
const complianceStatus = reportingDisabled
? "Reporting Disabled"
: isCompliant
- ? "Compliant"
- : "Non-Compliant";
+ ? "Compliant"
+ : "Non-Compliant";
// Check if this standard is overridden by another template
const tenantTemplateId = standardObject?.TemplateId;
@@ -913,14 +1044,14 @@ const Page = () => {
const compliancePercentage =
allCount > 0
? Math.round(
- (compliantCount / (allCount - reportingDisabledCount - overriddenCount || 1)) * 100
+ (compliantCount / (allCount - reportingDisabledCount - overriddenCount || 1)) * 100,
)
: 0;
const missingLicensePercentage =
allCount > 0
? Math.round(
- (missingLicenseCount / (allCount - reportingDisabledCount - overriddenCount || 1)) * 100
+ (missingLicenseCount / (allCount - reportingDisabledCount - overriddenCount || 1)) * 100,
)
: 0;
@@ -1102,7 +1233,7 @@ const Page = () => {
query: query,
},
undefined,
- { shallow: true }
+ { shallow: true },
);
}}
sx={{ width: 300 }}
@@ -1198,8 +1329,8 @@ const Page = () => {
compliancePercentage === 100
? "success"
: compliancePercentage >= 50
- ? "warning"
- : "error"
+ ? "warning"
+ : "error"
}
/>
{
missingLicensePercentage === 0
? "success"
: missingLicensePercentage <= 25
- ? "warning"
- : "error"
+ ? "warning"
+ : "error"
}
/>
{
standard.complianceStatus === "Compliant"
? "success.main"
: standard.complianceStatus === "Overridden"
- ? "warning.main"
- : standard.complianceStatus === "Reporting Disabled"
- ? "grey.500"
- : "error.main",
+ ? "warning.main"
+ : standard.complianceStatus === "Reporting Disabled"
+ ? "grey.500"
+ : "error.main",
}}
>
{standard.complianceStatus === "Compliant" ? (
@@ -1481,78 +1612,94 @@ const Page = () => {
- {!standard.standardValue ? (
-
- This data has not yet been collected. Collect the data by pressing
- the report button on the top of the page.
-
- ) : (
+ {/* Show Expected Configuration with property-by-property breakdown */}
+ {standard.currentTenantValue?.ExpectedValue !== undefined ? (
-
-
- {standard.standardValue &&
- typeof standard.standardValue === "object" &&
- Object.keys(standard.standardValue).length > 0 ? (
- Object.entries(standard.standardValue).map(([key, value]) => (
-
+
+ Expected Configuration
+
+ {typeof standard.currentTenantValue.ExpectedValue === "object" &&
+ standard.currentTenantValue.ExpectedValue !== null ? (
+
+ {Object.entries(standard.currentTenantValue.ExpectedValue).map(
+ ([key, val]) => (
+
- {key}:
+ {key}
-
- {typeof value === "object" && value !== null
- ? value?.label || JSON.stringify(value)
- : value === true
- ? "Enabled"
- : value === false
- ? "Disabled"
- : String(value)}
-
+
+ {val !== undefined
+ ? JSON.stringify(val, null, 2)
+ : "Not set"}
+
+
- ))
- ) : (
-
- {standard.standardValue === true ? (
-
- This setting is configured correctly
-
- ) : standard.standardValue === false ? (
-
- This setting is not configured correctly
-
- ) : standard.standardValue !== undefined ? (
- typeof standard.standardValue === "object" ? (
- "No settings configured"
- ) : (
- String(standard.standardValue)
- )
- ) : (
-
- This setting is not configured, or data has not been
- collected. If you are getting this after data
- collection, the tenant might not be licensed for this
- feature
-
- )}
-
+ ),
)}
+
+ ) : (
+
+
+ {String(standard.currentTenantValue.ExpectedValue)}
+
-
+ )}
+ ) : (
+
+ This data has not yet been collected. Collect the data by pressing
+ the report button on the top of the page.
+
)}
@@ -1563,8 +1710,8 @@ const Page = () => {
standard.standardImpactColour === "info"
? "info"
: standard.standardImpactColour === "warning"
- ? "warning"
- : "error"
+ ? "warning"
+ : "error"
}
sx={{ mr: 1 }}
/>
@@ -1629,10 +1776,10 @@ const Page = () => {
standard.complianceStatus === "Compliant"
? "success.main"
: standard.complianceStatus === "Overridden"
- ? "warning.main"
- : standard.complianceStatus === "Reporting Disabled"
- ? "grey.500"
- : "error.main",
+ ? "warning.main"
+ : standard.complianceStatus === "Reporting Disabled"
+ ? "grey.500"
+ : "error.main",
borderRadius: "50%",
width: 8,
height: 8,
@@ -1652,7 +1799,7 @@ const Page = () => {
}
size="small"
label={`${new Date(
- standard.currentTenantValue.LastRefresh
+ standard.currentTenantValue.LastRefresh,
).toLocaleString()}`}
variant="outlined"
/>
@@ -1689,109 +1836,244 @@ const Page = () => {
) : standard.complianceStatus === "Compliant" ? (
<>
-
- This setting is configured correctly
-
- >
- ) : standard.currentTenantValue?.Value === false ? (
- <>
-
- This setting is not configured correctly
-
- {/* Show Current/Expected values for non-compliant standards */}
- {standard.currentTenantValue?.CurrentValue &&
- standard.currentTenantValue?.ExpectedValue && (
-
-
-
- Expected
-
-
+ {/* Show Current value property-by-property for compliant standards */}
+ {standard.currentTenantValue?.CurrentValue !== undefined ? (
+ typeof standard.currentTenantValue.CurrentValue ===
+ "object" &&
+ standard.currentTenantValue.CurrentValue !== null ? (
+
+
+ Current Configuration
+
+ {Object.entries(
+ standard.currentTenantValue.CurrentValue,
+ ).map(([key, val]) => (
+
- {JSON.stringify(
- standard.currentTenantValue.ExpectedValue,
- null,
- 2
- )}
+ {key}
+
+
+
+
+
+ {val !== undefined
+ ? JSON.stringify(val, null, 2)
+ : "Not set"}
+
+
-
-
+ ))}
+
+ ) : (
+
+
+ Current Configuration
+
+
- Current
+ {String(standard.currentTenantValue.CurrentValue)}
-
+
+
+ )
+ ) : null}
+ >
+ ) : (
+ <>
+ {standard.currentTenantValue?.Value === false && (
+
+ This setting is not configured correctly
+
+ )}
+ {/* Show Current value property-by-property for non-compliant standards */}
+ {standard.currentTenantValue?.CurrentValue !== undefined &&
+ (typeof standard.currentTenantValue.CurrentValue ===
+ "object" &&
+ standard.currentTenantValue.CurrentValue !== null ? (
+
+
+ Current Configuration
+
+ {Object.entries(
+ standard.currentTenantValue.CurrentValue,
+ ).map(([key, val]) => (
+
- {JSON.stringify(
- standard.currentTenantValue.CurrentValue,
- null,
- 2
- )}
+ {key}
+
+
+ {val !== undefined
+ ? JSON.stringify(val, null, 2)
+ : "Not set"}
+
+
+ ))}
+
+ ) : (
+
+
+ Current Configuration
+
+
+
+ {String(standard.currentTenantValue.CurrentValue)}
+
- )}
+ ))}
>
- ) : null}
+ )}
{/* Only show values if they're not simple true/false that's already covered by the alerts above */}
{!(
@@ -1810,7 +2092,7 @@ const Page = () => {
key === "Value" &&
(standard.currentTenantValue?.Value === true ||
standard.currentTenantValue?.Value === false)
- )
+ ),
)
.map(([key, value]) => {
const actualValue = key === "Value" ? value : value;
@@ -1858,8 +2140,8 @@ const Page = () => {
standard.complianceStatus === "Compliant"
? "success.main"
: isDifferent
- ? "error.main"
- : "inherit",
+ ? "error.main"
+ : "inherit",
fontWeight:
standard.complianceStatus === "Non-Compliant" &&
isDifferent
@@ -1902,10 +2184,10 @@ const Page = () => {
standard.complianceStatus === "Compliant"
? "success.main"
: standard.complianceStatus === "Overridden"
- ? "warning.main"
- : standard.complianceStatus === "Reporting Disabled"
- ? "text.secondary"
- : "error.main",
+ ? "warning.main"
+ : standard.complianceStatus === "Reporting Disabled"
+ ? "text.secondary"
+ : "error.main",
fontWeight:
standard.complianceStatus === "Non-Compliant"
? "medium"
@@ -1924,26 +2206,265 @@ const Page = () => {
standard.overridingTemplateId}
) : standard.complianceStatus === "Compliant" ? (
-
- This setting is configured correctly
-
- ) : standard.currentTenantValue?.Value === false ||
- standard.currentTenantValue === false ? (
-
- This setting is not configured correctly
-
- ) : standard.currentTenantValue !== undefined ? (
- String(
- standard.currentTenantValue?.Value !== undefined
- ? standard.currentTenantValue?.Value
- : standard.currentTenantValue
- )
+ <>
+ {/* Show Current value property-by-property in card view */}
+ {standard.currentTenantValue?.CurrentValue !== undefined ? (
+ typeof standard.currentTenantValue.CurrentValue ===
+ "object" &&
+ standard.currentTenantValue.CurrentValue !== null ? (
+
+
+ Current Configuration
+
+ {Object.entries(
+ standard.currentTenantValue.CurrentValue,
+ ).map(([key, val]) => (
+
+
+ {key}
+
+
+
+
+
+
+ {val !== undefined
+ ? JSON.stringify(val, null, 2)
+ : "Not set"}
+
+
+
+ ))}
+
+ ) : (
+
+
+ Current Configuration
+
+
+
+ {String(standard.currentTenantValue.CurrentValue)}
+
+
+
+ )
+ ) : null}
+ >
) : (
-
- This setting is not configured, or data has not been collected.
- If you are getting this after data collection, the tenant might
- not be licensed for this feature
-
+ <>
+ {(standard.currentTenantValue?.Value === false ||
+ standard.currentTenantValue === false) && (
+
+ This setting is not configured correctly
+
+ )}
+ {/* Show Current value property-by-property for non-compliant standards in card view */}
+ {standard.currentTenantValue?.CurrentValue !== undefined ? (
+ typeof standard.currentTenantValue.CurrentValue ===
+ "object" &&
+ standard.currentTenantValue.CurrentValue !== null ? (
+
+
+ Current Configuration
+
+ {Object.entries(
+ standard.currentTenantValue.CurrentValue,
+ ).map(([key, val]) => (
+
+
+ {key}
+
+
+
+ {val !== undefined
+ ? JSON.stringify(val, null, 2)
+ : "Not set"}
+
+
+
+ ))}
+
+ ) : (
+
+
+ Current Configuration
+
+
+
+ {String(standard.currentTenantValue.CurrentValue)}
+
+
+
+ )
+ ) : standard.currentTenantValue !== undefined &&
+ standard.currentTenantValue?.Value !== true &&
+ standard.currentTenantValue?.Value !== false ? (
+
+ {String(
+ standard.currentTenantValue?.Value !== undefined
+ ? standard.currentTenantValue?.Value
+ : standard.currentTenantValue,
+ )}
+
+ ) : standard.currentTenantValue === undefined ||
+ (standard.currentTenantValue?.Value === null &&
+ standard.currentTenantValue?.CurrentValue === undefined &&
+ standard.currentTenantValue?.ExpectedValue ===
+ undefined) ? (
+
+ This setting is not configured, or data has not been
+ collected. If you are getting this after data collection,
+ the tenant might not be licensed for this feature
+
+ ) : null}
+ >
)}
)}
diff --git a/src/pages/tenant/manage/drift.js b/src/pages/tenant/manage/drift.js
index 5e3d3756352d..0b3c226bc693 100644
--- a/src/pages/tenant/manage/drift.js
+++ b/src/pages/tenant/manage/drift.js
@@ -130,7 +130,7 @@ const ManageDriftPage = () => {
}
if (item.customerSpecificDeviations && Array.isArray(item.customerSpecificDeviations)) {
acc.customerSpecificDeviationsList.push(
- ...item.customerSpecificDeviations.filter((dev) => dev !== null)
+ ...item.customerSpecificDeviations.filter((dev) => dev !== null),
);
}
if (item.deniedDeviations && Array.isArray(item.deniedDeviations)) {
@@ -143,7 +143,7 @@ const ManageDriftPage = () => {
Array.isArray(item.driftSettings.ComparisonDetails)
) {
const compliantStandards = item.driftSettings.ComparisonDetails.filter(
- (detail) => detail.Compliant === true
+ (detail) => detail.Compliant === true,
)
.map((detail) => {
// Strip "standards." prefix if present
@@ -205,8 +205,10 @@ const ManageDriftPage = () => {
ComplianceStatus: detail.ComplianceStatus,
StandardValue: detail.StandardValue,
ReportingDisabled: detail.ReportingDisabled,
- expectedValue: "Compliant with template",
- receivedValue: detail.StandardValue,
+ ExpectedValue: detail.ExpectedValue,
+ CurrentValue: detail.CurrentValue,
+ expectedValue: detail.ExpectedValue || "Compliant with template",
+ receivedValue: detail.CurrentValue || detail.StandardValue,
};
})
.filter((item) => item !== null); // Filter out null items where templates weren't found
@@ -236,7 +238,7 @@ const ManageDriftPage = () => {
deniedDeviationsList: [],
alignedStandards: [],
latestDataCollection: null,
- }
+ },
);
// Transform currentDeviations into deviation items for display
@@ -376,6 +378,46 @@ const ManageDriftPage = () => {
}
};
+ // Helper function to format differences for display
+ const formatDifferences = (differences) => {
+ if (!differences || typeof differences !== "object") return null;
+
+ const formatted = [];
+ Object.entries(differences).forEach(([key, value]) => {
+ formatted.push({
+ property: key,
+ expected:
+ value.expected !== undefined ? JSON.stringify(value.expected, null, 2) : "Not set",
+ current: value.current !== undefined ? JSON.stringify(value.current, null, 2) : "Not set",
+ });
+ });
+
+ return formatted;
+ };
+
+ // Helper function to format matching properties for compliant items
+ const formatCompliantProperties = (value) => {
+ if (!value) return null;
+
+ try {
+ const obj = typeof value === "string" ? JSON.parse(value) : value;
+
+ if (typeof obj !== "object" || obj === null) return null;
+
+ const formatted = [];
+ Object.entries(obj).forEach(([key, val]) => {
+ formatted.push({
+ property: key,
+ value: val !== undefined ? JSON.stringify(val, null, 2) : "Not set",
+ });
+ });
+
+ return formatted.length > 0 ? formatted : null;
+ } catch (e) {
+ return null;
+ }
+ };
+
// Helper function to format policy objects for display
const formatPolicyValue = (value) => {
if (!value) return "N/A";
@@ -454,22 +496,27 @@ const ManageDriftPage = () => {
const actualStatus = isActuallyCompliant
? "aligned"
: isLicenseSkipped
- ? "skipped"
- : statusOverride || deviation.Status || deviation.state;
+ ? "skipped"
+ : statusOverride || deviation.Status || deviation.state;
const actualStatusText = isActuallyCompliant
? "Compliant"
: isLicenseSkipped
- ? "Skipped - No License Available"
- : getDeviationStatusText(actualStatus);
+ ? "Skipped - No License Available"
+ : getDeviationStatusText(actualStatus);
// For skipped items, show different expected/received values
let displayExpectedValue = deviation.ExpectedValue || deviation.expectedValue;
let displayReceivedValue = deviation.CurrentValue || deviation.receivedValue;
- // If we have JSON differences, show only the differences
+ // If we have JSON differences, format them for display
+ let formattedDifferences = null;
+ let formattedCompliantProps = null;
+
if (jsonDifferences && !isLicenseSkipped && !isActuallyCompliant) {
- displayExpectedValue = JSON.stringify(jsonDifferences, null, 2);
- displayReceivedValue = "See differences in Expected column";
+ formattedDifferences = formatDifferences(jsonDifferences);
+ } else if ((isActuallyCompliant || actualStatus === "aligned") && displayExpectedValue) {
+ // For compliant items, format the properties to show them matching
+ formattedCompliantProps = formatCompliantProperties(displayExpectedValue);
}
return {
@@ -512,10 +559,289 @@ const ManageDriftPage = () => {
)}
- {(displayExpectedValue && displayExpectedValue !== "Compliant with template") ||
- displayReceivedValue ? (
-
- {displayExpectedValue && displayExpectedValue !== "Compliant with template" && (
+ {formattedDifferences && formattedDifferences.length > 0 ? (
+
+
+ Property Differences
+
+ {formattedDifferences.map((diff, idx) => (
+
+
+ {diff.property}
+
+
+
+
+ Expected
+
+
+
+
+
+
+ {diff.expected}
+
+
+
+
+
+ Current
+
+
+
+
+
+
+ {diff.current}
+
+
+
+
+
+ ))}
+
+ ) : formattedCompliantProps && formattedCompliantProps.length > 0 ? (
+
+
+ Compliant Properties
+
+ {formattedCompliantProps.map((prop, idx) => (
+
+
+ {prop.property}
+
+
+
+
+ Expected
+
+
+
+
+
+
+ {prop.value}
+
+
+
+
+
+ Current
+
+
+
+
+
+
+ {prop.value}
+
+
+
+
+
+ ))}
+
+ ) : displayExpectedValue || displayReceivedValue ? (
+
+ {displayExpectedValue && (
{
color: "text.secondary",
textTransform: "uppercase",
letterSpacing: 0.5,
+ display: "block",
+ mb: 0.5,
}}
>
- {jsonDifferences ? "Differences" : "Expected"}
+ Expected
+ {(isActuallyCompliant || actualStatus === "aligned") && (
+
+
+
+ )}
{
fontSize: "0.8125rem",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
+ color:
+ isActuallyCompliant || actualStatus === "aligned"
+ ? "success.dark"
+ : "text.primary",
}}
>
- {displayExpectedValue}
+ {displayExpectedValue === "Compliant with template"
+ ? displayReceivedValue || "Compliant"
+ : displayExpectedValue}
)}
- {displayReceivedValue && !jsonDifferences && (
+ {displayReceivedValue && (
{
color: "text.secondary",
textTransform: "uppercase",
letterSpacing: 0.5,
+ display: "block",
+ mb: 0.5,
}}
>
Current
+ {(isActuallyCompliant || actualStatus === "aligned") && (
+
+
+
+ )}
{
fontSize: "0.8125rem",
whiteSpace: "pre-wrap",
wordBreak: "break-word",
+ color:
+ isActuallyCompliant || actualStatus === "aligned"
+ ? "success.dark"
+ : "text.primary",
}}
>
{displayReceivedValue}
@@ -646,15 +1042,15 @@ const ManageDriftPage = () => {
const deviationItems = createDeviationItems(processedDriftData.currentDeviations);
const acceptedDeviationItems = createDeviationItems(
processedDriftData.acceptedDeviations,
- "accepted"
+ "accepted",
);
const customerSpecificDeviationItems = createDeviationItems(
processedDriftData.customerSpecificDeviationsList,
- "customerspecific"
+ "customerspecific",
);
const deniedDeviationItems = createDeviationItems(
processedDriftData.deniedDeviationsList,
- "denied"
+ "denied",
);
const alignedStandardItems = createDeviationItems(processedDriftData.alignedStandards, "aligned");
@@ -662,7 +1058,7 @@ const ManageDriftPage = () => {
const licenseSkippedItems = deviationItems.filter((item) => item.isLicenseSkipped);
const compliantFromDeviations = deviationItems.filter((item) => item.isActuallyCompliant);
const actualDeviationItems = deviationItems.filter(
- (item) => !item.isLicenseSkipped && !item.isActuallyCompliant
+ (item) => !item.isLicenseSkipped && !item.isActuallyCompliant,
);
// Combine compliant items from both sources
@@ -884,7 +1280,7 @@ const ManageDriftPage = () => {
// Find current tenant data
const currentTenantData = currentTenantInfo.data?.find(
- (tenant) => tenant.defaultDomainName === tenantFilter
+ (tenant) => tenant.defaultDomainName === tenantFilter,
);
// Actions for the ActionsMenu
@@ -1047,7 +1443,7 @@ const ManageDriftPage = () => {
(item) =>
item.text?.toLowerCase().includes(searchQuery.toLowerCase()) ||
item.subtext?.toLowerCase().includes(searchQuery.toLowerCase()) ||
- item.standardName?.toLowerCase().includes(searchQuery.toLowerCase())
+ item.standardName?.toLowerCase().includes(searchQuery.toLowerCase()),
);
}
@@ -1285,10 +1681,10 @@ const ManageDriftPage = () => {
combinedScore === 100
? "success"
: combinedScore >= 80
- ? "warning"
- : combinedScore >= 30
- ? "warning"
- : "error"
+ ? "warning"
+ : combinedScore >= 30
+ ? "warning"
+ : "error"
}
variant="outlined"
/>
@@ -1320,7 +1716,7 @@ const ManageDriftPage = () => {
query: query,
},
undefined,
- { shallow: true }
+ { shallow: true },
);
}}
placeholder="Select a drift template..."
@@ -1364,8 +1760,8 @@ const ManageDriftPage = () => {
sortBy === "name"
? "Name"
: sortBy === "status"
- ? "Status"
- : "Category",
+ ? "Status"
+ : "Category",
value: sortBy,
}
: null
@@ -1426,7 +1822,7 @@ const ManageDriftPage = () => {
(deviation.standardName?.includes("ConditionalAccessTemplate") ||
deviation.standardName?.includes("IntuneTemplate")) &&
deviation.expectedValue ===
- "This policy only exists in the tenant, not in the template."
+ "This policy only exists in the tenant, not in the template.",
) && (
+
+ Tenant Region:{" "}
+ {getTenant.data?.OpenIdConfig?.tenant_region_scope}