diff --git a/package.json b/package.json
index 51a2591ae67b..624037bd4884 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cipp",
- "version": "7.1.3",
+ "version": "8.0.2",
"author": "CIPP Contributors",
"homepage": "https://cipp.app/",
"bugs": {
@@ -112,4 +112,4 @@
"eslint": "9.22.0",
"eslint-config-next": "15.2.2"
}
-}
+}
\ No newline at end of file
diff --git a/public/version.json b/public/version.json
index f47e65940e3b..6e2baa8faabc 100644
--- a/public/version.json
+++ b/public/version.json
@@ -1,3 +1,3 @@
{
- "version": "8.0.2"
+ "version": "8.0.3"
}
\ No newline at end of file
diff --git a/src/components/CippComponents/CippMailboxPermissionsDialog.jsx b/src/components/CippComponents/CippMailboxPermissionsDialog.jsx
index 52b2f3cb7372..b890544cc216 100644
--- a/src/components/CippComponents/CippMailboxPermissionsDialog.jsx
+++ b/src/components/CippComponents/CippMailboxPermissionsDialog.jsx
@@ -25,7 +25,7 @@ const CippMailboxPermissionsDialog = ({ formHook }) => {
});
return (
-
+
{
})) || []
}
/>
- {fullAccess && (
+
+ {fullAccess?.length > 0 && (
+
- )}
-
+
+ )}
{
const [configuredState, setConfiguredState] = useState({});
const [filter, setFilter] = useState("all");
@@ -188,46 +189,48 @@ const CippStandardAccordion = ({
// Initialize when watchedValues are available
useEffect(() => {
- // Only run initialization if we have watchedValues and they contain data
- if (!watchedValues || Object.keys(watchedValues).length === 0) {
- return;
- }
-
- // Prevent re-initialization if we already have configuration state
- const hasConfigState = Object.keys(configuredState).length > 0;
- if (hasConfigState) {
- return;
- }
-
- console.log("Initializing configuration state from template values");
- const initial = {};
- const initialConfigured = {};
-
- // For each standard, get its current values and determine if it's configured
- Object.keys(selectedStandards).forEach((standardName) => {
- const currentValues = _.get(watchedValues, standardName);
- if (!currentValues) return;
-
- initial[standardName] = _.cloneDeep(currentValues);
+ if (editMode) {
+ // Only run initialization if we have watchedValues and they contain data
+ if (!watchedValues || Object.keys(watchedValues).length === 0) {
+ return;
+ }
- const baseStandardName = standardName.split("[")[0];
- const standard = providedStandards.find((s) => s.name === baseStandardName);
- if (standard) {
- initialConfigured[standardName] = isStandardConfigured(
- standardName,
- standard,
- currentValues
- );
+ // Prevent re-initialization if we already have configuration state
+ const hasConfigState = Object.keys(configuredState).length > 0;
+ if (hasConfigState) {
+ return;
}
- });
- // Store both the initial values and set them as current saved values
- setOriginalValues(initial);
- setSavedValues(initial);
- setConfiguredState(initialConfigured);
- // Only depend on watchedValues and selectedStandards to avoid infinite loops
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [watchedValues, selectedStandards]);
+ console.log("Initializing configuration state from template values");
+ const initial = {};
+ const initialConfigured = {};
+
+ // For each standard, get its current values and determine if it's configured
+ Object.keys(selectedStandards).forEach((standardName) => {
+ const currentValues = _.get(watchedValues, standardName);
+ if (!currentValues) return;
+
+ initial[standardName] = _.cloneDeep(currentValues);
+
+ const baseStandardName = standardName.split("[")[0];
+ const standard = providedStandards.find((s) => s.name === baseStandardName);
+ if (standard) {
+ initialConfigured[standardName] = isStandardConfigured(
+ standardName,
+ standard,
+ currentValues
+ );
+ }
+ });
+
+ // Store both the initial values and set them as current saved values
+ setOriginalValues(initial);
+ setSavedValues(initial);
+ setConfiguredState(initialConfigured);
+ // Only depend on watchedValues and selectedStandards to avoid infinite loops
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }
+ }, [watchedValues, selectedStandards, editMode]);
// Save changes for a standard
const handleSave = (standardName, standard, current) => {
@@ -519,6 +522,9 @@ const CippStandardAccordion = ({
// Get current values and check if they differ from saved values
const current = _.get(watchedValues, standardName);
const saved = _.get(savedValues, standardName) || {};
+ console.log(`Current values for ${standardName}:`, current);
+ console.log(`Saved values for ${standardName}:`, saved);
+
const hasUnsaved = !_.isEqual(current, saved);
// Check if all required fields are filled
@@ -609,7 +615,7 @@ const CippStandardAccordion = ({
const canSave = hasAction && requiredFieldsFilled && hasUnsaved;
console.log(
- `Standard: ${standardName}, Action Required: ${actionRequired}, Has Action: ${hasAction}, Required Fields Filled: ${requiredFieldsFilled}, Can Save: ${canSave}`
+ `Standard: ${standardName}, Action Required: ${actionRequired}, Has Action: ${hasAction}, Required Fields Filled: ${requiredFieldsFilled}, Unsaved Changes: ${hasUnsaved}, Can Save: ${canSave}`
);
return (
@@ -760,7 +766,7 @@ const CippStandardAccordion = ({
-
+
- {comparisonApi.data?.find((comparison) => comparison.RowKey === currentTenant) && (
+ {comparisonApi.data?.find(
+ (comparison) => comparison.tenantFilter === currentTenant
+ ) && (
{
}
sx={{ ml: 2 }}
/>
-
-
-
- }
- size="small"
- label={`Updated on ${new Date(
- comparisonApi.data.find(
- (comparison) => comparison.RowKey === currentTenant
- ).LastRefresh
- ).toLocaleString()}`}
- />
)}
@@ -501,209 +500,380 @@ const Page = () => {
))}
>
)}
-
-
-
-
- setSearchQuery(e.target.value)}
- slotProps={{
- input: {
- startAdornment: (
-
-
-
- ),
- endAdornment: searchQuery && (
-
-
- setSearchQuery("")}
- aria-label="Clear search"
- >
-
-
-
-
- ),
- },
+ {!comparisonApi.isFetching && (
+ <>
+
+
-
-
-
-
-
-
-
-
- {comparisonApi.isError && (
-
-
- Error fetching comparison data
-
-
- There was an error retrieving the comparison data. Please try running the report again
- by clicking the "Run Report Once" button above.
-
- {comparisonApi.error && (
-
-
- {comparisonApi.error.message || JSON.stringify(comparisonApi.error, null, 2)}
+
+ setSearchQuery(e.target.value)}
+ slotProps={{
+ input: {
+ startAdornment: (
+
+
+
+ ),
+ endAdornment: searchQuery && (
+
+
+ setSearchQuery("")}
+ aria-label="Clear search"
+ >
+
+
+
+
+ ),
+ },
+ }}
+ />
+
+
+
+
+
+
+
+
+ {comparisonApi.isError && (
+
+
+ Error fetching comparison data
+
+
+ There was an error retrieving the comparison data. Please try running the report
+ again by clicking the "Run Report Once" button above.
-
+ {comparisonApi.error && (
+
+
+ {comparisonApi.error.message || JSON.stringify(comparisonApi.error, null, 2)}
+
+
+ )}
+
)}
-
- )}
- {comparisonApi.isSuccess && (!comparisonApi.data || comparisonApi.data.length === 0) && (
-
-
- No comparison data is available. This might be because:
-
-
-
- • The tenant has not been scanned yet
-
-
- • The template has no standards configured
-
-
- • There was an issue with the comparison
-
-
-
- Try running the report by clicking the "Run Report Once" button above.
-
-
- )}
+ {comparisonApi.isSuccess &&
+ (!comparisonApi.data || comparisonApi.data.length === 0) && (
+
+
+ No comparison data is available. This might be because:
+
+
+
+ • The tenant has not been scanned yet
+
+
+ • The template has no standards configured
+
+
+ • There was an issue with the comparison
+
+
+
+ Try running the report by clicking the "Run Report Once" button above.
+
+
+ )}
- {filteredGroupedStandards && Object.keys(filteredGroupedStandards).length === 0 && (
-
-
- No standards match the selected filter criteria or search query.
-
-
- Try selecting a different filter or modifying the search query.
-
-
- )}
+ {filteredGroupedStandards && Object.keys(filteredGroupedStandards).length === 0 && (
+
+
+ No standards match the selected filter criteria or search query.
+
+
+ Try selecting a different filter or modifying the search query.
+
+
+ )}
- {Object.keys(filteredGroupedStandards).map((category) => (
-
-
- {category}
-
+ {Object.keys(filteredGroupedStandards).map((category) => (
+
+
+ {category}
+
- {filteredGroupedStandards[category].map((standard, index) => (
-
-
-
-
-
-
- (
+
+
+
+
+
- {standard.complianceStatus === "Compliant" ? (
-
- ) : standard.complianceStatus === "Reporting Disabled" ? (
-
- ) : (
-
- )}
-
-
- {standard?.standardName}
+
+
+ {standard.complianceStatus === "Compliant" ? (
+
+ ) : standard.complianceStatus === "Reporting Disabled" ? (
+
+ ) : (
+
+ )}
+
+
+ {standard?.standardName}
+
+
+
+
+
+
+
+
+
+ {!standard.standardValue ? (
+
+ This data has not yet been collected. Collect the data by pressing the
+ report button on the top of the page.
+
+ ) : (
-
+
+
+ {standard.standardValue &&
+ typeof standard.standardValue === "object" &&
+ Object.keys(standard.standardValue).length > 0 ? (
+ Object.entries(standard.standardValue).map(([key, value]) => (
+
+
+ {key}:
+
+
+ {typeof value === "object" && value !== null
+ ? value?.label || JSON.stringify(value)
+ : value === true
+ ? "Enabled"
+ : value === false
+ ? "Disabled"
+ : String(value)}
+
+
+ ))
+ ) : (
+
+ {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
+
+ )}
+
+ )}
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {currentTenant}
+
+
+
+
+
+
+
+
+
+ {standard.complianceStatus}
+
+
+ {standard.currentTenantValue?.LastRefresh && (
+
+
+
+ }
+ size="small"
+ label={`${new Date(
+ standard.currentTenantValue.LastRefresh
+ ).toLocaleString()}`}
+ variant="outlined"
+ />
+ )}
+
-
-
-
-
- {!standard.standardValue ? (
-
- This data has not yet been collected. Collect the data by pressing the
- report button on the top of the page.
-
- ) : (
-
-
+
+
+ {/* Existing tenant comparison content */}
+ {typeof standard.currentTenantValue?.Value === "object" &&
+ standard.currentTenantValue?.Value !== null ? (
{
borderColor: "divider",
}}
>
- {standard.standardValue &&
- typeof standard.standardValue === "object" &&
- Object.keys(standard.standardValue).length > 0 ? (
- Object.entries(standard.standardValue).map(([key, value]) => (
-
-
- {key}:
-
-
- {typeof value === "object" && value !== null
- ? value?.label || JSON.stringify(value)
- : value === true
- ? "Enabled"
- : value === false
- ? "Disabled"
- : String(value)}
-
-
- ))
+ {standard.complianceStatus === "Reporting Disabled" ? (
+
+ Reporting is disabled for this standard in the template
+ configuration.
+
) : (
-
- {standard.standardValue === true ? (
-
+ <>
+ {standard.complianceStatus === "Compliant" ? (
+
This setting is configured correctly
- ) : standard.standardValue === false ? (
-
+ ) : standard.currentTenantValue?.Value === 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
-
- )}
-
- )}
-
-
-
- )}
-
-
-
-
-
-
-
+ ) : null}
-
-
-
-
-
-
-
-
-
- {currentTenant}
-
-
+ {/* Only show values if they're not simple true/false that's already covered by the alerts above */}
+ {!(
+ standard.complianceStatus === "Compliant" &&
+ (standard.currentTenantValue?.Value === true ||
+ standard.currentTenantValue?.Value === false)
+ ) &&
+ Object.entries(standard.currentTenantValue)
+ .filter(
+ ([key]) =>
+ key !== "LastRefresh" &&
+ // Skip showing the Value field separately if it's just true/false
+ !(
+ key === "Value" &&
+ (standard.currentTenantValue?.Value === true ||
+ standard.currentTenantValue?.Value === false)
+ )
+ )
+ .map(([key, value]) => {
+ const actualValue = key === "Value" ? value : value;
+
+ const standardValueForKey =
+ standard.standardValue &&
+ typeof standard.standardValue === "object"
+ ? standard.standardValue[key]
+ : undefined;
+
+ const isDifferent =
+ standardValueForKey !== undefined &&
+ JSON.stringify(actualValue) !==
+ JSON.stringify(standardValueForKey);
+
+ return (
+
+
+ {key}:
+ {" "}
+
+ {typeof value === "object" && value !== null
+ ? value?.label || JSON.stringify(value)
+ : value === true
+ ? "Enabled"
+ : value === false
+ ? "Disabled"
+ : String(value)}
+
+
+ );
+ })}
+ >
+ )}
-
-
-
-
-
- {standard.complianceStatus}
-
-
-
-
-
-
- {/* Existing tenant comparison content */}
- {typeof standard.currentTenantValue === "object" &&
- standard.currentTenantValue !== null ? (
-
- {standard.complianceStatus === "Reporting Disabled" ? (
-
- Reporting is disabled for this standard in the template configuration.
-
) : (
- Object.entries(standard.currentTenantValue).map(([key, value]) => {
- const standardValueForKey =
- standard.standardValue && typeof standard.standardValue === "object"
- ? standard.standardValue[key]
- : undefined;
-
- const isDifferent =
- standardValueForKey !== undefined &&
- JSON.stringify(value) !== JSON.stringify(standardValueForKey);
-
- return (
-
-
- {key}:
-
-
- {standard.complianceStatus === "Compliant" && value === true
- ? "Compliant"
- : typeof value === "object" && value !== null
- ? value?.label || JSON.stringify(value)
- : value === true
- ? "Enabled"
- : value === false
- ? "Disabled"
- : String(value)}
-
-
- );
- })
+
+ {standard.complianceStatus === "Reporting Disabled" ? (
+
+ Reporting is disabled for this standard in the template
+ configuration.
+
+ ) : 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
+ )
+ ) : (
+
+ 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.complianceStatus === "Reporting Disabled" ? (
-
- Reporting is disabled for this standard in the template configuration.
-
- ) : standard.complianceStatus === "Compliant" &&
- standard.currentTenantValue === true ? (
-
- This setting is configured correctly
-
- ) : standard.currentTenantValue === false ? (
-
- This setting is not configured correctly
-
- ) : standard.currentTenantValue !== undefined ? (
- String(standard.currentTenantValue)
- ) : (
-
- 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.complianceDetails && (
-
-
-
-
-
-
- {standard.complianceDetails}
-
-
+ {standard.complianceDetails && (
+
+
+
+
+
+
+ {standard.complianceDetails}
+
+
+
+ )}
- )}
-
+ ))}
+
))}
-
- ))}
+ >
+ )}
{
// Track form changes
useEffect(() => {
+ // Compare the current form values with the initial values to check for real changes
+ const currentValues = formControl.getValues();
+ const initialValues = initialStandardsRef.current;
+
if (
formState.isDirty ||
JSON.stringify(selectedStandards) !== JSON.stringify(initialStandardsRef.current)
@@ -109,7 +113,7 @@ const Page = () => {
} else {
setHasUnsavedChanges(false);
}
- }, [formState.isDirty, selectedStandards]);
+ }, [formState.isDirty, selectedStandards, formControl]);
useEffect(() => {
if (router.query.id) {
@@ -341,7 +345,12 @@ const Page = () => {
selectedStandards={selectedStandards}
edit={editMode}
updatedAt={updatedAt}
- onSaveSuccess={() => setHasUnsavedChanges(false)}
+ onSaveSuccess={() => {
+ // Reset unsaved changes flag
+ setHasUnsavedChanges(false);
+ // Update reference for future change detection
+ initialStandardsRef.current = { ...selectedStandards };
+ }}
/>
@@ -358,6 +367,7 @@ const Page = () => {
handleRemoveStandard={handleRemoveStandard}
handleAddMultipleStandard={handleAddMultipleStandard} // Pass the handler for adding multiple
formControl={formControl}
+ editMode={editMode}
/>
)}