From edcad608a8775b516015a1f4f1b206e7435c3e9d Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 09:17:19 -0500 Subject: [PATCH 1/8] Refactor tenant selection and URL sync logic Simplifies tenant selection by making the URL parameter the single source of truth for tenant changes. Updates settings only when the URL changes, and removes redundant settings updates from the selection handler. Also streamlines effect dependencies and improves comments for clarity. --- .../CippComponents/CippTenantSelector.jsx | 61 +++++++++---------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/src/components/CippComponents/CippTenantSelector.jsx b/src/components/CippComponents/CippTenantSelector.jsx index de58af87c29a..688afab83bb8 100644 --- a/src/components/CippComponents/CippTenantSelector.jsx +++ b/src/components/CippComponents/CippTenantSelector.jsx @@ -184,7 +184,7 @@ export const CippTenantSelector = (props) => { // Cancel all in-flight queries before changing tenant queryClient.cancelQueries(); - // Update router and settings + // Update router only - let the URL watcher handle settings query.tenantFilter = currentTenant.value; router.replace( { @@ -194,53 +194,52 @@ export const CippTenantSelector = (props) => { undefined, { shallow: true } ); - - settings.handleUpdate({ - currentTenant: currentTenant.value, - }); } - //if we have a tenantfilter, we add the tenantfilter to the title of the tab/page so its "Tenant - original title". } }, [currentTenant?.value]); - // This effect handles when the URL parameter changes externally + // This effect handles when the URL parameter changes (from deep link or user selection) + // This is the single source of truth for tenant changes useEffect(() => { - if (!router.isReady || !tenantList.isSuccess || !settings.isInitialized) return; + if (!router.isReady || !tenantList.isSuccess) return; - // Get the current tenant from URL or settings - const urlTenant = router.query.tenantFilter || settings.currentTenant; + const urlTenant = router.query.tenantFilter; - // Only update if there's a URL tenant and it's different from our current state - if (urlTenant && (!currentTenant || urlTenant !== currentTenant.value)) { + // Only process if we have a URL tenant + if (urlTenant) { // Find the tenant in our list const matchingTenant = tenantList.data.find( ({ defaultDomainName }) => defaultDomainName === urlTenant ); if (matchingTenant) { - setSelectedTenant({ - value: urlTenant, - label: `${matchingTenant.displayName} (${urlTenant})`, - addedFields: { - defaultDomainName: matchingTenant.defaultDomainName, - displayName: matchingTenant.displayName, - customerId: matchingTenant.customerId, - initialDomainName: matchingTenant.initialDomainName, - }, - }); + // Update local state if different + if (!currentTenant || urlTenant !== currentTenant.value) { + setSelectedTenant({ + value: urlTenant, + label: `${matchingTenant.displayName} (${urlTenant})`, + addedFields: { + defaultDomainName: matchingTenant.defaultDomainName, + displayName: matchingTenant.displayName, + customerId: matchingTenant.customerId, + initialDomainName: matchingTenant.initialDomainName, + }, + }); + } + + // Update settings if different (null filter in settings-context prevents saving null) + if (settings.currentTenant !== urlTenant) { + settings.handleUpdate({ + currentTenant: urlTenant, + }); + } } } - }, [ - router.isReady, - router.query.tenantFilter, - tenantList.isSuccess, - settings.currentTenant, - settings.isInitialized, - ]); + }, [router.isReady, router.query.tenantFilter, tenantList.isSuccess]); // This effect ensures the tenant filter parameter is included in the URL when missing useEffect(() => { - if (!router.isReady || !settings.isInitialized || !settings.currentTenant) return; + if (!router.isReady || !settings.currentTenant) return; // If the tenant parameter is missing from the URL but we have it in settings if (!router.query.tenantFilter && settings.currentTenant) { @@ -254,7 +253,7 @@ export const CippTenantSelector = (props) => { { shallow: true } ); } - }, [router.isReady, router.query, settings.currentTenant, settings.isInitialized]); + }, [router.isReady, router.query.tenantFilter, settings.currentTenant]); useEffect(() => { if (tenant && currentTenant?.value && currentTenant?.value !== "AllTenants") { From 39f747313d7f00b4abf3e168291f1f3c0e266019 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 12:00:42 -0500 Subject: [PATCH 2/8] update text --- src/components/CippStandards/CippStandardsSideBar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CippStandards/CippStandardsSideBar.jsx b/src/components/CippStandards/CippStandardsSideBar.jsx index 170c1b73e28f..53a6d571b8a8 100644 --- a/src/components/CippStandards/CippStandardsSideBar.jsx +++ b/src/components/CippStandards/CippStandardsSideBar.jsx @@ -556,7 +556,7 @@ const CippStandardsSideBar = ({ title="Add Standard" api={{ confirmText: isDriftMode - ? "This template will automatically every hour to detect drift. Are you sure you want to apply this Drift Template?" + ? "This template will automatically every 12 hours to detect drift. Are you sure you want to apply this Drift Template?" : watchForm.runManually ? "Are you sure you want to apply this standard? This template has been set to never run on a schedule. After saving the template you will have to run it manually." : "Are you sure you want to apply this standard? This will apply the template and run every 3 hours.", From 328c709023fa5ede115ee2a00a9d0cb2b2a92ef5 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 12:00:55 -0500 Subject: [PATCH 3/8] fix conditions --- src/pages/endpoint/MEM/devices/index.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/pages/endpoint/MEM/devices/index.js b/src/pages/endpoint/MEM/devices/index.js index 49a658899ea9..e2d5c52ef4ef 100644 --- a/src/pages/endpoint/MEM/devices/index.js +++ b/src/pages/endpoint/MEM/devices/index.js @@ -178,10 +178,7 @@ const Page = () => { GUID: "id", Action: "resetPasscode", }, - condition: (row) => - row.operatingSystem === "iOS" || - row.operatingSystem === "macOS" || - row.operatingSystem === "Android", + condition: (row) => row.operatingSystem === "Android", confirmText: "Are you sure you want to reset the passcode for [deviceName]? A new passcode will be generated and displayed.", }, @@ -194,10 +191,7 @@ const Page = () => { GUID: "id", Action: "removeDevicePasscode", }, - condition: (row) => - row.operatingSystem === "iOS" || - row.operatingSystem === "macOS" || - row.operatingSystem === "Android", + condition: (row) => row.operatingSystem === "iOS", confirmText: "Are you sure you want to remove the passcode from [deviceName]? This will remove the device passcode requirement.", }, From ad999601f92e180b9d1116c7462dcca2bccce732 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 12:39:21 -0500 Subject: [PATCH 4/8] Clean up breadcrumb query params and titles Introduced helper functions to remove unnecessary 'tenantFilter' query parameters and to clean 'AllTenants' from page titles in breadcrumbs. Updated all relevant breadcrumb and navigation logic to use these helpers, ensuring cleaner URLs and more accurate breadcrumb titles throughout the component. Added debug logging for easier troubleshooting. --- .../CippComponents/CippBreadcrumbNav.jsx | 114 +++++++++++++++--- src/utils/cippVersion.js | 1 - 2 files changed, 97 insertions(+), 18 deletions(-) diff --git a/src/components/CippComponents/CippBreadcrumbNav.jsx b/src/components/CippComponents/CippBreadcrumbNav.jsx index c0db7786d253..62c33337f63d 100644 --- a/src/components/CippComponents/CippBreadcrumbNav.jsx +++ b/src/components/CippComponents/CippBreadcrumbNav.jsx @@ -62,6 +62,27 @@ export const CippBreadcrumbNav = () => { const titleCheckCountRef = useRef(0); const titleCheckIntervalRef = useRef(null); + // Helper function to filter out unnecessary query parameters + const getCleanQueryParams = (query) => { + const cleaned = { ...query }; + // Remove tenantFilter if it's "AllTenants" or not explicitly needed + if (cleaned.tenantFilter === "AllTenants" || cleaned.tenantFilter === undefined) { + delete cleaned.tenantFilter; + } + return cleaned; + }; + + // Helper function to clean page titles + const cleanPageTitle = (title) => { + if (!title) return title; + // Remove AllTenants and any surrounding separators + return title + .replace(/\s*-\s*AllTenants\s*/, "") + .replace(/AllTenants\s*-\s*/, "") + .replace(/AllTenants/, "") + .trim(); + }; + // Load tab options on mount useEffect(() => { loadTabOptions().then(setTabOptions); @@ -109,6 +130,9 @@ export const CippBreadcrumbNav = () => { pageTitle = parts.slice(0, -1).join(" - ").trim(); } + // Clean AllTenants from title + pageTitle = cleanPageTitle(pageTitle); + // Skip if title is empty, generic, or error page if ( !pageTitle || @@ -155,7 +179,10 @@ export const CippBreadcrumbNav = () => { if (samePath && !sameTitle) { // Same URL but title changed - update the entry const updated = [...prevHistory]; - updated[prevHistory.length - 1] = currentPage; + updated[prevHistory.length - 1] = { + ...currentPage, + query: getCleanQueryParams(currentPage.query), + }; if (titleCheckIntervalRef.current) { clearInterval(titleCheckIntervalRef.current); titleCheckIntervalRef.current = null; @@ -173,7 +200,11 @@ export const CippBreadcrumbNav = () => { // URL not in history (except possibly as last entry which we handled) - add as new entry if (existingIndex === -1) { - const newHistory = [...prevHistory, currentPage]; + const cleanedCurrentPage = { + ...currentPage, + query: getCleanQueryParams(currentPage.query), + }; + const newHistory = [...prevHistory, cleanedCurrentPage]; // Keep only the last MAX_HISTORY_STORAGE pages const trimmedHistory = @@ -192,7 +223,10 @@ export const CippBreadcrumbNav = () => { titleCheckIntervalRef.current = null; } const updated = prevHistory.slice(0, existingIndex + 1); - updated[existingIndex] = currentPage; + updated[existingIndex] = { + ...currentPage, + query: getCleanQueryParams(currentPage.query), + }; return updated; }); }; @@ -211,9 +245,10 @@ export const CippBreadcrumbNav = () => { const handleBreadcrumbClick = (index) => { const page = history[index]; if (page) { + const cleanedQuery = getCleanQueryParams(page.query); router.push({ pathname: page.path, - query: page.query, + query: cleanedQuery, }); } }; @@ -247,15 +282,18 @@ export const CippBreadcrumbNav = () => { return; } - const pageTitle = document.title.replace(" - CIPP", "").trim(); + let pageTitle = document.title.replace(" - CIPP", "").trim(); const parts = pageTitle.split(" - "); const cleanTitle = parts.length > 1 && parts[parts.length - 1].includes(".") ? parts.slice(0, -1).join(" - ").trim() : pageTitle; - if (cleanTitle && cleanTitle !== "CIPP" && !cleanTitle.toLowerCase().includes("loading")) { - setCurrentPageTitle(cleanTitle); + // Clean AllTenants from title + const finalTitle = cleanPageTitle(cleanTitle); + + if (finalTitle && finalTitle !== "CIPP" && !finalTitle.toLowerCase().includes("loading")) { + setCurrentPageTitle(finalTitle); // Stop checking once we have a valid title if (hierarchicalTitleCheckRef.current) { clearInterval(hierarchicalTitleCheckRef.current); @@ -316,11 +354,11 @@ export const CippBreadcrumbNav = () => { // Check if this item matches the current path if (item.path && pathsMatch(item.path, currentPath)) { - // If this is the current page, include current query params + // If this is the current page, include current query params (cleaned) if (item.path === currentPath) { const lastItem = currentBreadcrumb[currentBreadcrumb.length - 1]; if (lastItem) { - lastItem.query = { ...router.query }; + lastItem.query = getCleanQueryParams(router.query); } } return currentBreadcrumb; @@ -339,6 +377,42 @@ export const CippBreadcrumbNav = () => { let result = findPathInMenu(nativeMenuItems); + console.log("🍞 Breadcrumb Debug - currentPath:", currentPath); + console.log("🍞 Breadcrumb Debug - result from menu:", result); + console.log("🍞 Breadcrumb Debug - tabOptions available:", tabOptions.length); + + // If we found a menu item, check if the current path matches any tab + // If so, tabOptions wins and we use its label + if (result.length > 0 && tabOptions.length > 0) { + const normalizedCurrentPath = currentPath.replace(/\/$/, ""); + + // Check if current path matches any tab (exact match) + const matchingTab = tabOptions.find((tab) => { + const normalizedTabPath = tab.path.replace(/\/$/, ""); + console.log("🍞 Comparing tab path:", normalizedTabPath, "with currentPath:", normalizedCurrentPath, "match:", normalizedTabPath === normalizedCurrentPath); + return normalizedTabPath === normalizedCurrentPath; + }); + + console.log("🍞 Matching tab found:", matchingTab); + + if (matchingTab) { + // Tab matches the current path - use tab's label instead of config's + result = result.map((item, idx) => { + if (idx === result.length - 1) { + console.log("🍞 Updating last breadcrumb from:", item.title, "to:", matchingTab.title); + return { + ...item, + title: matchingTab.title, + type: "tab", + }; + } + return item; + }); + } + } + + console.log("🍞 Final result after tab matching:", result); + // If not found in main menu, check if it's a tab page if (result.length === 0 && tabOptions.length > 0) { const normalizedCurrentPath = currentPath.replace(/\/$/, ""); @@ -395,12 +469,12 @@ export const CippBreadcrumbNav = () => { if (basePagePath.length > 0) { result = basePagePath; - // Add the tab as the final breadcrumb with current query params + // Add the tab as the final breadcrumb with current query params (cleaned) result.push({ title: matchingTab.title, path: matchingTab.path, type: "tab", - query: { ...router.query }, // Include current query params for tab page + query: getCleanQueryParams(router.query), // Include current query params for tab page }); } } @@ -411,7 +485,10 @@ export const CippBreadcrumbNav = () => { const lastItem = result[result.length - 1]; if (lastItem.path && lastItem.path !== currentPath && currentPath.startsWith(lastItem.path)) { // Use the tracked page title if available, otherwise fall back to document.title - const tabTitle = currentPageTitle || document.title.replace(" - CIPP", "").trim(); + let tabTitle = currentPageTitle || document.title.replace(" - CIPP", "").trim(); + + // Clean AllTenants from title + tabTitle = cleanPageTitle(tabTitle); // Add tab as an additional breadcrumb item if ( @@ -423,7 +500,7 @@ export const CippBreadcrumbNav = () => { title: tabTitle, path: currentPath, type: "tab", - query: { ...router.query }, // Include current query params + query: getCleanQueryParams(router.query), // Include current query params (cleaned) }); } } @@ -435,10 +512,11 @@ export const CippBreadcrumbNav = () => { // Handle click for hierarchical breadcrumbs const handleHierarchicalClick = (path, query) => { if (path) { - if (query && Object.keys(query).length > 0) { + const cleanedQuery = getCleanQueryParams(query); + if (cleanedQuery && Object.keys(cleanedQuery).length > 0) { router.push({ pathname: path, - query: query, + query: cleanedQuery, }); } else { router.push(path); @@ -459,6 +537,8 @@ export const CippBreadcrumbNav = () => { if (mode === "hierarchical") { let breadcrumbs = buildHierarchicalBreadcrumbs(); + console.log("🍞 Hierarchical breadcrumbs:", breadcrumbs); + // Fallback: If no breadcrumbs found in navigation config, generate from URL path if (breadcrumbs.length === 0) { const pathSegments = router.pathname.split("/").filter((segment) => segment); @@ -478,7 +558,7 @@ export const CippBreadcrumbNav = () => { title, path, type: "fallback", - query: index === pathSegments.length - 1 ? { ...router.query } : {}, + query: index === pathSegments.length - 1 ? getCleanQueryParams(router.query) : {}, }; }); @@ -488,7 +568,7 @@ export const CippBreadcrumbNav = () => { currentPageTitle !== "CIPP" && !currentPageTitle.toLowerCase().includes("loading") ) { - breadcrumbs[breadcrumbs.length - 1].title = currentPageTitle; + breadcrumbs[breadcrumbs.length - 1].title = cleanPageTitle(currentPageTitle); } } } diff --git a/src/utils/cippVersion.js b/src/utils/cippVersion.js index 3de297a7c070..ed64050c0759 100644 --- a/src/utils/cippVersion.js +++ b/src/utils/cippVersion.js @@ -29,7 +29,6 @@ export async function getCippVersion() { // Build headers including X-CIPP-Version. Accept extra headers to merge. export async function buildVersionedHeaders(extra = {}) { const version = await getCippVersion(); - console.log("CIPP Version:", version); return { "Content-Type": "application/json", "X-CIPP-Version": version, From b8730d5b1718effe0f4a5df4a5d11724c59a2222 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 12:41:13 -0500 Subject: [PATCH 5/8] fix useEffect --- src/pages/tenant/administration/tenants/groups/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/tenant/administration/tenants/groups/edit.js b/src/pages/tenant/administration/tenants/groups/edit.js index dd41f5f382ed..33edecd5e1c6 100644 --- a/src/pages/tenant/administration/tenants/groups/edit.js +++ b/src/pages/tenant/administration/tenants/groups/edit.js @@ -148,7 +148,7 @@ const Page = () => { dynamicRules: formattedDynamicRules, }); } - }, [groupDetails.isSuccess, groupDetails.data]); + }, [groupDetails.isSuccess, groupDetails.data, id]); const customDataFormatter = (values) => { const formattedData = { From 9feba6a994d9cb320d5b1b1724130944d25a9bae Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 12:42:39 -0500 Subject: [PATCH 6/8] Remove debug console.log statements from breadcrumb nav Eliminated multiple console.log statements used for debugging in CippBreadcrumbNav.jsx to clean up the code and reduce console noise. --- src/components/CippComponents/CippBreadcrumbNav.jsx | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/components/CippComponents/CippBreadcrumbNav.jsx b/src/components/CippComponents/CippBreadcrumbNav.jsx index 62c33337f63d..e69377b4e070 100644 --- a/src/components/CippComponents/CippBreadcrumbNav.jsx +++ b/src/components/CippComponents/CippBreadcrumbNav.jsx @@ -377,10 +377,6 @@ export const CippBreadcrumbNav = () => { let result = findPathInMenu(nativeMenuItems); - console.log("🍞 Breadcrumb Debug - currentPath:", currentPath); - console.log("🍞 Breadcrumb Debug - result from menu:", result); - console.log("🍞 Breadcrumb Debug - tabOptions available:", tabOptions.length); - // If we found a menu item, check if the current path matches any tab // If so, tabOptions wins and we use its label if (result.length > 0 && tabOptions.length > 0) { @@ -389,17 +385,13 @@ export const CippBreadcrumbNav = () => { // Check if current path matches any tab (exact match) const matchingTab = tabOptions.find((tab) => { const normalizedTabPath = tab.path.replace(/\/$/, ""); - console.log("🍞 Comparing tab path:", normalizedTabPath, "with currentPath:", normalizedCurrentPath, "match:", normalizedTabPath === normalizedCurrentPath); return normalizedTabPath === normalizedCurrentPath; }); - console.log("🍞 Matching tab found:", matchingTab); - if (matchingTab) { // Tab matches the current path - use tab's label instead of config's result = result.map((item, idx) => { if (idx === result.length - 1) { - console.log("🍞 Updating last breadcrumb from:", item.title, "to:", matchingTab.title); return { ...item, title: matchingTab.title, @@ -411,8 +403,6 @@ export const CippBreadcrumbNav = () => { } } - console.log("🍞 Final result after tab matching:", result); - // If not found in main menu, check if it's a tab page if (result.length === 0 && tabOptions.length > 0) { const normalizedCurrentPath = currentPath.replace(/\/$/, ""); @@ -537,8 +527,6 @@ export const CippBreadcrumbNav = () => { if (mode === "hierarchical") { let breadcrumbs = buildHierarchicalBreadcrumbs(); - console.log("🍞 Hierarchical breadcrumbs:", breadcrumbs); - // Fallback: If no breadcrumbs found in navigation config, generate from URL path if (breadcrumbs.length === 0) { const pathSegments = router.pathname.split("/").filter((segment) => segment); From 448b32d0caa0e15f6eabb32fff521f097b9ae765 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 12:47:38 -0500 Subject: [PATCH 7/8] Update CippBreadcrumbNav.jsx --- src/components/CippComponents/CippBreadcrumbNav.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CippComponents/CippBreadcrumbNav.jsx b/src/components/CippComponents/CippBreadcrumbNav.jsx index e69377b4e070..ed7e4be84c1b 100644 --- a/src/components/CippComponents/CippBreadcrumbNav.jsx +++ b/src/components/CippComponents/CippBreadcrumbNav.jsx @@ -476,7 +476,7 @@ export const CippBreadcrumbNav = () => { if (lastItem.path && lastItem.path !== currentPath && currentPath.startsWith(lastItem.path)) { // Use the tracked page title if available, otherwise fall back to document.title let tabTitle = currentPageTitle || document.title.replace(" - CIPP", "").trim(); - + // Clean AllTenants from title tabTitle = cleanPageTitle(tabTitle); From cefd906f974b1aefe78f7ea80c7d1ae809db10d6 Mon Sep 17 00:00:00 2001 From: John Duprey Date: Fri, 19 Dec 2025 13:14:21 -0500 Subject: [PATCH 8/8] Refactor standards pages and update alignment routes Moved and renamed standards-related pages for better organization, including aligning routes under /tenant/standards/alignment and /tenant/standards/templates. Updated navigation paths and tab options accordingly. Enhanced the alignment page with new drift management actions and removed obsolete files. --- src/layouts/config.js | 2 +- .../tenant/manage/driftManagementActions.js | 6 +- .../drift-alignment => alignment}/index.js | 29 ++- .../standards/list-standards/tabOptions.json | 10 - src/pages/tenant/standards/tabOptions.json | 10 + .../classic-standards => templates}/index.js | 19 +- .../standards/{ => templates}/template.jsx | 229 +++++++++--------- .../standards/tenant-alignment/index.js | 38 --- 8 files changed, 167 insertions(+), 176 deletions(-) rename src/pages/tenant/standards/{list-standards/drift-alignment => alignment}/index.js (53%) delete mode 100644 src/pages/tenant/standards/list-standards/tabOptions.json create mode 100644 src/pages/tenant/standards/tabOptions.json rename src/pages/tenant/standards/{list-standards/classic-standards => templates}/index.js (91%) rename src/pages/tenant/standards/{ => templates}/template.jsx (68%) delete mode 100644 src/pages/tenant/standards/tenant-alignment/index.js diff --git a/src/layouts/config.js b/src/layouts/config.js index cb3833d87085..8923b99832d1 100644 --- a/src/layouts/config.js +++ b/src/layouts/config.js @@ -195,7 +195,7 @@ export const nativeMenuItems = [ items: [ { title: "Standards Management", - path: "/tenant/standards/list-standards", + path: "/tenant/standards/alignment", permissions: ["Tenant.Standards.*"], }, { diff --git a/src/pages/tenant/manage/driftManagementActions.js b/src/pages/tenant/manage/driftManagementActions.js index 6141a292e5aa..5d7cd8e80488 100644 --- a/src/pages/tenant/manage/driftManagementActions.js +++ b/src/pages/tenant/manage/driftManagementActions.js @@ -49,11 +49,13 @@ export const createDriftManagementActions = ({ // Use Next.js router for internal navigation import("next/router") .then(({ default: router }) => { - router.push(`/tenant/standards/template?id=${templateId}&type=${templateType}`); + router.push( + `/tenant/standards/templates/template?id=${templateId}&type=${templateType}` + ); }) .catch(() => { // Fallback to window.location if router is not available - window.location.href = `/tenant/standards/template?id=${templateId}&type=${templateType}`; + window.location.href = `/tenant/standards/templates/template?id=${templateId}&type=${templateType}`; }); }, }); diff --git a/src/pages/tenant/standards/list-standards/drift-alignment/index.js b/src/pages/tenant/standards/alignment/index.js similarity index 53% rename from src/pages/tenant/standards/list-standards/drift-alignment/index.js rename to src/pages/tenant/standards/alignment/index.js index ef0aa4f974e3..6ddd7a186223 100644 --- a/src/pages/tenant/standards/list-standards/drift-alignment/index.js +++ b/src/pages/tenant/standards/alignment/index.js @@ -1,11 +1,12 @@ import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; import { Layout as DashboardLayout } from "/src/layouts/index.js"; import { TabbedLayout } from "/src/layouts/TabbedLayout"; +import { Delete, Add } from "@mui/icons-material"; import { EyeIcon } from "@heroicons/react/24/outline"; import tabOptions from "../tabOptions.json"; const Page = () => { - const pageTitle = "Drift Alignment"; + const pageTitle = "Standard & Drift Alignment"; const actions = [ { @@ -15,6 +16,28 @@ const Page = () => { color: "info", target: "_self", }, + { + label: "Manage Drift", + link: "/tenant/manage/drift?templateId=[standardId]&tenantFilter=[tenantFilter]", + icon: , + color: "info", + target: "_self", + condition: (row) => row.standardType === "drift", + }, + { + label: "Remove Drift Customization", + type: "POST", + url: "/api/ExecUpdateDriftDeviation", + icon: , + data: { + RemoveDriftCustomization: "true", + tenantFilter: "tenantFilter", + }, + confirmText: + "Are you sure you want to remove all drift customizations? This resets the Drift Standard to the default template, and will generate alerts for the drifted items.", + multiPost: false, + condition: (row) => row.standardType === "drift", + }, ]; return ( @@ -23,15 +46,15 @@ const Page = () => { apiUrl="/api/ListTenantAlignment" tenantInTitle={false} actions={actions} - tableFilter={
} simpleColumns={[ "tenantFilter", "standardName", + "standardType", "alignmentScore", "LicenseMissingPercentage", "combinedAlignmentScore", ]} - queryKey="listDriftAlignment" + queryKey="listTenantAlignment" /> ); }; diff --git a/src/pages/tenant/standards/list-standards/tabOptions.json b/src/pages/tenant/standards/list-standards/tabOptions.json deleted file mode 100644 index 1c522e6ca8ca..000000000000 --- a/src/pages/tenant/standards/list-standards/tabOptions.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "label": "Standard & Drift Alignment", - "path": "/tenant/standards/list-standards" - }, - { - "label": "Templates", - "path": "/tenant/standards/list-standards/classic-standards" - } -] diff --git a/src/pages/tenant/standards/tabOptions.json b/src/pages/tenant/standards/tabOptions.json new file mode 100644 index 000000000000..26c6c751dc1a --- /dev/null +++ b/src/pages/tenant/standards/tabOptions.json @@ -0,0 +1,10 @@ +[ + { + "label": "Standard & Drift Alignment", + "path": "/tenant/standards/alignment" + }, + { + "label": "Templates", + "path": "/tenant/standards/templates" + } +] \ No newline at end of file diff --git a/src/pages/tenant/standards/list-standards/classic-standards/index.js b/src/pages/tenant/standards/templates/index.js similarity index 91% rename from src/pages/tenant/standards/list-standards/classic-standards/index.js rename to src/pages/tenant/standards/templates/index.js index a204fddb81b9..6b3c7c483f87 100644 --- a/src/pages/tenant/standards/list-standards/classic-standards/index.js +++ b/src/pages/tenant/standards/templates/index.js @@ -4,13 +4,13 @@ import { Layout as DashboardLayout } from "/src/layouts/index.js"; // had to add import { TabbedLayout } from "/src/layouts/TabbedLayout"; import Link from "next/link"; import { CopyAll, Delete, PlayArrow, AddBox, Edit, GitHub, ContentCopy } from "@mui/icons-material"; -import { ApiGetCall, ApiPostCall } from "../../../../../api/ApiCall"; +import { ApiGetCall, ApiPostCall } from "../../../../api/ApiCall"; import { Grid } from "@mui/system"; -import { CippApiResults } from "../../../../../components/CippComponents/CippApiResults"; +import { CippApiResults } from "../../../../components/CippComponents/CippApiResults"; import { EyeIcon } from "@heroicons/react/24/outline"; import tabOptions from "../tabOptions.json"; import { useSettings } from "/src/hooks/use-settings.js"; -import { CippPolicyImportDrawer } from "../../../../../components/CippComponents/CippPolicyImportDrawer.jsx"; +import { CippPolicyImportDrawer } from "../../../../components/CippComponents/CippPolicyImportDrawer.jsx"; import { PermissionButton } from "/src/utils/permissions.js"; const Page = () => { @@ -36,14 +36,14 @@ const Page = () => { { label: "Edit Template", //when using a link it must always be the full path /identity/administration/users/[id] for example. - link: "/tenant/standards/template?id=[GUID]&type=[type]", + link: "/tenant/standards/templates/template?id=[GUID]&type=[type]", icon: , color: "success", target: "_self", }, { label: "Clone & Edit Template", - link: "/tenant/standards/template?id=[GUID]&clone=true&type=[type]", + link: "/tenant/standards/templates/template?id=[GUID]&clone=true&type=[type]", icon: , color: "success", target: "_self", @@ -183,12 +183,17 @@ const Page = () => { tenantInTitle={false} cardButton={ <> - - - {/* Drift management actions */} - {driftActions.length > 0 && ( - - )} - - - - - {/* Left Column for Accordions */} - - { - // Reset unsaved changes flag - setHasUnsavedChanges(false); - // Update reference for future change detection - initialStandardsRef.current = { ...selectedStandards }; - }} - /> - - - - {/* Show accordions based on selectedStandards (which is populated by API when editing) */} - {existingTemplate.isLoading ? ( - - ) : ( - - )} - - - - + + + + {editMode + ? isDriftMode + ? "Edit Drift Template" + : "Edit Standards Template" + : isDriftMode + ? "Add Drift Template" + : "Add Standards Template"} + + + + + {/* Drift management actions */} + {driftActions.length > 0 && ( + + )} + - {/* Only render the dialog when it's needed */} - {dialogOpen && ( - }> - - - )} - + + + {/* Left Column for Accordions */} + + { + // Reset unsaved changes flag + setHasUnsavedChanges(false); + // Update reference for future change detection + initialStandardsRef.current = { ...selectedStandards }; + }} + /> + + + + {/* Show accordions based on selectedStandards (which is populated by API when editing) */} + {existingTemplate.isLoading ? ( + + ) : ( + + )} + + + + + + + {/* Only render the dialog when it's needed */} + {dialogOpen && ( + }> + + + )} ); }; diff --git a/src/pages/tenant/standards/tenant-alignment/index.js b/src/pages/tenant/standards/tenant-alignment/index.js deleted file mode 100644 index e891f2a0576d..000000000000 --- a/src/pages/tenant/standards/tenant-alignment/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { EyeIcon } from "@heroicons/react/24/outline"; - -const Page = () => { - const pageTitle = "Tenant Alignment"; - - const actions = [ - { - label: "View Tenant Report", - link: "/tenant/manage/applied-standards/?tenantFilter=[tenantFilter]&templateId=[standardId]", - icon: , - color: "info", - target: "_self", - }, - ]; - - return ( - - ); -}; - -Page.getLayout = (page) => {page}; - -export default Page;