diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..e8faf3f82477 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "npm" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + target-branch: "dev" diff --git a/.github/workflows/Assign_Issue_Volunteer.yml b/.github/workflows/Assign_Issue_Volunteer.yml index 08f1e82701d0..23ef1d16ffc0 100644 --- a/.github/workflows/Assign_Issue_Volunteer.yml +++ b/.github/workflows/Assign_Issue_Volunteer.yml @@ -1,10 +1,10 @@ --- -name: 'Assign Issue to Volunteer' -on: [issue_comment] # yamllint disable-line rule:truthy +name: "Assign Issue to Volunteer" +on: [issue_comment] # yamllint disable-line rule:truthy jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-slim steps: - uses: bhermann/issue-volunteer@v0.1.12 with: - GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/Check_for_Version_Update.yml b/.github/workflows/Check_for_Version_Update.yml index f8a7db0343f2..001dfdb4ffa5 100644 --- a/.github/workflows/Check_for_Version_Update.yml +++ b/.github/workflows/Check_for_Version_Update.yml @@ -8,7 +8,7 @@ jobs: build: if: github.repository_owner == 'KelvinTegelaar' name: "Check for Version Update" - runs-on: ubuntu-latest + runs-on: ubuntu-slim steps: - name: Check for Changed Files uses: brettcannon/check-for-changed-files@v1.1.0 diff --git a/.github/workflows/Close_Stale_Issues_and_PRs.yml b/.github/workflows/Close_Stale_Issues_and_PRs.yml index 88607a42675d..a90ac229be88 100644 --- a/.github/workflows/Close_Stale_Issues_and_PRs.yml +++ b/.github/workflows/Close_Stale_Issues_and_PRs.yml @@ -1,18 +1,18 @@ --- -name: 'Close stale issues and PRs' +name: "Close stale issues and PRs" on: schedule: - - cron: '30 1 * * *' + - cron: "30 1 * * *" jobs: stale: if: github.repository_owner == 'KelvinTegelaar' - runs-on: ubuntu-latest + runs-on: ubuntu-slim steps: - uses: actions/stale@v4 with: - stale-issue-message: 'This issue is stale because it has been open 10 days with no activity. We will close this issue soon. If you want this feature implemented you can contribute it. See: https://docs.cipp.app/dev-documentation/contributing-to-the-code . Please notify the team if you are working on this yourself.' - close-issue-message: 'This issue was closed because it has been stalled for 14 days with no activity.' - stale-issue-label: 'no-activity' - exempt-issue-labels: 'planned,bug,roadmap' + stale-issue-message: "This issue is stale because it has been open 10 days with no activity. We will close this issue soon. If you want this feature implemented you can contribute it. See: https://docs.cipp.app/dev-documentation/contributing-to-the-code . Please notify the team if you are working on this yourself." + close-issue-message: "This issue was closed because it has been stalled for 14 days with no activity." + stale-issue-label: "no-activity" + exempt-issue-labels: "planned,bug,roadmap" days-before-stale: 9 days-before-close: 5 diff --git a/.github/workflows/Comment_on_Issues.yml b/.github/workflows/Comment_on_Issues.yml index 8d83f6237d82..c408d8b38c84 100644 --- a/.github/workflows/Comment_on_Issues.yml +++ b/.github/workflows/Comment_on_Issues.yml @@ -7,7 +7,7 @@ on: jobs: add-comment_bug: if: github.repository_owner == 'KelvinTegelaar' && github.event.label.name == 'unconfirmed-by-user' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: issues: write steps: @@ -18,5 +18,5 @@ jobs: body: | Thank you for reporting a potential bug. If you would like to work on this bug, please comment: > I would like to work on this please! - + Thank you for helping us maintain the project! diff --git a/.github/workflows/Label_Issues.yml b/.github/workflows/Label_Issues.yml index 38865a62fae8..ebdc3992980f 100644 --- a/.github/workflows/Label_Issues.yml +++ b/.github/workflows/Label_Issues.yml @@ -7,23 +7,23 @@ on: jobs: label_issues_bugs: if: github.repository_owner == 'KelvinTegelaar' && contains(github.event.issue.title, 'Bug') - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: issues: write steps: - name: Label Issues uses: andymckay/labeler@5c59dabdfd4dd5bd9c6e6d255b01b9d764af4414 with: - add-labels: 'not-assigned' + add-labels: "not-assigned" repo-token: ${{ secrets.GITHUB_TOKEN }} label_issues_frs: if: github.repository_owner == 'KelvinTegelaar' && contains(github.event.issue.title, 'Feature') - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: issues: write steps: - name: Label Issues uses: andymckay/labeler@5c59dabdfd4dd5bd9c6e6d255b01b9d764af4414 with: - add-labels: 'enhancement, not-assigned' + add-labels: "enhancement, not-assigned" repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/auto_comments.yml b/.github/workflows/auto_comments.yml index 7d7b11b45474..6cd003a36ae8 100644 --- a/.github/workflows/auto_comments.yml +++ b/.github/workflows/auto_comments.yml @@ -7,7 +7,7 @@ on: jobs: handle_comment: - runs-on: ubuntu-latest + runs-on: ubuntu-slim # We need permissions to modify issue comments. # 'issues: write' is required for deleting comments. permissions: diff --git a/.github/workflows/label_sponsor_requests.yml b/.github/workflows/label_sponsor_requests.yml index 479cad06c728..bb9d6a31b427 100644 --- a/.github/workflows/label_sponsor_requests.yml +++ b/.github/workflows/label_sponsor_requests.yml @@ -1,4 +1,3 @@ ---- name: Label Issues on: issues: @@ -7,11 +6,13 @@ on: jobs: label_issues_bugs: if: github.repository_owner == 'KelvinTegelaar' && contains(github.event.issue.title, 'Bug') - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: issues: write steps: - 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/.github/workflows/pr_check.yml b/.github/workflows/pr_check.yml index 0d9e2fd7abcd..08cce1de130b 100644 --- a/.github/workflows/pr_check.yml +++ b/.github/workflows/pr_check.yml @@ -16,7 +16,7 @@ permissions: jobs: check-branch: - runs-on: ubuntu-latest + runs-on: ubuntu-slim steps: - name: Check and Comment on PR # Only process fork PRs with specific branch conditions @@ -46,14 +46,14 @@ jobs: } message += '🔒 This PR will now be automatically closed due to the above rules.'; - + // Post the comment await github.rest.issues.createComment({ ...context.repo, issue_number: context.issue.number, body: message }); - + // Close the PR await github.rest.pulls.update({ ...context.repo, diff --git a/next.config.js b/next.config.js index 7a97a124b4bb..416f3f22d1df 100644 --- a/next.config.js +++ b/next.config.js @@ -4,12 +4,13 @@ const config = { images: { unoptimized: true, }, - webpack(config) { - config.module.rules.push({ - test: /\.svg$/, - use: ["@svgr/webpack"], - }); - return config; + turbopack: { + rules: { + "*.svg": { + loaders: ["@svgr/webpack"], + as: "*.js", + }, + }, }, async redirects() { return []; diff --git a/package.json b/package.json index 6d01e9706b32..231bc20bc860 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "8.8.2", + "version": "10.0.9", "author": "CIPP Contributors", "homepage": "https://cipp.app/", "bugs": { @@ -32,9 +32,9 @@ "@monaco-editor/react": "^4.6.0", "@mui/icons-material": "7.3.2", "@mui/lab": "7.0.0-beta.17", - "@mui/material": "7.3.2", + "@mui/material": "7.3.7", "@mui/system": "7.3.2", - "@mui/x-date-pickers": "^8.11.1", + "@mui/x-date-pickers": "^8.25.0", "@musement/iso-duration": "^1.0.0", "@nivo/core": "^0.99.0", "@nivo/sankey": "^0.99.0", @@ -72,18 +72,18 @@ "material-react-table": "^3.0.1", "monaco-editor": "^0.53.0", "mui-tiptap": "^1.14.0", - "next": "^15.2.2", + "next": "^16.1.2", "nprogress": "0.2.0", "numeral": "2.0.6", "prop-types": "15.8.1", "punycode": "^2.3.1", - "react": "19.1.1", + "react": "19.2.3", "react-apexcharts": "1.7.0", "react-beautiful-dnd": "13.1.1", "react-copy-to-clipboard": "^5.1.0", - "react-dom": "19.1.1", + "react-dom": "19.2.3", "react-dropzone": "14.3.8", - "react-error-boundary": "^6.0.0", + "react-error-boundary": "^6.1.0", "react-grid-layout": "^1.5.0", "react-hook-form": "^7.53.0", "react-hot-toast": "2.6.0", @@ -106,7 +106,7 @@ "redux-persist": "^6.0.0", "redux-thunk": "3.1.0", "rehype-raw": "^7.0.0", - "remark-gfm": "^3.0.1", + "remark-gfm": "^4.0.0", "simplebar": "6.3.2", "simplebar-react": "3.3.2", "stylis-plugin-rtl": "2.1.1", diff --git a/public/sponsors/relentless-dark.png b/public/sponsors/relentless-dark.png new file mode 100644 index 000000000000..b234ac7c7903 Binary files /dev/null and b/public/sponsors/relentless-dark.png differ diff --git a/public/sponsors/relentless-light.png b/public/sponsors/relentless-light.png new file mode 100644 index 000000000000..5884597febe7 Binary files /dev/null and b/public/sponsors/relentless-light.png differ diff --git a/public/version.json b/public/version.json index 7a3adc4ddaba..ad15f02cceba 100644 --- a/public/version.json +++ b/public/version.json @@ -1,3 +1,3 @@ { - "version": "8.8.2" + "version": "10.0.9" } \ No newline at end of file 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/CippCards/CippBannerListCard.jsx b/src/components/CippCards/CippBannerListCard.jsx index c1fe171cd4e2..55f5c2ecbae6 100644 --- a/src/components/CippCards/CippBannerListCard.jsx +++ b/src/components/CippCards/CippBannerListCard.jsx @@ -3,6 +3,7 @@ import { useState, useCallback } from "react"; import { Box, Card, + Checkbox, Collapse, Divider, IconButton, @@ -16,13 +17,34 @@ import { CippPropertyListCard } from "./CippPropertyListCard"; import { CippDataTable } from "../CippTable/CippDataTable"; export const CippBannerListCard = (props) => { - const { items = [], isCollapsible = false, isFetching = false, children, ...other } = props; + const { + items = [], + isCollapsible = false, + isFetching = false, + children, + onSelectionChange, + selectedItems = [], + ...other + } = props; const [expanded, setExpanded] = useState(null); const handleExpand = useCallback((itemId) => { setExpanded((prevState) => (prevState === itemId ? null : itemId)); }, []); + const handleCheckboxChange = useCallback( + (itemId, checked) => { + if (onSelectionChange) { + if (checked) { + onSelectionChange([...selectedItems, itemId]); + } else { + onSelectionChange(selectedItems.filter((id) => id !== itemId)); + } + } + }, + [onSelectionChange, selectedItems] + ); + const hasItems = items.length > 0; if (isFetching) { @@ -91,6 +113,16 @@ export const CippBannerListCard = (props) => { alignItems="center" sx={{ flex: 1, minWidth: 0 }} > + {onSelectionChange && ( + { + e.stopPropagation(); + handleCheckboxChange(item.id, e.target.checked); + }} + onClick={(e) => e.stopPropagation()} + /> + )} { + // Extract data with null safety + const identityPassed = data?.TestResultSummary?.IdentityPassed || 0; + const identityTotal = data?.TestResultSummary?.IdentityTotal || 1; + const devicesPassed = data?.TestResultSummary?.DevicesPassed || 0; + const devicesTotal = data?.TestResultSummary?.DevicesTotal || 0; + + // Determine if we should show devices section + const hasDeviceTests = devicesTotal > 0; + + // Calculate percentages for the radial chart + // If no device tests, set devices to 100% (complete) + const devicesPercentage = hasDeviceTests ? (devicesPassed / devicesTotal) * 100 : 100; + const identityPercentage = (identityPassed / identityTotal) * 100; + + const chartData = [ + { + value: devicesPercentage, + fill: "#22c55e", + }, + { + value: identityPercentage, + fill: "#3b82f6", + }, + ]; + + return ( + + + + Assessment + + } + sx={{ pb: 1.5 }} + /> + + + + + + Identity + + + {isLoading ? ( + + ) : ( + <> + {identityPassed}/{identityTotal} + + tests + + + )} + + + {hasDeviceTests && ( + + + Devices + + + {isLoading ? ( + + ) : ( + <> + {devicesPassed}/{devicesTotal} + + tests + + + )} + + + )} + + + Last Data Collection + + + {isLoading ? ( + + ) : data?.ExecutedAt ? ( + + ) : ( + "Not Available" + )} + + + + + {isLoading ? ( + + ) : ( + + + + + + + )} + + + + + ); +}; diff --git a/src/components/CippComponents/AuthMethodCard.jsx b/src/components/CippComponents/AuthMethodCard.jsx new file mode 100644 index 000000000000..97ade0f8d6c6 --- /dev/null +++ b/src/components/CippComponents/AuthMethodCard.jsx @@ -0,0 +1,242 @@ +import { Box, Card, CardHeader, CardContent, Typography, Skeleton } from "@mui/material"; +import { People as UsersIcon } from "@mui/icons-material"; +import { CippSankey } from "./CippSankey"; +import { useRouter } from "next/router"; + +export const AuthMethodCard = ({ data, isLoading }) => { + const router = useRouter(); + + const processData = () => { + if (!data || !Array.isArray(data) || data.length === 0) { + return null; + } + + const enabledUsers = data.filter((user) => user.AccountEnabled === true); + if (enabledUsers.length === 0) { + return null; + } + + const phishableMethods = ["mobilePhone", "email", "microsoftAuthenticatorPush"]; + const phishResistantMethods = ["fido2", "windowsHelloForBusiness", "x509Certificate"]; + + let singleFactor = 0; + let phishableCount = 0; + let phishResistantCount = 0; + let perUserMFA = 0; + let phoneCount = 0; + let authenticatorCount = 0; + let passkeyCount = 0; + let whfbCount = 0; + + enabledUsers.forEach((user) => { + const methods = Array.isArray(user.MFAMethods) ? user.MFAMethods : []; + const perUser = user.PerUser === "enforced" || user.PerUser === "enabled"; + const hasRegistered = user.MFARegistration === true; + + if (perUser && !hasRegistered && methods.length === 0) { + perUserMFA++; + return; + } + + if (!hasRegistered || methods.length === 0) { + singleFactor++; + return; + } + + const hasPhishResistant = methods.some((m) => phishResistantMethods.includes(m)); + const hasPhishable = methods.some((m) => phishableMethods.includes(m)); + + if (hasPhishResistant) { + phishResistantCount++; + if (methods.includes("fido2") || methods.includes("x509Certificate")) { + passkeyCount++; + } + if (methods.includes("windowsHelloForBusiness")) { + whfbCount++; + } + } else if (hasPhishable) { + phishableCount++; + if (methods.includes("mobilePhone") || methods.includes("email")) { + phoneCount++; + } + if ( + methods.includes("microsoftAuthenticatorPush") || + methods.includes("softwareOneTimePasscode") + ) { + authenticatorCount++; + } + } else { + phishableCount++; + authenticatorCount++; + } + }); + + const mfaPercentage = ( + ((phishableCount + phishResistantCount + perUserMFA) / enabledUsers.length) * + 100 + ).toFixed(1); + const phishResistantPercentage = ((phishResistantCount / enabledUsers.length) * 100).toFixed(1); + + const links = [ + { source: "Users", target: "Single factor", value: singleFactor }, + { source: "Users", target: "Multi factor", value: perUserMFA }, + { source: "Users", target: "Phishable", value: phishableCount }, + { source: "Users", target: "Phish resistant", value: phishResistantCount }, + ]; + + if (phoneCount > 0) links.push({ source: "Phishable", target: "Phone", value: phoneCount }); + if (authenticatorCount > 0) + links.push({ source: "Phishable", target: "Authenticator", value: authenticatorCount }); + + if (passkeyCount > 0) + links.push({ source: "Phish resistant", target: "Passkey", value: passkeyCount }); + if (whfbCount > 0) links.push({ source: "Phish resistant", target: "WHfB", value: whfbCount }); + + const description = `${mfaPercentage}% of enabled users have MFA configured. ${phishResistantPercentage}% use phish-resistant authentication methods.`; + + return { + nodes: [ + { id: "Users", nodeColor: "hsl(28, 100%, 53%)" }, + { id: "Single factor", nodeColor: "hsl(0, 100%, 50%)" }, + { id: "Multi factor", nodeColor: "hsl(200, 70%, 50%)" }, + { id: "Phishable", nodeColor: "hsl(39, 100%, 50%)" }, + { id: "Phone", nodeColor: "hsl(39, 100%, 45%)" }, + { id: "Authenticator", nodeColor: "hsl(39, 100%, 55%)" }, + { id: "Phish resistant", nodeColor: "hsl(99, 70%, 50%)" }, + { id: "Passkey", nodeColor: "hsl(140, 70%, 50%)" }, + { id: "WHfB", nodeColor: "hsl(160, 70%, 50%)" }, + ], + links, + description, + }; + }; + + const processedData = processData(); + + const handleNodeClick = (node) => { + let filters = []; + + switch (node.id) { + case "Users": + filters = [{ id: "AccountEnabled", value: "Yes" }]; + break; + case "Single factor": + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "No" }, + ]; + break; + case "Multi factor": + // Per-user MFA enabled/enforced + filters = [{ id: "AccountEnabled", value: "Yes" }]; + break; + case "Phishable": + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "Yes" }, + ]; + break; + case "Phish resistant": + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "Yes" }, + ]; + break; + default: + return; + } + + router.push({ + pathname: "/identity/reports/mfa-report", + query: { filters: JSON.stringify(filters) }, + }); + }; + + const handleLinkClick = (link) => { + let filters = []; + + if (link.source.id === "Users" && link.target.id === "Single factor") { + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "No" }, + ]; + } else if (link.source.id === "Users" && link.target.id === "Multi factor") { + filters = [{ id: "AccountEnabled", value: "Yes" }]; + } else if (link.source.id === "Users" && link.target.id === "Phishable") { + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "Yes" }, + ]; + } else if (link.source.id === "Users" && link.target.id === "Phish resistant") { + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "Yes" }, + ]; + } else if (link.source.id === "Phishable") { + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "Yes" }, + ]; + } else if (link.source.id === "Phish resistant") { + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "Yes" }, + ]; + } + + if (filters.length > 0) { + router.push({ + pathname: "/identity/reports/mfa-report", + query: { filters: JSON.stringify(filters) }, + }); + } + }; + + return ( + + + + All users auth methods + + } + sx={{ pb: 1 }} + /> + + + {isLoading ? ( + + ) : processedData ? ( + + ) : ( + + + No authentication method data available + + + )} + + + {!isLoading && processedData?.description && ( + + + {processedData.description} + + + )} + + ); +}; diff --git a/src/components/CippComponents/AuthMethodSankey.jsx b/src/components/CippComponents/AuthMethodSankey.jsx index 6ef4e61e666d..f57c42573c52 100644 --- a/src/components/CippComponents/AuthMethodSankey.jsx +++ b/src/components/CippComponents/AuthMethodSankey.jsx @@ -1,45 +1,159 @@ import { CippSankey } from "./CippSankey"; export const AuthMethodSankey = ({ data }) => { + // Null safety checks + if (!data || !Array.isArray(data) || data.length === 0) { + return null; + } + + // Count enabled users only + const enabledUsers = data.filter((user) => user.AccountEnabled === true); + + if (enabledUsers.length === 0) { + return null; + } + + // Categorize MFA methods as phishable or phish-resistant + const phishableMethods = ["mobilePhone", "email", "microsoftAuthenticatorPush"]; + const phishResistantMethods = ["fido2", "windowsHelloForBusiness", "x509Certificate"]; + + let singleFactor = 0; + let phishableCount = 0; + let phishResistantCount = 0; + let perUserMFA = 0; + + // Breakdown of phishable methods + let phoneCount = 0; + let authenticatorCount = 0; + + // Breakdown of phish-resistant methods + let passkeyCount = 0; + let whfbCount = 0; + + enabledUsers.forEach((user) => { + const methods = user.MFAMethods || []; + const perUser = user.PerUser === "enforced" || user.PerUser === "enabled"; + const hasRegistered = user.MFARegistration === true; + + // If user has per-user MFA enforced but no specific methods, count as generic MFA + if (perUser && !hasRegistered && methods.length === 0) { + perUserMFA++; + return; + } + + // Check if user has any MFA methods + if (!hasRegistered || methods.length === 0) { + singleFactor++; + return; + } + + // Categorize by method type + const hasPhishResistant = methods.some((m) => phishResistantMethods.includes(m)); + const hasPhishable = methods.some((m) => phishableMethods.includes(m)); + + if (hasPhishResistant) { + phishResistantCount++; + // Count specific phish-resistant methods + if (methods.includes("fido2") || methods.includes("x509Certificate")) { + passkeyCount++; + } + if (methods.includes("windowsHelloForBusiness")) { + whfbCount++; + } + } else if (hasPhishable) { + phishableCount++; + // Count specific phishable methods + if (methods.includes("mobilePhone") || methods.includes("email")) { + phoneCount++; + } + if ( + methods.includes("microsoftAuthenticatorPush") || + methods.includes("softwareOneTimePasscode") + ) { + authenticatorCount++; + } + } else { + // Has MFA methods but not in our categorized lists + phishableCount++; + authenticatorCount++; + } + }); + + const mfaPercentage = ( + ((phishableCount + phishResistantCount + perUserMFA) / enabledUsers.length) * + 100 + ).toFixed(1); + const phishResistantPercentage = ((phishResistantCount / enabledUsers.length) * 100).toFixed(1); + + const links = [ + { source: "Users", target: "Single factor", value: singleFactor }, + { source: "Users", target: "Multi factor", value: perUserMFA }, + { source: "Users", target: "Phishable", value: phishableCount }, + { source: "Users", target: "Phish resistant", value: phishResistantCount }, + ]; + + // Add phishable method breakdowns + if (phoneCount > 0) links.push({ source: "Phishable", target: "Phone", value: phoneCount }); + if (authenticatorCount > 0) + links.push({ source: "Phishable", target: "Authenticator", value: authenticatorCount }); + + // Add phish-resistant method breakdowns + if (passkeyCount > 0) + links.push({ source: "Phish resistant", target: "Passkey", value: passkeyCount }); + if (whfbCount > 0) links.push({ source: "Phish resistant", target: "WHfB", value: whfbCount }); + + const description = `${mfaPercentage}% of enabled users have MFA configured. ${phishResistantPercentage}% use phish-resistant authentication methods.`; + return ( - + <> + + {description && ( +
+ {description} +
+ )} + ); }; diff --git a/src/components/CippComponents/BPASyncDialog.jsx b/src/components/CippComponents/BPASyncDialog.jsx index 43a16450416a..3add79b46f54 100644 --- a/src/components/CippComponents/BPASyncDialog.jsx +++ b/src/components/CippComponents/BPASyncDialog.jsx @@ -9,7 +9,7 @@ import { import { Sync } from "@mui/icons-material"; import { useForm, FormProvider } from "react-hook-form"; import { CippFormTenantSelector } from "./CippFormTenantSelector"; -import { ApiPostCall } from "/src/api/ApiCall"; +import { ApiPostCall } from "../../api/ApiCall"; import { CippApiResults } from "./CippApiResults"; export const BPASyncDialog = ({ createDialog }) => { diff --git a/src/components/CippComponents/BreachSearchDialog.jsx b/src/components/CippComponents/BreachSearchDialog.jsx index e089908ffb43..8a69cd40206c 100644 --- a/src/components/CippComponents/BreachSearchDialog.jsx +++ b/src/components/CippComponents/BreachSearchDialog.jsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { Dialog, DialogContent, DialogTitle, Button, DialogActions } from "@mui/material"; import { Search } from "@mui/icons-material"; import { useForm, FormProvider } from "react-hook-form"; -import { ApiPostCall } from "/src/api/ApiCall"; +import { ApiPostCall } from "../../api/ApiCall"; import { CippApiResults } from "./CippApiResults"; import { useSettings } from "../../hooks/use-settings"; diff --git a/src/components/CippComponents/CIPPM365OAuthButton.jsx b/src/components/CippComponents/CIPPM365OAuthButton.jsx index 88e517a2139d..0818190ca1cc 100644 --- a/src/components/CippComponents/CIPPM365OAuthButton.jsx +++ b/src/components/CippComponents/CIPPM365OAuthButton.jsx @@ -1,7 +1,9 @@ import { useState, useEffect } from "react"; import { Alert, Button, Typography, CircularProgress, Box } from "@mui/material"; +import { Microsoft, Login, Refresh } from "@mui/icons-material"; import { ApiGetCall } from "../../api/ApiCall"; import { CippCopyToClipBoard } from "./CippCopyToClipboard"; +import { CippApiDialog } from "./CippApiDialog"; export const CIPPM365OAuthButton = ({ onAuthSuccess, @@ -14,12 +16,14 @@ export const CIPPM365OAuthButton = ({ applicationId = null, autoStartDeviceLogon = false, validateServiceAccount = true, + promptBeforeAuth = false, }) => { const [authInProgress, setAuthInProgress] = useState(false); const [authError, setAuthError] = useState(null); const [deviceCodeInfo, setDeviceCodeInfo] = useState(null); const [codeRetrievalInProgress, setCodeRetrievalInProgress] = useState(false); const [isServiceAccount, setIsServiceAccount] = useState(true); + const [promptDialog, setPromptDialog] = useState({ open: false }); const [tokens, setTokens] = useState({ accessToken: null, refreshToken: null, @@ -32,13 +36,10 @@ export const CIPPM365OAuthButton = ({ const appIdInfo = ApiGetCall({ url: `/api/ExecListAppId`, + queryKey: "listAppId", waiting: true, }); - useEffect(() => { - appIdInfo.refetch(); - }, []); - const handleCloseError = () => { setAuthError(null); }; @@ -55,8 +56,10 @@ export const CIPPM365OAuthButton = ({ setCodeRetrievalInProgress(true); setAuthError(null); - // Refetch appId to ensure we have the latest - await appIdInfo.refetch(); + // Only refetch appId if not already present + if (!applicationId && !appIdInfo?.data?.applicationId) { + await appIdInfo.refetch(); + } try { // Get the application ID to use @@ -66,8 +69,8 @@ export const CIPPM365OAuthButton = ({ // Request device code from our API endpoint const deviceCodeResponse = await fetch( `/api/ExecDeviceCodeLogon?operation=getDeviceCode&clientId=${appId}&scope=${encodeURIComponent( - scope - )}` + scope, + )}`, ); const deviceCodeData = await deviceCodeResponse.json(); @@ -95,8 +98,10 @@ export const CIPPM365OAuthButton = ({ // Device code authentication function - opens popup and starts polling const handleDeviceCodeAuthentication = async () => { - // Refetch appId to ensure we have the latest - await appIdInfo.refetch(); + // Only refetch appId if not already present + if (!applicationId && !appIdInfo?.data?.applicationId) { + await appIdInfo.refetch(); + } if (!deviceCodeInfo) { // If we don't have a device code yet, retrieve it first @@ -129,7 +134,7 @@ export const CIPPM365OAuthButton = ({ const popup = window.open( "https://microsoft.com/devicelogin", "deviceLoginPopup", - `width=${width},height=${height},left=${left},top=${top}` + `width=${width},height=${height},left=${left},top=${top}`, ); // Start polling for token @@ -155,7 +160,7 @@ export const CIPPM365OAuthButton = ({ try { // Poll for token using our API endpoint const tokenResponse = await fetch( - `/api/ExecDeviceCodeLogon?operation=checkToken&clientId=${appId}&deviceCode=${deviceCodeInfo.device_code}` + `/api/ExecDeviceCodeLogon?operation=checkToken&clientId=${appId}&deviceCode=${deviceCodeInfo.device_code}`, ); const tokenData = await tokenResponse.json(); @@ -263,7 +268,9 @@ export const CIPPM365OAuthButton = ({ }; // MSAL-like authentication function - const handleMsalAuthentication = async () => { + const handleMsalAuthentication = async (retryCount = 0) => { + const maxRetries = 3; + // Clear previous authentication state when starting a new authentication setAuthInProgress(true); setAuthError(null); @@ -277,10 +284,12 @@ export const CIPPM365OAuthButton = ({ onmicrosoftDomain: null, }); - // Refetch app ID info to ensure we have the latest - await appIdInfo.refetch(); + // Only refetch app ID if not already present + if (!applicationId && !appIdInfo?.data?.applicationId) { + await appIdInfo.refetch(); + } - // Get the application ID to use - now we're sure to have the latest after the await + // Get the application ID to use const appId = applicationId || appIdInfo?.data?.applicationId; // Generate MSAL-like authentication parameters @@ -327,7 +336,7 @@ export const CIPPM365OAuthButton = ({ const popup = window.open( authUrl, "msalAuthPopup", - `width=${width},height=${height},left=${left},top=${top}` + `width=${width},height=${height},left=${left},top=${top}`, ); // Function to actually exchange the authorization code for tokens @@ -356,20 +365,43 @@ export const CIPPM365OAuthButton = ({ }; // Make the token request through our API proxy to avoid origin header issues - const tokenResponse = await fetch(`/api/ExecTokenExchange`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - tokenRequest, - tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token", - tenantId: appId, // Pass the tenant ID to retrieve the correct client secret - }), - }); + // Retry logic for AADSTS650051 (service principal already exists) + let retryCount = 0; + const maxRetries = 3; + let tokenResponse; + let tokenData; + + while (retryCount <= maxRetries) { + tokenResponse = await fetch(`/api/ExecTokenExchange`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + tokenRequest, + tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token", + tenantId: appId, // Pass the tenant ID to retrieve the correct client secret + }), + }); - // Parse the token response - const tokenData = await tokenResponse.json(); + // Parse the token response + tokenData = await tokenResponse.json(); + + // Check if it's the AADSTS650051 error (service principal already exists) + if ( + tokenData.error === "invalid_client" && + tokenData.error_description?.includes("AADSTS650051") + ) { + retryCount++; + if (retryCount <= maxRetries) { + // Wait before retrying (exponential backoff) + await new Promise((resolve) => setTimeout(resolve, 2000 * retryCount)); + continue; + } + } + // If no error or different error, break out of retry loop + break; + } // Check if the response contains an error if (tokenData.error) { @@ -408,6 +440,9 @@ export const CIPPM365OAuthButton = ({ if (!refreshResponse.ok) { console.warn("Failed to store refresh token, but continuing with authentication"); + } else { + // Invalidate the listAppId and tenants-table queryKeys to refresh data + appIdInfo.refetch(); } } catch (error) { console.error("Failed to store refresh token:", error); @@ -502,7 +537,27 @@ export const CIPPM365OAuthButton = ({ const errorCode = urlParams.get("error"); const errorDescription = urlParams.get("error_description"); - // Set the error state + // Check if it's the AADSTS650051 error (service principal already exists during consent) + if ( + errorCode === "invalid_client" && + errorDescription?.includes("AADSTS650051") && + retryCount < maxRetries + ) { + // Close the popup + popup.close(); + setAuthInProgress(false); + + // Wait before retrying (exponential backoff) + setTimeout( + () => { + handleMsalAuthentication(retryCount + 1); + }, + 2000 * (retryCount + 1), + ); + return; + } + + // Set the error state for non-retryable errors const error = { errorCode: errorCode, errorMessage: errorDescription || "Unknown authentication error", @@ -550,9 +605,9 @@ export const CIPPM365OAuthButton = ({
{!applicationId && !appIdInfo.isLoading && - appIdInfo?.data && // Only check if data is available + appIdInfo?.data?.applicationId && // Only check if applicationId is present in data !/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test( - appIdInfo?.data?.applicationId + appIdInfo?.data?.applicationId, ) && ( The Application ID is not valid. Please check your configuration. @@ -653,6 +708,30 @@ export const CIPPM365OAuthButton = ({ ) : null} )} + + {promptBeforeAuth !== false && ( + setPromptDialog({ open: false }), + }} + api={{ + type: "POST", + confirmText: promptBeforeAuth, + noConfirm: false, + customFunction: () => { + setPromptDialog({ open: false }); + const authFunction = useDeviceCode + ? handleDeviceCodeAuthentication + : handleMsalAuthentication; + authFunction(); + }, + }} + fields={[]} + /> + )} +
); diff --git a/src/components/CippComponents/CaSankey.jsx b/src/components/CippComponents/CaSankey.jsx index 5b860e45dda5..ffad546d8738 100644 --- a/src/components/CippComponents/CaSankey.jsx +++ b/src/components/CippComponents/CaSankey.jsx @@ -6,24 +6,32 @@ export const CaSankey = ({ data }) => { data={{ nodes: [ { - id: "User sign in", + id: "Enabled users", nodeColor: "hsl(28, 100%, 53%)", }, { - id: "No CA applied", - nodeColor: "hsl(0, 100%, 50%)", + id: "MFA registered", + nodeColor: "hsl(99, 70%, 50%)", }, { - id: "CA applied", - nodeColor: "hsl(12, 76%, 61%)", + id: "Not registered", + nodeColor: "hsl(39, 100%, 50%)", }, { - id: "No MFA", - nodeColor: "hsl(0, 69%, 50%)", + id: "CA policy", + nodeColor: "hsl(99, 70%, 50%)", }, { - id: "MFA", - nodeColor: "hsl(99, 70%, 50%)", + id: "Security defaults", + nodeColor: "hsl(140, 70%, 50%)", + }, + { + id: "Per-user MFA", + nodeColor: "hsl(200, 70%, 50%)", + }, + { + id: "No enforcement", + nodeColor: "hsl(0, 100%, 50%)", }, ], links: data, diff --git a/src/components/CippComponents/CippAddEditTenantGroups.jsx b/src/components/CippComponents/CippAddEditTenantGroups.jsx index 9ba637d003c8..4208c18eaf7c 100644 --- a/src/components/CippComponents/CippAddEditTenantGroups.jsx +++ b/src/components/CippComponents/CippAddEditTenantGroups.jsx @@ -1,4 +1,4 @@ -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "./CippFormComponent"; import { Typography } from "@mui/material"; import { Grid } from "@mui/system"; import { CippFormTenantSelector } from "./CippFormTenantSelector"; diff --git a/src/components/CippComponents/CippAddTestReportDrawer.jsx b/src/components/CippComponents/CippAddTestReportDrawer.jsx new file mode 100644 index 000000000000..6ea7d84ea0bb --- /dev/null +++ b/src/components/CippComponents/CippAddTestReportDrawer.jsx @@ -0,0 +1,385 @@ +import React, { useState, useEffect } from "react"; +import { + Button, + Card, + CardContent, + TextField, + Typography, + Box, + Chip, + Tab, + Tabs, + Paper, + Stack, +} from "@mui/material"; +import { Grid } from "@mui/system"; +import { useForm, useFormState, useWatch } from "react-hook-form"; +import { Add } from "@mui/icons-material"; +import { CippOffCanvas } from "./CippOffCanvas"; +import CippFormComponent from "./CippFormComponent"; +import { CippApiResults } from "./CippApiResults"; +import { ApiPostCall, ApiGetCall } from "../../api/ApiCall"; + +export const CippAddTestReportDrawer = ({ buttonText = "Create custom report" }) => { + const [drawerVisible, setDrawerVisible] = useState(false); + const [activeTab, setActiveTab] = useState(0); + const [searchTerm, setSearchTerm] = useState(""); + + const formControl = useForm({ + mode: "onChange", + defaultValues: { + name: "", + description: "", + IdentityTests: [], + DevicesTests: [], + }, + }); + + const { isValid } = useFormState({ control: formControl.control }); + const selectedIdentityTests = + useWatch({ control: formControl.control, name: "IdentityTests" }) || []; + const selectedDeviceTests = + useWatch({ control: formControl.control, name: "DevicesTests" }) || []; + + const createReport = ApiPostCall({ + urlFromData: true, + relatedQueryKeys: "ListTestReports", + }); + + // Fetch available tests for the form + const availableTestsApi = ApiGetCall({ + url: "/api/ListAvailableTests", + queryKey: "ListAvailableTests", + }); + + const availableTests = availableTestsApi.data || { IdentityTests: [], DevicesTests: [] }; + + // Reset form fields on successful creation + useEffect(() => { + if (createReport.isSuccess) { + formControl.reset({ + name: "", + description: "", + IdentityTests: [], + DevicesTests: [], + }); + } + }, [createReport.isSuccess, formControl]); + + const handleSubmit = () => { + formControl.trigger(); + if (!isValid) { + return; + } + + const values = formControl.getValues(); + Object.keys(values).forEach((key) => { + if (values[key] === "" || values[key] === null) { + delete values[key]; + } + }); + + createReport.mutate({ + url: "/api/AddTestReport", + data: values, + }); + }; + + const handleCloseDrawer = () => { + setDrawerVisible(false); + setSearchTerm(""); + setActiveTab(0); + formControl.reset({ + name: "", + description: "", + IdentityTests: [], + DevicesTests: [], + }); + }; + + const toggleTest = (testId, testType) => { + const fieldName = testType === "Identity" ? "IdentityTests" : "DevicesTests"; + const currentTests = formControl.getValues(fieldName) || []; + + if (currentTests.includes(testId)) { + formControl.setValue( + fieldName, + currentTests.filter((id) => id !== testId), + { shouldValidate: true } + ); + } else { + formControl.setValue(fieldName, [...currentTests, testId], { shouldValidate: true }); + } + }; + + const isTestSelected = (testId, testType) => { + return testType === "Identity" + ? selectedIdentityTests.includes(testId) + : selectedDeviceTests.includes(testId); + }; + + const filterTests = (tests) => { + if (!searchTerm) return tests; + return tests.filter( + (test) => + test.id.toLowerCase().includes(searchTerm.toLowerCase()) || + test.name.toLowerCase().includes(searchTerm.toLowerCase()) + ); + }; + + const currentTests = + activeTab === 0 + ? filterTests(availableTests.IdentityTests || []) + : filterTests(availableTests.DevicesTests || []); + + const currentTestType = activeTab === 0 ? "Identity" : "Devices"; + + return ( + <> + + + +
+ + +
+ + } + > + + {/* Report Details Section */} + + + + Report Details + + + + + + + + + + + + + {/* Selection Summary */} + + + + + Selected Tests: + + + + + + Total: {selectedIdentityTests.length + selectedDeviceTests.length} tests + + + + + + {/* Test Selection Section */} + + + + { + setActiveTab(newValue); + setSearchTerm(""); + }} + variant="fullWidth" + > + + Identity Tests + {selectedIdentityTests.length > 0 && ( + + )} + + } + /> + + Device Tests + {selectedDeviceTests.length > 0 && ( + + )} + + } + /> + + + + {/* Search Bar */} + + setSearchTerm(e.target.value)} + /> + + + {/* Test List */} + + {availableTestsApi.isFetching ? ( + + Loading tests... + + ) : currentTests.length === 0 ? ( + + + {searchTerm ? "No tests found matching your search" : "No tests available"} + + + ) : ( + + {currentTests.map((test) => { + const isSelected = isTestSelected(test.id, currentTestType); + return ( + + toggleTest(test.id, currentTestType)} + > + + + + + + + {test.name} + + + {test.description && ( + + {test.description} + + )} + + + + + + ); + })} + + )} + + + + +
+ + ); +}; diff --git a/src/components/CippComponents/CippAddVacationModeDrawer.jsx b/src/components/CippComponents/CippAddVacationModeDrawer.jsx index 9434b456547e..bd6b4397d590 100644 --- a/src/components/CippComponents/CippAddVacationModeDrawer.jsx +++ b/src/components/CippComponents/CippAddVacationModeDrawer.jsx @@ -9,7 +9,7 @@ import { CippApiResults } from "./CippApiResults"; import { CippFormUserSelector } from "./CippFormUserSelector"; import { CippFormTenantSelector } from "./CippFormTenantSelector"; import { ApiPostCall, ApiGetCallWithPagination } from "../../api/ApiCall"; -import CippJsonView from "/src/components/CippFormPages/CippJSONView"; +import CippJsonView from "../CippFormPages/CippJSONView"; export const CippAddVacationModeDrawer = ({ buttonText = "Add Vacation Schedule", diff --git a/src/components/CippComponents/CippApiDialog.jsx b/src/components/CippComponents/CippApiDialog.jsx index 39f590570dd0..3748e4005376 100644 --- a/src/components/CippComponents/CippApiDialog.jsx +++ b/src/components/CippComponents/CippApiDialog.jsx @@ -364,6 +364,7 @@ export const CippApiDialog = (props) => { formControl={formHook} addedFieldData={addedFieldData} setAddedFieldData={setAddedFieldData} + row={row} {...fieldProps} /> diff --git a/src/components/CippComponents/CippAppPermissionBuilder.jsx b/src/components/CippComponents/CippAppPermissionBuilder.jsx index 46adfc153008..3ff6635eec32 100644 --- a/src/components/CippComponents/CippAppPermissionBuilder.jsx +++ b/src/components/CippComponents/CippAppPermissionBuilder.jsx @@ -17,7 +17,7 @@ import { Tab, } from "@mui/material"; import { Grid } from "@mui/system"; -import { ApiGetCall, ApiPostCall } from "/src/api/ApiCall"; +import { ApiGetCall, ApiPostCall } from "../../api/ApiCall"; import { CippDataTable } from "../CippTable/CippDataTable"; import { PlusIcon, ShieldCheckIcon, WrenchIcon } from "@heroicons/react/24/outline"; import CippFormComponent from "./CippFormComponent"; diff --git a/src/components/CippComponents/CippApplicationDeployDrawer.jsx b/src/components/CippComponents/CippApplicationDeployDrawer.jsx index 16384787e6ec..6b51f4889e57 100644 --- a/src/components/CippComponents/CippApplicationDeployDrawer.jsx +++ b/src/components/CippComponents/CippApplicationDeployDrawer.jsx @@ -8,7 +8,7 @@ import CippFormComponent from "./CippFormComponent"; import { CippFormTenantSelector } from "./CippFormTenantSelector"; import { CippFormCondition } from "./CippFormCondition"; import { CippApiResults } from "./CippApiResults"; -import languageList from "/src/data/languageList.json"; +import languageList from "../../data/languageList.json"; import { ApiPostCall } from "../../api/ApiCall"; export const CippApplicationDeployDrawer = ({ diff --git a/src/components/CippComponents/CippAuditLogDetails.jsx b/src/components/CippComponents/CippAuditLogDetails.jsx index b5a3077a0da6..44a291245cb7 100644 --- a/src/components/CippComponents/CippAuditLogDetails.jsx +++ b/src/components/CippComponents/CippAuditLogDetails.jsx @@ -1,10 +1,10 @@ import { useEffect } from "react"; -import { getCippTranslation } from "/src/utils/get-cipp-translation"; -import { getCippFormatting } from "/src/utils/get-cipp-formatting"; -import CippGeoLocation from "/src/components/CippComponents/CippGeoLocation"; +import { getCippTranslation } from "../../utils/get-cipp-translation"; +import { getCippFormatting } from "../../utils/get-cipp-formatting"; +import CippGeoLocation from "./CippGeoLocation"; import { Tooltip, CircularProgress, Stack } from "@mui/material"; -import { useGuidResolver } from "/src/hooks/use-guid-resolver"; -import { CippPropertyListCard } from "/src/components/CippCards/CippPropertyListCard"; +import { useGuidResolver } from "../../hooks/use-guid-resolver"; +import { CippPropertyListCard } from "../CippCards/CippPropertyListCard"; const CippAuditLogDetails = ({ row }) => { const { diff --git a/src/components/CippComponents/CippAuditLogSearchDrawer.jsx b/src/components/CippComponents/CippAuditLogSearchDrawer.jsx index 93628842b386..9c3179f0490d 100644 --- a/src/components/CippComponents/CippAuditLogSearchDrawer.jsx +++ b/src/components/CippComponents/CippAuditLogSearchDrawer.jsx @@ -6,7 +6,7 @@ import { CippOffCanvas } from "./CippOffCanvas"; import { ApiPostCall, ApiGetCallWithPagination } from "../../api/ApiCall"; import CippFormComponent from "./CippFormComponent"; import { CippApiResults } from "./CippApiResults"; -import { useSettings } from "/src/hooks/use-settings"; +import { useSettings } from "../../hooks/use-settings"; export const CippAuditLogSearchDrawer = ({ buttonText = "New Search", diff --git a/src/components/CippComponents/CippAutocomplete.jsx b/src/components/CippComponents/CippAutocomplete.jsx index daaa6dda867a..ea5d9811bda8 100644 --- a/src/components/CippComponents/CippAutocomplete.jsx +++ b/src/components/CippComponents/CippAutocomplete.jsx @@ -6,6 +6,8 @@ import { TextField, IconButton, Tooltip, + Box, + Typography, } from "@mui/material"; import { useEffect, useState, useMemo, useCallback, useRef } from "react"; import { useSettings } from "../../hooks/use-settings"; @@ -189,6 +191,12 @@ export const CippAutoComplete = (props) => { typeof api?.valueField === "function" ? api.valueField(option) : option[api?.valueField], + description: + typeof api?.descriptionField === "function" + ? api.descriptionField(option) + : api?.descriptionField + ? option[api?.descriptionField] + : undefined, addedFields, rawData: option, // Store the full original object }; @@ -545,6 +553,21 @@ export const CippAutoComplete = (props) => { )} groupBy={groupBy} renderGroup={renderGroup} + renderOption={(props, option) => { + const { key, ...optionProps } = props; + return ( + + + {option.label} + {option.description && ( + + {option.description} + + )} + + + ); + }} {...other} /> {api?.templateView && ( diff --git a/src/components/CippComponents/CippAutopilotProfileDrawer.jsx b/src/components/CippComponents/CippAutopilotProfileDrawer.jsx index f5b48ae54040..7edc95b46b60 100644 --- a/src/components/CippComponents/CippAutopilotProfileDrawer.jsx +++ b/src/components/CippComponents/CippAutopilotProfileDrawer.jsx @@ -7,7 +7,7 @@ import { CippOffCanvas } from "./CippOffCanvas"; import CippFormComponent from "./CippFormComponent"; import { CippFormTenantSelector } from "./CippFormTenantSelector"; import { CippApiResults } from "./CippApiResults"; -import languageList from "/src/data/languageList.json"; +import languageList from "../../data/languageList.json"; import { ApiPostCall } from "../../api/ApiCall"; export const CippAutopilotProfileDrawer = ({ diff --git a/src/components/CippComponents/CippBreadcrumbNav.jsx b/src/components/CippComponents/CippBreadcrumbNav.jsx index ff146073ae1c..98093aa34974 100644 --- a/src/components/CippComponents/CippBreadcrumbNav.jsx +++ b/src/components/CippComponents/CippBreadcrumbNav.jsx @@ -32,7 +32,7 @@ async function loadTabOptions() { for (const basePath of tabOptionPaths) { try { - const module = await import(`/src/pages${basePath}/tabOptions.json`); + const module = await import(`../../pages${basePath}/tabOptions.json`); const options = module.default || module; // Add each tab option with metadata diff --git a/src/components/CippComponents/CippBulkUserDrawer.jsx b/src/components/CippComponents/CippBulkUserDrawer.jsx index 7718ae957138..6fd62b106c38 100644 --- a/src/components/CippComponents/CippBulkUserDrawer.jsx +++ b/src/components/CippComponents/CippBulkUserDrawer.jsx @@ -11,7 +11,7 @@ import { CippApiResults } from "./CippApiResults"; import { useSettings } from "../../hooks/use-settings"; import { ApiPostCall } from "../../api/ApiCall"; import { getCippTranslation } from "../../utils/get-cipp-translation"; -import countryList from "/src/data/countryList.json"; +import countryList from "../../data/countryList.json"; export const CippBulkUserDrawer = ({ buttonText = "Bulk Add Users", diff --git a/src/components/CippComponents/CippCentralSearch.jsx b/src/components/CippComponents/CippCentralSearch.jsx index 0c7bf858e88b..2f0252fcaf11 100644 --- a/src/components/CippComponents/CippCentralSearch.jsx +++ b/src/components/CippComponents/CippCentralSearch.jsx @@ -14,8 +14,8 @@ import { } from "@mui/material"; import { Grid } from "@mui/system"; import { useRouter } from "next/router"; -import { nativeMenuItems } from "/src/layouts/config"; -import { usePermissions } from "/src/hooks/use-permissions"; +import { nativeMenuItems } from "../../layouts/config"; +import { usePermissions } from "../../hooks/use-permissions"; /** * Recursively collects only leaf items (those without sub-items). @@ -62,7 +62,7 @@ async function loadTabOptions() { for (const basePath of tabOptionPaths) { try { - const module = await import(`/src/pages${basePath}/tabOptions.json`); + const module = await import(`../../pages${basePath}/tabOptions.json`); const options = module.default || module; // Add each tab option with metadata @@ -172,7 +172,7 @@ export const CippCentralSearch = ({ handleClose, open }) => { const filteredMainMenu = filterItemsByPermissionsAndRoles( allLeafItems, userPermissions, - userRoles + userRoles, ).map((item) => { const rawBreadcrumbs = buildBreadcrumbPath(nativeMenuItems, item.path) || []; // Remove the leaf item's own title to avoid duplicate when rendering @@ -250,7 +250,7 @@ export const CippCentralSearch = ({ handleClose, open }) => { const inTitle = leaf.title?.toLowerCase().includes(normalizedSearch); const inPath = leaf.path?.toLowerCase().includes(normalizedSearch); const inBreadcrumbs = leaf.breadcrumbs?.some((crumb) => - crumb?.toLowerCase().includes(normalizedSearch) + crumb?.toLowerCase().includes(normalizedSearch), ); // If there's no search value, show no results (you could change this logic) return normalizedSearch ? inTitle || inPath || inBreadcrumbs : false; @@ -267,7 +267,7 @@ export const CippCentralSearch = ({ handleClose, open }) => { ) : ( part - ) + ), ); }; diff --git a/src/components/CippComponents/CippCustomVariables.jsx b/src/components/CippComponents/CippCustomVariables.jsx index b408deaae8ea..69b5975d1777 100644 --- a/src/components/CippComponents/CippCustomVariables.jsx +++ b/src/components/CippComponents/CippCustomVariables.jsx @@ -1,10 +1,10 @@ import { useState } from "react"; import { CardContent, Button, SvgIcon, Alert } from "@mui/material"; import { PlusIcon, TrashIcon, PencilIcon } from "@heroicons/react/24/outline"; -import { CippDataTable } from "/src/components/CippTable/CippDataTable"; -import { CippApiResults } from "/src/components/CippComponents/CippApiResults"; -import { CippApiDialog } from "/src/components/CippComponents/CippApiDialog"; -import { ApiPostCall } from "/src/api/ApiCall"; +import { CippDataTable } from "../CippTable/CippDataTable"; +import { CippApiResults } from "./CippApiResults"; +import { CippApiDialog } from "./CippApiDialog"; +import { ApiPostCall } from "../../api/ApiCall"; const CippCustomVariables = ({ id }) => { const [openAddDialog, setOpenAddDialog] = useState(false); diff --git a/src/components/CippComponents/CippExchangeActions.jsx b/src/components/CippComponents/CippExchangeActions.jsx index e88171a3f20f..a783b263a339 100644 --- a/src/components/CippComponents/CippExchangeActions.jsx +++ b/src/components/CippComponents/CippExchangeActions.jsx @@ -17,7 +17,7 @@ import { PersonAdd, Email, } from "@mui/icons-material"; -import { useSettings } from "/src/hooks/use-settings.js"; +import { useSettings } from "../../hooks/use-settings.js"; import { useMemo } from "react"; export const CippExchangeActions = () => { diff --git a/src/components/CippComponents/CippFormComponent.jsx b/src/components/CippComponents/CippFormComponent.jsx index 5e76c4152ad1..0ab4bbfc2164 100644 --- a/src/components/CippComponents/CippFormComponent.jsx +++ b/src/components/CippComponents/CippFormComponent.jsx @@ -56,6 +56,7 @@ export const CippFormComponent = (props) => { helperText, disableVariables = false, includeSystemVariables = false, + row, ...other } = props; const { errors } = useFormState({ control: formControl.control }); @@ -408,16 +409,32 @@ export const CippFormComponent = (props) => { ); - case "autoComplete": + case "autoComplete": { + // Resolve options if it's a function + const resolvedOptions = + typeof other.options === "function" ? other.options(row) : other.options; + + // Wrap validate function to pass row as third parameter + const resolvedValidators = validators + ? { + ...validators, + validate: + typeof validators.validate === "function" + ? (value, formValues) => validators.validate(value, formValues, row) + : validators.validate, + } + : validators; + return (
( { )}
); + } case "richText": { const editorInstanceRef = React.useRef(null); diff --git a/src/components/CippComponents/CippFormTenantSelector.jsx b/src/components/CippComponents/CippFormTenantSelector.jsx index 0ce271515d29..4f39e48868d4 100644 --- a/src/components/CippComponents/CippFormTenantSelector.jsx +++ b/src/components/CippComponents/CippFormTenantSelector.jsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import { CippFormComponent } from "./CippFormComponent"; import { useSettings } from "../../hooks/use-settings"; import { GroupHeader, GroupItems } from "../CippComponents/CippAutocompleteGrouping"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../api/ApiCall"; export const CippFormTenantSelector = ({ formControl, diff --git a/src/components/CippComponents/CippForwardingSection.jsx b/src/components/CippComponents/CippForwardingSection.jsx index df7fcba9b177..24a18345ddc5 100644 --- a/src/components/CippComponents/CippForwardingSection.jsx +++ b/src/components/CippComponents/CippForwardingSection.jsx @@ -3,7 +3,7 @@ import CippFormComponent from "./CippFormComponent"; import { CippFormCondition } from "./CippFormCondition"; import { Grid } from "@mui/system"; import { CippApiResults } from "./CippApiResults"; -import { getCippValidator } from "/src/utils/get-cipp-validator"; +import { getCippValidator } from "../../utils/get-cipp-validator"; const CippForwardingSection = ({ formControl, usersList, contactsList, postRequest, handleSubmit }) => { diff --git a/src/components/CippComponents/CippGeoLocation.jsx b/src/components/CippComponents/CippGeoLocation.jsx index e7a4be63ed66..1621f602ba1d 100644 --- a/src/components/CippComponents/CippGeoLocation.jsx +++ b/src/components/CippComponents/CippGeoLocation.jsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import { Skeleton } from "@mui/material"; import { Grid } from "@mui/system"; import dynamic from "next/dynamic"; -import { ApiPostCall } from "/src/api/ApiCall"; +import { ApiPostCall } from "../../api/ApiCall"; import { CippPropertyList } from "./CippPropertyList"; import { getCippTranslation } from "../../utils/get-cipp-translation"; import { getCippFormatting } from "../../utils/get-cipp-formatting"; diff --git a/src/components/CippComponents/CippMap.jsx b/src/components/CippComponents/CippMap.jsx index 7f5be9bb4b92..5efed559ef70 100644 --- a/src/components/CippComponents/CippMap.jsx +++ b/src/components/CippComponents/CippMap.jsx @@ -1,11 +1,18 @@ import "leaflet/dist/leaflet.css"; import "react-leaflet-markercluster/styles"; -import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.webpack.css"; -import "leaflet-defaulticon-compatibility"; import { useEffect, useRef } from "react"; import { MapContainer, Marker, Popup, TileLayer } from "react-leaflet"; +import L from "leaflet"; import MarkerClusterGroup from "react-leaflet-markercluster"; +// Fix leaflet icon paths for Turbopack/Next.js +delete L.Icon.Default.prototype._getIconUrl; +L.Icon.Default.mergeOptions({ + iconRetinaUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png", + iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png", + shadowUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png", +}); + export default function CippMap({ markers = [], zoom = 11, diff --git a/src/components/CippComponents/CippMessageViewer.jsx b/src/components/CippComponents/CippMessageViewer.jsx index 366c17dcc507..e91530db384b 100644 --- a/src/components/CippComponents/CippMessageViewer.jsx +++ b/src/components/CippComponents/CippMessageViewer.jsx @@ -43,7 +43,7 @@ import { CippTimeAgo } from "./CippTimeAgo"; import { CippCodeBlock } from "./CippCodeBlock"; import DOMPurify from "dompurify"; import ReactHtmlParser from "react-html-parser"; -import { FileDropzone } from "/src/components/file-dropzone.js"; +import { FileDropzone } from "../file-dropzone.js"; import CippPageCard from "../CippCards/CippPageCard"; import { MoonIcon, @@ -51,7 +51,7 @@ import { ShieldExclamationIcon, SunIcon, } from "@heroicons/react/24/outline"; -import { useSettings } from "/src/hooks/use-settings"; +import { useSettings } from "../../hooks/use-settings"; import CippForefrontHeaderDialog from "./CippForefrontHeaderDialog"; export const CippMessageViewer = ({ emailSource }) => { diff --git a/src/components/CippComponents/CippOffCanvas.jsx b/src/components/CippComponents/CippOffCanvas.jsx index 25b05ed69a28..b8e5b548e94d 100644 --- a/src/components/CippComponents/CippOffCanvas.jsx +++ b/src/components/CippComponents/CippOffCanvas.jsx @@ -4,6 +4,8 @@ import { getCippTranslation } from "../../utils/get-cipp-translation"; import { getCippFormatting } from "../../utils/get-cipp-formatting"; import { useMediaQuery, Grid } from "@mui/system"; import CloseIcon from "@mui/icons-material/Close"; +import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; +import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; export const CippOffCanvas = (props) => { const { @@ -17,6 +19,10 @@ export const CippOffCanvas = (props) => { children, size = "sm", footer, + onNavigateUp, + onNavigateDown, + canNavigateUp = false, + canNavigateDown = false, } = props; const mdDown = useMediaQuery((theme) => theme.breakpoints.down("md")); @@ -84,9 +90,31 @@ export const CippOffCanvas = (props) => { sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", p: 1.5 }} > {title} - - - + + {(canNavigateUp || canNavigateDown) && ( + <> + + + + + + + + )} + + + + { +export const CippSankey = ({ data, onNodeClick, onLinkClick }) => { const settings = useSettings(); const isDark = settings.currentTheme?.value === "dark"; @@ -27,7 +27,11 @@ export const CippSankey = ({ data }) => { return (
{ sort="input" legends={[]} valueFormat={(value) => `${value}`} + isInteractive={true} + onClick={(node, event) => { + if (onNodeClick && node.id) { + onNodeClick(node); + } else if (onLinkClick && node.source) { + onLinkClick(node); + } + }} />
); diff --git a/src/components/CippComponents/CippTablePage.jsx b/src/components/CippComponents/CippTablePage.jsx index 60b6fc127b19..b95db81b5a0c 100644 --- a/src/components/CippComponents/CippTablePage.jsx +++ b/src/components/CippComponents/CippTablePage.jsx @@ -3,7 +3,7 @@ import { Box, Container, Stack } from "@mui/system"; import { CippDataTable } from "../CippTable/CippDataTable"; import { useSettings } from "../../hooks/use-settings"; import { CippHead } from "./CippHead"; -import { useState } from "react"; +import { useState, useEffect } from "react"; export const CippTablePage = (props) => { const { @@ -24,11 +24,14 @@ export const CippTablePage = (props) => { tableFilter, tenantInTitle = true, filters, - sx = { flexGrow: 1, pb: 4 }, + initialFilters, + sx = {}, ...other } = props; const tenant = useSettings().currentTenant; - const [tableFilters] = useState(filters || []); + + // Use initialFilters if provided, otherwise use regular filters + const activeFilters = initialFilters || filters; return ( <> @@ -63,15 +66,7 @@ export const CippTablePage = (props) => { columns={columns} columnsFromApi={columnsFromApi} offCanvas={offCanvas} - filters={tableFilters} - initialState={{ - columnFilters: filters - ? filters.map((filter) => ({ - id: filter.id || filter.columnId, - value: filter.value, - })) - : [], - }} + filters={activeFilters} {...other} /> diff --git a/src/components/CippComponents/CippTemplateEditor.jsx b/src/components/CippComponents/CippTemplateEditor.jsx index e223abf49962..bb2c38af8b53 100644 --- a/src/components/CippComponents/CippTemplateEditor.jsx +++ b/src/components/CippComponents/CippTemplateEditor.jsx @@ -2,11 +2,11 @@ import React, { useEffect, useState } from "react"; import { Box, Typography, Divider } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm } from "react-hook-form"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; -import { ApiGetCall } from "/src/api/ApiCall"; -import { getCippTranslation } from "/src/utils/get-cipp-translation"; +import CippFormPage from "../CippFormPages/CippFormPage"; +import CippFormComponent from "./CippFormComponent"; +import CippFormSkeleton from "../CippFormPages/CippFormSkeleton"; +import { ApiGetCall } from "../../api/ApiCall"; +import { getCippTranslation } from "../../utils/get-cipp-translation"; const CippTemplateEditor = ({ templateId, diff --git a/src/components/CippComponents/CippTemplateFieldRenderer.jsx b/src/components/CippComponents/CippTemplateFieldRenderer.jsx index 7fae93ec413f..5f385bfdda64 100644 --- a/src/components/CippComponents/CippTemplateFieldRenderer.jsx +++ b/src/components/CippComponents/CippTemplateFieldRenderer.jsx @@ -1,9 +1,9 @@ import React from "react"; import { Typography, Divider } from "@mui/material"; import { Grid } from "@mui/system"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { getCippTranslation } from "/src/utils/get-cipp-translation"; -import intuneCollection from "/src/data/intuneCollection.json"; +import CippFormComponent from "./CippFormComponent"; +import { getCippTranslation } from "../../utils/get-cipp-translation"; +import intuneCollection from "../../data/intuneCollection.json"; const CippTemplateFieldRenderer = ({ templateData, diff --git a/src/components/CippComponents/CippTenantSelector.jsx b/src/components/CippComponents/CippTenantSelector.jsx index 688afab83bb8..ab9e50ea8fcf 100644 --- a/src/components/CippComponents/CippTenantSelector.jsx +++ b/src/components/CippComponents/CippTenantSelector.jsx @@ -342,13 +342,14 @@ export const CippTenantSelector = (props) => { onChange={(nv) => setSelectedTenant(nv)} options={ tenantList.isSuccess && tenantList.data && tenantList.data.length > 0 - ? tenantList.data.map(({ customerId, displayName, defaultDomainName }) => ({ + ? tenantList.data.map(({ customerId, displayName, defaultDomainName, initialDomainName }) => ({ value: defaultDomainName, label: `${displayName} (${defaultDomainName})`, - addedField: { - defaultDomainName: "defaultDomainName", - displayName: "displayName", - customerId: "customerId", + addedFields: { + defaultDomainName: defaultDomainName, + displayName: displayName, + customerId: customerId, + initialDomainName: initialDomainName, }, })) : [] diff --git a/src/components/CippComponents/CippTextFieldWithVariables.jsx b/src/components/CippComponents/CippTextFieldWithVariables.jsx index 0f6abd9fea99..80d720ac9440 100644 --- a/src/components/CippComponents/CippTextFieldWithVariables.jsx +++ b/src/components/CippComponents/CippTextFieldWithVariables.jsx @@ -1,7 +1,7 @@ import React, { useState, useRef, useEffect, useMemo, useCallback } from "react"; import { TextField } from "@mui/material"; import { CippVariableAutocomplete } from "./CippVariableAutocomplete"; -import { useSettings } from "/src/hooks/use-settings.js"; +import { useSettings } from "../../hooks/use-settings.js"; /** * Enhanced TextField that supports custom variable autocomplete diff --git a/src/components/CippComponents/CippTranslations.jsx b/src/components/CippComponents/CippTranslations.jsx index 99d46a6e5182..af96b19cd027 100644 --- a/src/components/CippComponents/CippTranslations.jsx +++ b/src/components/CippComponents/CippTranslations.jsx @@ -52,4 +52,11 @@ export const CippTranslations = { includeTenantId: "Include Tenant ID in Notifications", logsToInclude: "Logs to Include in notifications", assignmentFilterManagementType: "Filter Type", + microsoftSupport: "Microsoft Support", + syndicatePartner: "Syndicate Partner", + breadthPartner: "Breadth Partner", + breadthPartnerDelegatedAdmin: "Breadth Partner (Delegated)", + resellerPartnerDelegatedAdmin: "Direct Reseller", + valueAddedResellerPartnerDelegatedAdmin: "Indirect Reseller", + unknownFutureValue: "Unknown", }; diff --git a/src/components/CippComponents/CippTransportRuleDrawer.jsx b/src/components/CippComponents/CippTransportRuleDrawer.jsx index b3beea3d0b1c..494ae0fdce1a 100644 --- a/src/components/CippComponents/CippTransportRuleDrawer.jsx +++ b/src/components/CippComponents/CippTransportRuleDrawer.jsx @@ -7,8 +7,8 @@ import { CippOffCanvas } from "./CippOffCanvas"; import CippFormComponent from "./CippFormComponent"; import { CippFormDomainSelector } from "./CippFormDomainSelector"; import { CippApiResults } from "./CippApiResults"; -import { useSettings } from "/src/hooks/use-settings"; -import { ApiGetCall, ApiPostCall } from "/src/api/ApiCall"; +import { useSettings } from "../../hooks/use-settings"; +import { ApiGetCall, ApiPostCall } from "../../api/ApiCall"; import { useQueryClient } from "@tanstack/react-query"; export const CippTransportRuleDrawer = ({ diff --git a/src/components/CippComponents/CippUserActions.jsx b/src/components/CippComponents/CippUserActions.jsx index 9d3e284e3678..ac6bf04a952d 100644 --- a/src/components/CippComponents/CippUserActions.jsx +++ b/src/components/CippComponents/CippUserActions.jsx @@ -20,7 +20,7 @@ import { CloudSync, } from "@mui/icons-material"; import { getCippLicenseTranslation } from "../../utils/get-cipp-license-translation"; -import { useSettings } from "/src/hooks/use-settings.js"; +import { useSettings } from "../../hooks/use-settings.js"; import { usePermissions } from "../../hooks/use-permissions"; import { Tooltip, Box } from "@mui/material"; import CippFormComponent from "./CippFormComponent"; @@ -333,7 +333,7 @@ export const useCippUserActions = () => { labelField: (option) => option?.calculatedGroupType ? `${option.displayName} (${option.calculatedGroupType})` - : option?.displayName ?? "", + : (option?.displayName ?? ""), valueField: "id", addedField: { groupType: "groupType", @@ -549,6 +549,17 @@ export const useCippUserActions = () => { "Are you sure you want to change the source of authority for [userPrincipalName]? Setting it to On-Premises Managed will take until the next sync cycle to show the change.", multiPost: false, }, + { + label: "Reprocess License Assignments", + type: "POST", + icon: , + url: "/api/ExecReprocessUserLicenses", + data: { ID: "id", userPrincipalName: "userPrincipalName" }, + confirmText: + "Are you sure you want to reprocess license assignments for [userPrincipalName]?", + multiPost: false, + condition: (row) => canWriteUser, + }, { label: "Revoke all user sessions", type: "POST", diff --git a/src/components/CippComponents/CippVariableAutocomplete.jsx b/src/components/CippComponents/CippVariableAutocomplete.jsx index 9910e9771afd..49e89fda27ee 100644 --- a/src/components/CippComponents/CippVariableAutocomplete.jsx +++ b/src/components/CippComponents/CippVariableAutocomplete.jsx @@ -9,9 +9,9 @@ import { useTheme, CircularProgress, } from "@mui/material"; -import { ApiGetCall } from "/src/api/ApiCall"; -import { useSettings } from "/src/hooks/use-settings.js"; -import { getCippError } from "/src/utils/get-cipp-error"; +import { ApiGetCall } from "../../api/ApiCall"; +import { useSettings } from "../../hooks/use-settings.js"; +import { getCippError } from "../../utils/get-cipp-error"; /** * Autocomplete component specifically for custom variables diff --git a/src/components/CippComponents/DomainAnalyserDialog.jsx b/src/components/CippComponents/DomainAnalyserDialog.jsx index 803baf43f154..ed5a6a948119 100644 --- a/src/components/CippComponents/DomainAnalyserDialog.jsx +++ b/src/components/CippComponents/DomainAnalyserDialog.jsx @@ -3,7 +3,7 @@ import { Dialog, DialogContent, DialogTitle, Button, DialogActions } from "@mui/ import { Refresh } from "@mui/icons-material"; import { useForm, FormProvider } from "react-hook-form"; import { CippFormTenantSelector } from "./CippFormTenantSelector"; -import { ApiPostCall } from "/src/api/ApiCall"; +import { ApiPostCall } from "../../api/ApiCall"; import { CippApiResults } from "./CippApiResults"; export const DomainAnalyserDialog = ({ createDialog }) => { diff --git a/src/components/CippComponents/LicenseCard.jsx b/src/components/CippComponents/LicenseCard.jsx new file mode 100644 index 000000000000..dce02b1e12f6 --- /dev/null +++ b/src/components/CippComponents/LicenseCard.jsx @@ -0,0 +1,190 @@ +import { Box, Card, CardHeader, CardContent, Typography, Divider, Skeleton } from "@mui/material"; +import { CardMembership as CardMembershipIcon } from "@mui/icons-material"; +import { CippSankey } from "./CippSankey"; + +export const LicenseCard = ({ data, isLoading }) => { + const processData = () => { + if (!data || !Array.isArray(data) || data.length === 0) { + return null; + } + + const topLicenses = data + .filter((license) => license && parseInt(license?.TotalLicenses || 0) > 0) + .sort((a, b) => parseInt(b?.TotalLicenses || 0) - parseInt(a?.TotalLicenses || 0)) + .slice(0, 5); + + if (topLicenses.length === 0) { + return null; + } + + const nodes = []; + const links = []; + + topLicenses.forEach((license, index) => { + if (license) { + const licenseName = + license.License || license.skuPartNumber || license.SkuPartNumber || "Unknown License"; + const shortName = + licenseName.length > 30 ? licenseName.substring(0, 27) + "..." : licenseName; + + const assigned = parseInt(license?.CountUsed || 0) || 0; + const available = parseInt(license?.CountAvailable || 0) || 0; + + nodes.push({ + id: shortName, + nodeColor: `hsl(${210 + index * 30}, 70%, 50%)`, + }); + + const assignedId = `${shortName} - Assigned`; + const availableId = `${shortName} - Available`; + + if (assigned > 0) { + nodes.push({ + id: assignedId, + nodeColor: "hsl(99, 70%, 50%)", + }); + + links.push({ + source: shortName, + target: assignedId, + value: assigned, + }); + } + + if (available > 0) { + nodes.push({ + id: availableId, + nodeColor: "hsl(28, 100%, 53%)", + }); + + links.push({ + source: shortName, + target: availableId, + value: available, + }); + } + } + }); + + if (nodes.length === 0 || links.length === 0) { + return null; + } + + return { nodes, links }; + }; + + const processedData = processData(); + + const calculateStats = () => { + if (!data || !Array.isArray(data)) { + return { total: 0, assigned: 0, available: 0 }; + } + + return { + total: data.reduce((sum, lic) => sum + (parseInt(lic?.TotalLicenses || 0) || 0), 0), + assigned: data.reduce((sum, lic) => sum + (parseInt(lic?.CountUsed || 0) || 0), 0), + available: data.reduce((sum, lic) => sum + (parseInt(lic?.CountAvailable || 0) || 0), 0), + }; + }; + + const stats = calculateStats(); + + return ( + + + + License Overview +
+ } + sx={{ pb: 1 }} + /> + + + {isLoading ? ( + + ) : processedData ? ( + + ) : ( + + + No license data available + + + )} + + + + + {isLoading ? ( + + + + + + + + + + + + + + + + + ) : data && Array.isArray(data) && data.length > 0 ? ( + + + + Total Licenses + + + {stats.total.toLocaleString()} + + + + + + Assigned + + + {stats.assigned.toLocaleString()} + + + + + + Available + + + {stats.available.toLocaleString()} + + + + ) : ( + + + No license statistics available + + + )} + + + ); +}; diff --git a/src/components/CippComponents/LicenseSankey.jsx b/src/components/CippComponents/LicenseSankey.jsx new file mode 100644 index 000000000000..fd4e1763f260 --- /dev/null +++ b/src/components/CippComponents/LicenseSankey.jsx @@ -0,0 +1,77 @@ +import { CippSankey } from "./CippSankey"; + +export const LicenseSankey = ({ data }) => { + // Null safety checks + if (!data || !Array.isArray(data) || data.length === 0) { + return null; + } + + // Get top 5 licenses by total count with null safety + const topLicenses = data + .filter((license) => license && parseInt(license?.TotalLicenses || 0) > 0) + .sort((a, b) => parseInt(b?.TotalLicenses || 0) - parseInt(a?.TotalLicenses || 0)) + .slice(0, 5); + + if (topLicenses.length === 0) { + return null; + } + + // Create Sankey flow: Top 5 Licenses -> Assigned/Available for each + const nodes = []; + const links = []; + + topLicenses.forEach((license, index) => { + if (license) { + const licenseName = + license.License || license.skuPartNumber || license.SkuPartNumber || "Unknown License"; + const shortName = + licenseName.length > 30 ? licenseName.substring(0, 27) + "..." : licenseName; + + const assigned = parseInt(license?.CountUsed || 0) || 0; + const available = parseInt(license?.CountAvailable || 0) || 0; + + // Add license node + nodes.push({ + id: shortName, + nodeColor: `hsl(${210 + index * 30}, 70%, 50%)`, + }); + + // Add Assigned and Available nodes for this license + const assignedId = `${shortName} - Assigned`; + const availableId = `${shortName} - Available`; + + if (assigned > 0) { + nodes.push({ + id: assignedId, + nodeColor: "hsl(99, 70%, 50%)", + }); + + links.push({ + source: shortName, + target: assignedId, + value: assigned, + }); + } + + if (available > 0) { + nodes.push({ + id: availableId, + nodeColor: "hsl(28, 100%, 53%)", + }); + + links.push({ + source: shortName, + target: availableId, + value: available, + }); + } + } + }); + + // Only render if we have valid data + if (nodes.length === 0 || links.length === 0) { + return null; + } + + return ; +}; diff --git a/src/components/CippComponents/MFACard.jsx b/src/components/CippComponents/MFACard.jsx new file mode 100644 index 000000000000..e166c3b496a0 --- /dev/null +++ b/src/components/CippComponents/MFACard.jsx @@ -0,0 +1,256 @@ +import { Box, Card, CardHeader, CardContent, Typography, Skeleton } from "@mui/material"; +import { Person as UserIcon } from "@mui/icons-material"; +import { CippSankey } from "./CippSankey"; +import { useRouter } from "next/router"; + +export const MFACard = ({ data, isLoading }) => { + const router = useRouter(); + // Process data inside component + const processData = () => { + if (!data || !Array.isArray(data) || data.length === 0) { + return null; + } + + const enabledUsers = data.filter((user) => user.AccountEnabled === true); + if (enabledUsers.length === 0) { + return null; + } + + let registeredUsers = 0; + let notRegisteredUsers = 0; + let registeredCA = 0; + let registeredSD = 0; + let registeredPerUser = 0; + let registeredNone = 0; + let notRegisteredCA = 0; + let notRegisteredSD = 0; + let notRegisteredPerUser = 0; + let notRegisteredNone = 0; + + enabledUsers.forEach((user) => { + const hasRegistered = user.MFARegistration === true; + const coveredByCA = user.CoveredByCA?.startsWith("Enforced") || false; + const coveredBySD = user.CoveredBySD === true; + const perUserEnabled = user.PerUser === "enforced" || user.PerUser === "enabled"; + + if (hasRegistered || perUserEnabled) { + registeredUsers++; + if (perUserEnabled) { + registeredPerUser++; + } else if (coveredByCA) { + registeredCA++; + } else if (coveredBySD) { + registeredSD++; + } else { + registeredNone++; + } + } else { + notRegisteredUsers++; + if (coveredByCA) { + notRegisteredCA++; + } else if (coveredBySD) { + notRegisteredSD++; + } else { + notRegisteredNone++; + } + } + }); + + const registeredPercentage = ((registeredUsers / enabledUsers.length) * 100).toFixed(1); + const protectedPercentage = ( + ((registeredCA + registeredSD + registeredPerUser) / enabledUsers.length) * + 100 + ).toFixed(1); + + const links = [ + { source: "Enabled users", target: "MFA registered", value: registeredUsers }, + { source: "Enabled users", target: "Not registered", value: notRegisteredUsers }, + ]; + + if (registeredCA > 0) + links.push({ source: "MFA registered", target: "CA policy", value: registeredCA }); + if (registeredSD > 0) + links.push({ source: "MFA registered", target: "Security defaults", value: registeredSD }); + if (registeredPerUser > 0) + links.push({ source: "MFA registered", target: "Per-user MFA", value: registeredPerUser }); + if (registeredNone > 0) + links.push({ source: "MFA registered", target: "No enforcement", value: registeredNone }); + + if (notRegisteredCA > 0) + links.push({ source: "Not registered", target: "CA policy", value: notRegisteredCA }); + if (notRegisteredSD > 0) + links.push({ + source: "Not registered", + target: "Security defaults", + value: notRegisteredSD, + }); + if (notRegisteredPerUser > 0) + links.push({ source: "Not registered", target: "Per-user MFA", value: notRegisteredPerUser }); + if (notRegisteredNone > 0) + links.push({ source: "Not registered", target: "No enforcement", value: notRegisteredNone }); + + const description = `${registeredPercentage}% of enabled users have registered MFA methods. ${protectedPercentage}% are protected by policies requiring MFA.`; + + return { + nodes: [ + { id: "Enabled users", nodeColor: "hsl(28, 100%, 53%)" }, + { id: "MFA registered", nodeColor: "hsl(99, 70%, 50%)" }, + { id: "Not registered", nodeColor: "hsl(39, 100%, 50%)" }, + { id: "CA policy", nodeColor: "hsl(99, 70%, 50%)" }, + { id: "Security defaults", nodeColor: "hsl(140, 70%, 50%)" }, + { id: "Per-user MFA", nodeColor: "hsl(200, 70%, 50%)" }, + { id: "No enforcement", nodeColor: "hsl(0, 100%, 50%)" }, + ], + links, + description, + }; + }; + + const processedData = processData(); + + const handleNodeClick = (node) => { + // Build filter based on clicked node + let filters = []; + + switch (node.id) { + case "Enabled users": + filters = [{ id: "AccountEnabled", value: "Yes" }]; + break; + case "MFA registered": + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "Yes" }, + ]; + break; + case "Not registered": + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "No" }, + ]; + break; + default: + // For other nodes, don't navigate + return; + } + + // Navigate to MFA report with filters + router.push({ + pathname: "/identity/reports/mfa-report", + query: { filters: JSON.stringify(filters) }, + }); + }; + + const handleLinkClick = (link) => { + // Build filters based on the link's source and target + let filters = []; + + if (link.source.id === "Enabled users" && link.target.id === "MFA registered") { + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "Yes" }, + ]; + } else if (link.source.id === "Enabled users" && link.target.id === "Not registered") { + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "No" }, + ]; + } else if (link.source.id === "MFA registered" && link.target.id === "CA policy") { + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "Yes" }, + ]; + // Note: We can't easily filter by CoveredByCA in the table since it needs complex logic + } else if (link.source.id === "MFA registered" && link.target.id === "Security defaults") { + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "Yes" }, + { id: "CoveredBySD", value: "Yes" }, + ]; + } else if (link.source.id === "MFA registered" && link.target.id === "Per-user MFA") { + filters = [{ id: "AccountEnabled", value: "Yes" }]; + // Note: Per-user MFA can be "enabled" or "enforced" + } else if (link.source.id === "MFA registered" && link.target.id === "No enforcement") { + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "Yes" }, + ]; + } else if (link.source.id === "Not registered" && link.target.id === "CA policy") { + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "No" }, + ]; + } else if (link.source.id === "Not registered" && link.target.id === "Security defaults") { + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "No" }, + { id: "CoveredBySD", value: "Yes" }, + ]; + } else if (link.source.id === "Not registered" && link.target.id === "Per-user MFA") { + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "No" }, + ]; + } else if (link.source.id === "Not registered" && link.target.id === "No enforcement") { + filters = [ + { id: "AccountEnabled", value: "Yes" }, + { id: "MFARegistration", value: "No" }, + ]; + } + + // Navigate to MFA report with filters + if (filters.length > 0) { + router.push({ + pathname: "/identity/reports/mfa-report", + query: { filters: JSON.stringify(filters) }, + }); + } + }; + + return ( + + + + User authentication + + } + sx={{ pb: 1 }} + /> + + + {isLoading ? ( + + ) : processedData ? ( + + ) : ( + + + No MFA data available + + + )} + + + {!isLoading && processedData?.description && ( + + + {processedData.description} + + + )} + + ); +}; diff --git a/src/components/CippComponents/MFASankey.jsx b/src/components/CippComponents/MFASankey.jsx new file mode 100644 index 000000000000..9fb387cefa9d --- /dev/null +++ b/src/components/CippComponents/MFASankey.jsx @@ -0,0 +1,140 @@ +import { CippSankey } from "./CippSankey"; + +export const MFASankey = ({ data }) => { + // Null safety checks + if (!data || !Array.isArray(data) || data.length === 0) { + return null; + } + + // Count enabled users only + const enabledUsers = data.filter((user) => user.AccountEnabled === true); + + if (enabledUsers.length === 0) { + return null; + } + + // Split by MFA registration status + let registeredUsers = 0; + let notRegisteredUsers = 0; + + // For registered users, split by protection method + let registeredCA = 0; + let registeredSD = 0; + let registeredPerUser = 0; + let registeredNone = 0; + + // For not registered users, split by protection method + let notRegisteredCA = 0; + let notRegisteredSD = 0; + let notRegisteredPerUser = 0; + let notRegisteredNone = 0; + + enabledUsers.forEach((user) => { + const hasRegistered = user.MFARegistration === true; + const coveredByCA = user.CoveredByCA?.startsWith("Enforced") || false; + const coveredBySD = user.CoveredBySD === true; + const perUserEnabled = user.PerUser === "enforced" || user.PerUser === "enabled"; + + // Consider PerUser as MFA enabled/registered + if (hasRegistered || perUserEnabled) { + registeredUsers++; + // Per-User gets its own separate terminal path + if (perUserEnabled) { + registeredPerUser++; + } else if (coveredByCA) { + registeredCA++; + } else if (coveredBySD) { + registeredSD++; + } else { + registeredNone++; + } + } else { + notRegisteredUsers++; + if (coveredByCA) { + notRegisteredCA++; + } else if (coveredBySD) { + notRegisteredSD++; + } else { + notRegisteredNone++; + } + } + }); + + const registeredPercentage = ((registeredUsers / enabledUsers.length) * 100).toFixed(1); + const protectedPercentage = ( + ((registeredCA + registeredSD + registeredPerUser) / enabledUsers.length) * + 100 + ).toFixed(1); + + const links = [ + { source: "Enabled users", target: "MFA registered", value: registeredUsers }, + { source: "Enabled users", target: "Not registered", value: notRegisteredUsers }, + ]; + + // Add protection methods for registered users + if (registeredCA > 0) + links.push({ source: "MFA registered", target: "CA policy", value: registeredCA }); + if (registeredSD > 0) + links.push({ source: "MFA registered", target: "Security defaults", value: registeredSD }); + if (registeredPerUser > 0) + links.push({ source: "MFA registered", target: "Per-user MFA", value: registeredPerUser }); + if (registeredNone > 0) + links.push({ source: "MFA registered", target: "No enforcement", value: registeredNone }); + + // Add protection methods for not registered users + if (notRegisteredCA > 0) + links.push({ source: "Not registered", target: "CA policy", value: notRegisteredCA }); + if (notRegisteredSD > 0) + links.push({ source: "Not registered", target: "Security defaults", value: notRegisteredSD }); + if (notRegisteredPerUser > 0) + links.push({ source: "Not registered", target: "Per-user MFA", value: notRegisteredPerUser }); + if (notRegisteredNone > 0) + links.push({ source: "Not registered", target: "No enforcement", value: notRegisteredNone }); + + const description = `${registeredPercentage}% of enabled users have registered MFA methods. ${protectedPercentage}% are protected by policies requiring MFA.`; + + return ( + <> + + {description && ( +
+ {description} +
+ )} + + ); +}; diff --git a/src/components/CippComponents/ScheduledTaskDetails.jsx b/src/components/CippComponents/ScheduledTaskDetails.jsx index ba52c316646a..51991b1b88a7 100644 --- a/src/components/CippComponents/ScheduledTaskDetails.jsx +++ b/src/components/CippComponents/ScheduledTaskDetails.jsx @@ -19,12 +19,12 @@ import { CippPropertyListCard } from "../CippCards/CippPropertyListCard"; import { ExpandMore, Sync, Search, Close } from "@mui/icons-material"; import { getCippFormatting } from "../../utils/get-cipp-formatting"; import { CippDataTable } from "../CippTable/CippDataTable"; -import { CippTimeAgo } from "/src/components/CippComponents/CippTimeAgo"; -import { ActionsMenu } from "/src/components/actions-menu"; +import { CippTimeAgo } from "./CippTimeAgo"; +import { ActionsMenu } from "../actions-menu"; import { CippScheduledTaskActions } from "./CippScheduledTaskActions"; import { CippApiLogsDrawer } from "./CippApiLogsDrawer"; -const ScheduledTaskDetails = ({ data, showActions = true }) => { +const ScheduledTaskDetails = ({ data, showActions = true, showTitle = true }) => { const [taskDetails, setTaskDetails] = useState(null); const [expanded, setExpanded] = useState(false); const [searchQuery, setSearchQuery] = useState(""); @@ -84,7 +84,7 @@ const ScheduledTaskDetails = ({ data, showActions = true }) => { - {taskDetailResults.isLoading ? : taskDetails?.Task?.Name} + {showTitle && (taskDetailResults.isLoading ? : taskDetails?.Task?.Name)} {showActions && ( diff --git a/src/components/CippComponents/SecureScoreCard.jsx b/src/components/CippComponents/SecureScoreCard.jsx new file mode 100644 index 000000000000..a26a3c466d20 --- /dev/null +++ b/src/components/CippComponents/SecureScoreCard.jsx @@ -0,0 +1,184 @@ +import { Box, Card, CardHeader, CardContent, Typography, Divider, Skeleton } from "@mui/material"; +import { Security as SecurityIcon } from "@mui/icons-material"; +import { + LineChart, + Line, + CartesianGrid, + XAxis, + YAxis, + ResponsiveContainer, + Tooltip as RechartsTooltip, +} from "recharts"; + +export const SecureScoreCard = ({ data, isLoading }) => { + return ( + + + + Secure Score + + } + sx={{ pb: 1 }} + /> + + {isLoading ? ( + <> + + + + + + + The Secure Score measures your security posture across your tenant. + + + ) : !data || !Array.isArray(data) || data.length === 0 ? ( + <> + + + + No secure score data available + + + + + The Secure Score measures your security posture across your tenant. + + + ) : ( + <> + + + {(() => { + const sortedData = [...data].sort((a, b) => new Date(a.createdDateTime) - new Date(b.createdDateTime)); + const chartData = sortedData.map((score) => ({ + date: new Date(score.createdDateTime).toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }), + score: score.currentScore, + percentage: Math.round((score.currentScore / score.maxScore) * 100), + })); + const ticks = chartData.map((d) => d.date); + return ( + + + + Math.round(value)} + /> + { + if (name === "score") return [value.toFixed(2), "Score"]; + if (name === "percentage") return [value + "%", "Percentage"]; + return value; + }} + /> + + + ); + })()} + + + + The Secure Score measures your security posture across your tenant. + + + )} + + + + {isLoading ? ( + + + + + + + + + + + + + + ) : !data || !Array.isArray(data) || data.length === 0 ? ( + + Enable secure score monitoring in your tenant + + ) : ( + + + + Latest % + + + {Math.round( + (data[data.length - 1].currentScore / data[data.length - 1].maxScore) * 100 + )} + % + + + + + + Current Score + + + {data[data.length - 1].currentScore.toFixed(2)} + + + + + + Max Score + + + {data[data.length - 1].maxScore.toFixed(2)} + + + + )} + + + ); +}; diff --git a/src/components/CippComponents/SecureScoreChart.jsx b/src/components/CippComponents/SecureScoreChart.jsx new file mode 100644 index 000000000000..f9830d128481 --- /dev/null +++ b/src/components/CippComponents/SecureScoreChart.jsx @@ -0,0 +1,153 @@ +import { Box, Typography, Divider, Skeleton } from "@mui/material"; +import { + LineChart, + Line, + CartesianGrid, + XAxis, + YAxis, + ResponsiveContainer, + Tooltip as RechartsTooltip, +} from "recharts"; + +export const SecureScoreChart = ({ data, isLoading }) => { + if (isLoading) { + return ( + <> + + + + + + + The Secure Score measures your security posture across your tenant. + + + + + + + + + + + + + + + + + ); + } + + if (!data || !Array.isArray(data) || data.length === 0) { + return ( + <> + + + + No secure score data available + + + + + The Secure Score measures your security posture across your tenant. + + + + + Enable secure score monitoring in your tenant + + + + ); + } + + const sortedData = [...data].sort( + (a, b) => new Date(a.createdDateTime) - new Date(b.createdDateTime) + ); + + const chartData = sortedData.map((score) => ({ + date: new Date(score.createdDateTime).toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }), + score: score.currentScore, + percentage: Math.round((score.currentScore / score.maxScore) * 100), + })); + + const latestScore = sortedData[sortedData.length - 1]; + const latestPercentage = Math.round((latestScore.currentScore / latestScore.maxScore) * 100); + + return ( + <> + + + + + + + { + if (name === "score") return [value.toFixed(2), "Score"]; + if (name === "percentage") return [value + "%", "Percentage"]; + return value; + }} + /> + + + + + + The Secure Score measures your security posture across your tenant. + + + + + + Latest % + + + {latestPercentage}% + + + + + + Current Score + + + {latestScore.currentScore.toFixed(2)} + + + + + + Max Score + + + {latestScore.maxScore.toFixed(2)} + + + + + ); +}; diff --git a/src/components/CippComponents/TenantInfoCard.jsx b/src/components/CippComponents/TenantInfoCard.jsx new file mode 100644 index 000000000000..cd4b753e8f2c --- /dev/null +++ b/src/components/CippComponents/TenantInfoCard.jsx @@ -0,0 +1,70 @@ +import { Box, Card, CardHeader, CardContent, Typography, Skeleton } from "@mui/material"; +import { Business as BuildingIcon } from "@mui/icons-material"; +import { CippCopyToClipBoard } from "./CippCopyToClipboard"; + +export const TenantInfoCard = ({ data, isLoading }) => { + return ( + + + + Tenant + + } + sx={{ pb: 1.5 }} + /> + + + + + Name + + {isLoading ? ( + + ) : ( + + {data?.displayName || "Not Available"} + + )} + + + + Tenant ID + + + {isLoading ? ( + + ) : data?.id ? ( + + ) : ( + + Not Available + + )} + + + + + Primary Domain + + + {isLoading ? ( + + ) : data?.verifiedDomains?.find((d) => d.isDefault)?.name ? ( + d.isDefault).name} + type="chip" + /> + ) : ( + + Not Available + + )} + + + + + + ); +}; diff --git a/src/components/CippComponents/TenantMetricsGrid.jsx b/src/components/CippComponents/TenantMetricsGrid.jsx new file mode 100644 index 000000000000..b8b0cfacc272 --- /dev/null +++ b/src/components/CippComponents/TenantMetricsGrid.jsx @@ -0,0 +1,100 @@ +import { Box, Grid, Tooltip, Avatar, Typography, Skeleton } from "@mui/material"; +import { + Person as UserIcon, + PersonOutline as GuestIcon, + Group as GroupIcon, + Apps as AppsIcon, + Devices as DevicesIcon, + PhoneAndroid as ManagedIcon, +} from "@mui/icons-material"; + +const formatNumber = (num) => { + if (num >= 1000000) return (num / 1000000).toFixed(1) + "M"; + if (num >= 1000) return (num / 1000).toFixed(1) + "K"; + return num?.toString() || "0"; +}; + +export const TenantMetricsGrid = ({ data, isLoading }) => { + const metrics = [ + { + label: "Users", + value: data?.UserCount || 0, + icon: UserIcon, + color: "primary", + }, + { + label: "Guests", + value: data?.GuestCount || 0, + icon: GuestIcon, + color: "info", + }, + { + label: "Groups", + value: data?.GroupCount || 0, + icon: GroupIcon, + color: "secondary", + }, + { + label: "Service Principals", + value: data?.ApplicationCount || 0, + icon: AppsIcon, + color: "error", + }, + { + label: "Devices", + value: data?.DeviceCount || 0, + icon: DevicesIcon, + color: "warning", + }, + { + label: "Managed", + value: data?.ManagedDeviceCount || 0, + icon: ManagedIcon, + color: "success", + }, + ]; + + return ( + + {metrics.map((metric) => { + const IconComponent = metric.icon; + return ( + + + + + + + + + {metric.label} + + + {isLoading ? : formatNumber(metric.value)} + + + + + + ); + })} + + ); +}; diff --git a/src/components/CippFormPages/CippAddAssignmentFilterForm.jsx b/src/components/CippFormPages/CippAddAssignmentFilterForm.jsx index 447b6ae4012d..551ec914e35b 100644 --- a/src/components/CippFormPages/CippAddAssignmentFilterForm.jsx +++ b/src/components/CippFormPages/CippAddAssignmentFilterForm.jsx @@ -1,7 +1,7 @@ import "@mui/material"; import { Grid } from "@mui/system"; import { useWatch } from "react-hook-form"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "../CippComponents/CippFormComponent"; const DEVICE_PLATFORM_OPTIONS = [ { label: "Windows 10 and later", value: "windows10AndLater" }, diff --git a/src/components/CippFormPages/CippAddAssignmentFilterTemplateForm.jsx b/src/components/CippFormPages/CippAddAssignmentFilterTemplateForm.jsx index 2b43d8f4c6b6..626b15db6e7a 100644 --- a/src/components/CippFormPages/CippAddAssignmentFilterTemplateForm.jsx +++ b/src/components/CippFormPages/CippAddAssignmentFilterTemplateForm.jsx @@ -1,7 +1,7 @@ import "@mui/material"; import { Grid } from "@mui/system"; import { useWatch } from "react-hook-form"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "../CippComponents/CippFormComponent"; const DEVICE_PLATFORM_OPTIONS = [ { label: "Windows 10 and later", value: "windows10AndLater" }, diff --git a/src/components/CippFormPages/CippAddEditContact.jsx b/src/components/CippFormPages/CippAddEditContact.jsx index cbc96616d37c..1e038f8231a5 100644 --- a/src/components/CippFormPages/CippAddEditContact.jsx +++ b/src/components/CippFormPages/CippAddEditContact.jsx @@ -1,8 +1,8 @@ import { Divider } from "@mui/material"; import { Grid } from "@mui/system"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { getCippValidator } from "/src/utils/get-cipp-validator"; -import countryList from "/src/data/countryList.json"; +import CippFormComponent from "../CippComponents/CippFormComponent"; +import { getCippValidator } from "../../utils/get-cipp-validator"; +import countryList from "../../data/countryList.json"; const countryOptions = countryList.map(({ Code, Name }) => ({ label: Name, diff --git a/src/components/CippFormPages/CippAddEditGdapRoleTemplate.jsx b/src/components/CippFormPages/CippAddEditGdapRoleTemplate.jsx index 1ed0e4163f88..891935f0f99a 100644 --- a/src/components/CippFormPages/CippAddEditGdapRoleTemplate.jsx +++ b/src/components/CippFormPages/CippAddEditGdapRoleTemplate.jsx @@ -1,5 +1,5 @@ import { Alert, Box, Stack, Typography } from "@mui/material"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "../CippComponents/CippFormComponent"; export const CippAddEditGdapRoleTemplate = (props) => { const { formControl, availableRoles } = props; diff --git a/src/components/CippFormPages/CippAddEditUser.jsx b/src/components/CippFormPages/CippAddEditUser.jsx index bfc03594152e..87e8088861a0 100644 --- a/src/components/CippFormPages/CippAddEditUser.jsx +++ b/src/components/CippFormPages/CippAddEditUser.jsx @@ -1,10 +1,10 @@ import { Alert, Divider, InputAdornment, Typography } from "@mui/material"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormCondition } from "/src/components/CippComponents/CippFormCondition"; -import { CippFormDomainSelector } from "/src/components/CippComponents/CippFormDomainSelector"; -import { CippFormUserSelector } from "/src/components/CippComponents/CippFormUserSelector"; -import countryList from "/src/data/countryList.json"; -import { CippFormLicenseSelector } from "/src/components/CippComponents/CippFormLicenseSelector"; +import CippFormComponent from "../CippComponents/CippFormComponent"; +import { CippFormCondition } from "../CippComponents/CippFormCondition"; +import { CippFormDomainSelector } from "../CippComponents/CippFormDomainSelector"; +import { CippFormUserSelector } from "../CippComponents/CippFormUserSelector"; +import countryList from "../../data/countryList.json"; +import { CippFormLicenseSelector } from "../CippComponents/CippFormLicenseSelector"; import { Grid } from "@mui/system"; import { ApiGetCall } from "../../api/ApiCall"; import { useSettings } from "../../hooks/use-settings"; @@ -606,9 +606,10 @@ const CippAddEditUser = (props) => { label: tenantGroup.displayName, value: tenantGroup.id, addedFields: { - calculatedGroupType: tenantGroup.calculatedGroupType, + groupType: tenantGroup.groupType, }, }))} + creatable={false} formControl={formControl} /> @@ -624,9 +625,10 @@ const CippAddEditUser = (props) => { label: userGroups.DisplayName, value: userGroups.id, addedFields: { - calculatedGroupType: userGroups.calculatedGroupType, + groupType: userGroups.groupType, }, }))} + creatable={false} formControl={formControl} /> diff --git a/src/components/CippFormPages/CippAddGroupForm.jsx b/src/components/CippFormPages/CippAddGroupForm.jsx index 9644b0426faf..713bb414c638 100644 --- a/src/components/CippFormPages/CippAddGroupForm.jsx +++ b/src/components/CippFormPages/CippAddGroupForm.jsx @@ -1,7 +1,7 @@ import { InputAdornment } from "@mui/material"; import { Grid } from "@mui/system"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormCondition } from "/src/components/CippComponents/CippFormCondition"; +import CippFormComponent from "../CippComponents/CippFormComponent"; +import { CippFormCondition } from "../CippComponents/CippFormCondition"; import { CippFormDomainSelector } from "../CippComponents/CippFormDomainSelector"; import { CippFormUserSelector } from "../CippComponents/CippFormUserSelector"; diff --git a/src/components/CippFormPages/CippAddGroupTemplateForm.jsx b/src/components/CippFormPages/CippAddGroupTemplateForm.jsx index 8db3b1ebea3f..36747ee70f32 100644 --- a/src/components/CippFormPages/CippAddGroupTemplateForm.jsx +++ b/src/components/CippFormPages/CippAddGroupTemplateForm.jsx @@ -1,8 +1,8 @@ import { useEffect } from "react"; import "@mui/material"; import { Grid } from "@mui/system"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormCondition } from "/src/components/CippComponents/CippFormCondition"; +import CippFormComponent from "../CippComponents/CippFormComponent"; +import { CippFormCondition } from "../CippComponents/CippFormCondition"; const CippAddGroupTemplateForm = (props) => { const { formControl } = props; diff --git a/src/components/CippFormPages/CippCustomDataMappingForm.jsx b/src/components/CippFormPages/CippCustomDataMappingForm.jsx index acb0c7134a56..8a5d30af5da7 100644 --- a/src/components/CippFormPages/CippCustomDataMappingForm.jsx +++ b/src/components/CippFormPages/CippCustomDataMappingForm.jsx @@ -1,13 +1,13 @@ import { useWatch } from "react-hook-form"; import { Box, Stack, Typography, Divider } from "@mui/material"; import { Grid } from "@mui/system"; -import { CippFormComponent } from "/src/components/CippComponents/CippFormComponent"; -import { CippFormTenantSelector } from "/src/components/CippComponents/CippFormTenantSelector"; -import { CippFormCondition } from "/src/components/CippComponents/CippFormCondition"; -import { CippPropertyListCard } from "/src/components/CippCards/CippPropertyListCard"; -import { CippCopyToClipBoard } from "/src/components/CippComponents/CippCopyToClipboard"; -import extensionDataMapping from "/src/data/extensionDataMapping"; -import { getCippTranslation } from "/src/utils/get-cipp-translation"; +import { CippFormComponent } from "../CippComponents/CippFormComponent"; +import { CippFormTenantSelector } from "../CippComponents/CippFormTenantSelector"; +import { CippFormCondition } from "../CippComponents/CippFormCondition"; +import { CippPropertyListCard } from "../CippCards/CippPropertyListCard"; +import { CippCopyToClipBoard } from "../CippComponents/CippCopyToClipboard"; +import extensionDataMapping from "../../data/extensionDataMapping"; +import { getCippTranslation } from "../../utils/get-cipp-translation"; const CippCustomDataMappingForm = ({ formControl }) => { const selectedAttribute = useWatch({ control: formControl.control, name: "customDataAttribute" }); @@ -42,7 +42,7 @@ const CippCustomDataMappingForm = ({ formControl }) => { multiple: false, placeholder: "Select a Source Type", options: [ - { value: "extensionSync", label: "Extension Sync" }, + { value: "reportingDb", label: "Reporting DB" }, { value: "manualEntry", label: "Manual Entry" }, ], }; @@ -65,7 +65,7 @@ const CippCustomDataMappingForm = ({ formControl }) => { condition: { field: "sourceType", compareType: "valueEq", - compareValue: "extensionSync", + compareValue: "reportingDb", }, }, { @@ -219,7 +219,7 @@ const CippCustomDataMappingForm = ({ formControl }) => { - {selectedSourceType?.value === "extensionSync" && ( + {selectedSourceType?.value === "reportingDb" && ( <> @@ -282,7 +282,7 @@ const CippCustomDataMappingForm = ({ formControl }) => { - {selectedExtensionSyncDataset && selectedSourceType?.value === "extensionSync" && ( + {selectedExtensionSyncDataset && selectedSourceType?.value === "reportingDb" && ( { const { formControl, userSettingsDefaults } = props; diff --git a/src/components/CippFormPages/CippJSONView.jsx b/src/components/CippFormPages/CippJSONView.jsx index 0215385cbc89..4f082ef9ac33 100644 --- a/src/components/CippFormPages/CippJSONView.jsx +++ b/src/components/CippFormPages/CippJSONView.jsx @@ -19,7 +19,7 @@ import { PropertyList } from "../property-list"; import { getCippTranslation } from "../../utils/get-cipp-translation"; import { getCippFormatting } from "../../utils/get-cipp-formatting"; import { CippCodeBlock } from "../CippComponents/CippCodeBlock"; -import intuneCollection from "/src/data/intuneCollection.json"; +import intuneCollection from "../../data/intuneCollection.json"; import { useGuidResolver } from "../../hooks/use-guid-resolver"; const cleanObject = (obj) => { diff --git a/src/components/CippFormPages/CippSafeLinksPolicyRuleForm.jsx b/src/components/CippFormPages/CippSafeLinksPolicyRuleForm.jsx index ce25cc269028..2937aec1ae1a 100644 --- a/src/components/CippFormPages/CippSafeLinksPolicyRuleForm.jsx +++ b/src/components/CippFormPages/CippSafeLinksPolicyRuleForm.jsx @@ -1,15 +1,15 @@ import { useEffect, useState } from "react"; import { Grid } from "@mui/system"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "../CippComponents/CippFormComponent"; import { Typography } from "@mui/material"; -import { CippFormUserSelector } from "/src/components/CippComponents/CippFormUserSelector"; -import { CippFormGroupSelector } from "/src/components/CippComponents/CippFormGroupSelector"; -import { CippFormDomainSelector } from "/src/components/CippComponents/CippFormDomainSelector"; -import { CippInfoCard } from "/src/components/CippCards/CippInfoCard"; +import { CippFormUserSelector } from "../CippComponents/CippFormUserSelector"; +import { CippFormGroupSelector } from "../CippComponents/CippFormGroupSelector"; +import { CippFormDomainSelector } from "../CippComponents/CippFormDomainSelector"; +import { CippInfoCard } from "../CippCards/CippInfoCard"; import { InformationCircleIcon } from "@heroicons/react/24/outline"; -import { getCippValidator } from "/src/utils/get-cipp-validator"; -import { ApiGetCall } from "/src/api/ApiCall"; -import { useSettings } from "/src/hooks/use-settings"; +import { getCippValidator } from "../../utils/get-cipp-validator"; +import { ApiGetCall } from "../../api/ApiCall"; +import { useSettings } from "../../hooks/use-settings"; // Utility functions for data processing export const safeLinksDataUtils = { diff --git a/src/components/CippFormPages/CippSchedulerForm.jsx b/src/components/CippFormPages/CippSchedulerForm.jsx index f134461bce8c..8857a9d427a5 100644 --- a/src/components/CippFormPages/CippSchedulerForm.jsx +++ b/src/components/CippFormPages/CippSchedulerForm.jsx @@ -14,15 +14,15 @@ import { } from "@mui/material"; import { Grid, Stack } from "@mui/system"; import { useWatch } from "react-hook-form"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormTenantSelector } from "/src/components/CippComponents/CippFormTenantSelector"; -import { CippFormCondition } from "/src/components/CippComponents/CippFormCondition"; -import CippGraphResourceSelector from "/src/components/CippComponents/CippGraphResourceSelector"; -import CippGraphAttributeSelector from "/src/components/CippComponents/CippGraphAttributeSelector"; -import { getCippValidator } from "/src/utils/get-cipp-validator"; +import CippFormComponent from "../CippComponents/CippFormComponent"; +import { CippFormTenantSelector } from "../CippComponents/CippFormTenantSelector"; +import { CippFormCondition } from "../CippComponents/CippFormCondition"; +import CippGraphResourceSelector from "../CippComponents/CippGraphResourceSelector"; +import CippGraphAttributeSelector from "../CippComponents/CippGraphAttributeSelector"; +import { getCippValidator } from "../../utils/get-cipp-validator"; import { useRouter } from "next/router"; import Link from "next/link"; -import { ApiGetCall, ApiPostCall } from "/src/api/ApiCall"; +import { ApiGetCall, ApiPostCall } from "../../api/ApiCall"; import { useEffect, useState } from "react"; import CippFormInputArray from "../CippComponents/CippFormInputArray"; import { CippApiResults } from "../CippComponents/CippApiResults"; diff --git a/src/components/CippIntegrations/CippApiClientManagement.jsx b/src/components/CippIntegrations/CippApiClientManagement.jsx index 4f42eb3d99a7..829ad4413ce2 100644 --- a/src/components/CippIntegrations/CippApiClientManagement.jsx +++ b/src/components/CippIntegrations/CippApiClientManagement.jsx @@ -2,7 +2,7 @@ import { Button, Stack, SvgIcon, Menu, MenuItem, ListItemText, Alert } from "@mu import { useState } from "react"; import isEqual from "lodash/isEqual"; import { useForm } from "react-hook-form"; -import { ApiGetCall, ApiGetCallWithPagination, ApiPostCall } from "/src/api/ApiCall"; +import { ApiGetCall, ApiGetCallWithPagination, ApiPostCall } from "../../api/ApiCall"; import { CippDataTable } from "../CippTable/CippDataTable"; import { ChevronDownIcon, diff --git a/src/components/CippIntegrations/CippIntegrationFieldMapping.jsx b/src/components/CippIntegrations/CippIntegrationFieldMapping.jsx index ae0014d0e276..53a88787de19 100644 --- a/src/components/CippIntegrations/CippIntegrationFieldMapping.jsx +++ b/src/components/CippIntegrations/CippIntegrationFieldMapping.jsx @@ -8,13 +8,13 @@ import { Button, Alert, } from "@mui/material"; -import CippFormSection from "/src/components/CippFormPages/CippFormSection"; +import CippFormSection from "../CippFormPages/CippFormSection"; import { useForm } from "react-hook-form"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../api/ApiCall"; import { useRouter } from "next/router"; -import extensions from "/src/data/Extensions.json"; +import extensions from "../../data/Extensions.json"; import React, { useEffect, useState } from "react"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "../CippComponents/CippFormComponent"; import { Sync } from "@mui/icons-material"; import { Stack, Grid } from "@mui/system"; diff --git a/src/components/CippIntegrations/CippIntegrationSettings.jsx b/src/components/CippIntegrations/CippIntegrationSettings.jsx index 3ff5bea6f256..d0156df3897a 100644 --- a/src/components/CippIntegrations/CippIntegrationSettings.jsx +++ b/src/components/CippIntegrations/CippIntegrationSettings.jsx @@ -1,12 +1,12 @@ import { Box, CardContent } from "@mui/material"; import { Grid } from "@mui/system"; -import CippFormSection from "/src/components/CippFormPages/CippFormSection"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormSection from "../CippFormPages/CippFormSection"; +import CippFormComponent from "../CippComponents/CippFormComponent"; import { useForm } from "react-hook-form"; -import { useSettings } from "/src/hooks/use-settings"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { useSettings } from "../../hooks/use-settings"; +import { ApiGetCall } from "../../api/ApiCall"; import { useRouter } from "next/router"; -import extensions from "/src/data/Extensions.json"; +import extensions from "../../data/Extensions.json"; import React, { useEffect } from "react"; import { CippFormCondition } from "../CippComponents/CippFormCondition"; diff --git a/src/components/CippIntegrations/CippIntegrationTenantMapping.jsx b/src/components/CippIntegrations/CippIntegrationTenantMapping.jsx index 57680d8bae7d..cfe214218139 100644 --- a/src/components/CippIntegrations/CippIntegrationTenantMapping.jsx +++ b/src/components/CippIntegrations/CippIntegrationTenantMapping.jsx @@ -12,9 +12,9 @@ import { import { Grid } from "@mui/system"; import { useState, useMemo } from "react"; import { useForm } from "react-hook-form"; -import { ApiGetCall, ApiPostCall } from "/src/api/ApiCall"; +import { ApiGetCall, ApiPostCall } from "../../api/ApiCall"; import { useRouter } from "next/router"; -import extensions from "/src/data/Extensions.json"; +import extensions from "../../data/Extensions.json"; import { useEffect } from "react"; import { CippDataTable } from "../CippTable/CippDataTable"; import { PlusSmallIcon, SparklesIcon, TrashIcon } from "@heroicons/react/24/outline"; diff --git a/src/components/CippSettings/CippBackendCard.jsx b/src/components/CippSettings/CippBackendCard.jsx index 2b0890b0f340..6c9465ae5a90 100644 --- a/src/components/CippSettings/CippBackendCard.jsx +++ b/src/components/CippSettings/CippBackendCard.jsx @@ -1,9 +1,9 @@ import { OpenInNew } from "@mui/icons-material"; -import CippButtonCard from "/src/components/CippCards/CippButtonCard"; +import CippButtonCard from "../CippCards/CippButtonCard"; import { Button, Stack, SvgIcon, Typography } from "@mui/material"; import { CippOffCanvas } from "../CippComponents/CippOffCanvas"; import { useState } from "react"; -import { getCippTranslation } from "/src/utils/get-cipp-translation"; +import { getCippTranslation } from "../../utils/get-cipp-translation"; export const CippBackendCard = ({ backendComponents, item, hosted }) => { const [open, setOpen] = useState(false); diff --git a/src/components/CippSettings/CippBackupRetentionSettings.jsx b/src/components/CippSettings/CippBackupRetentionSettings.jsx index bcd74ec4bb5d..38cd78ed2ceb 100644 --- a/src/components/CippSettings/CippBackupRetentionSettings.jsx +++ b/src/components/CippSettings/CippBackupRetentionSettings.jsx @@ -1,6 +1,6 @@ import { Button, ButtonGroup, SvgIcon, Typography, TextField, Box } from "@mui/material"; -import CippButtonCard from "/src/components/CippCards/CippButtonCard"; -import { ApiGetCall, ApiPostCall } from "/src/api/ApiCall"; +import CippButtonCard from "../CippCards/CippButtonCard"; +import { ApiGetCall, ApiPostCall } from "../../api/ApiCall"; import { History } from "@mui/icons-material"; import { useState, useEffect } from "react"; diff --git a/src/components/CippSettings/CippBackupSettings.jsx b/src/components/CippSettings/CippBackupSettings.jsx index 39663651f7d2..a7f56afe7d0f 100644 --- a/src/components/CippSettings/CippBackupSettings.jsx +++ b/src/components/CippSettings/CippBackupSettings.jsx @@ -1,7 +1,7 @@ import { Button, SvgIcon, Typography } from "@mui/material"; -import CippButtonCard from "/src/components/CippCards/CippButtonCard"; -import { ApiPostCall } from "/src/api/ApiCall"; -import { useDialog } from "/src/hooks/use-dialog"; +import CippButtonCard from "../CippCards/CippButtonCard"; +import { ApiPostCall } from "../../api/ApiCall"; +import { useDialog } from "../../hooks/use-dialog"; import { SettingsBackupRestore } from "@mui/icons-material"; import Link from "next/link"; diff --git a/src/components/CippSettings/CippBrandingSettings.jsx b/src/components/CippSettings/CippBrandingSettings.jsx index a75330da986b..b0e0f747ef35 100644 --- a/src/components/CippSettings/CippBrandingSettings.jsx +++ b/src/components/CippSettings/CippBrandingSettings.jsx @@ -1,9 +1,9 @@ import { useState } from "react"; import { Button, Typography, Box, Alert } from "@mui/material"; import { Palette, Upload } from "@mui/icons-material"; -import CippButtonCard from "/src/components/CippCards/CippButtonCard"; -import { ApiGetCall, ApiPostCall } from "/src/api/ApiCall"; -import { useSettings } from "/src/hooks/use-settings"; +import CippButtonCard from "../CippCards/CippButtonCard"; +import { ApiGetCall, ApiPostCall } from "../../api/ApiCall"; +import { useSettings } from "../../hooks/use-settings"; import { CippApiResults } from "../CippComponents/CippApiResults"; import CippFormComponent from "../CippComponents/CippFormComponent"; import { useForm } from "react-hook-form"; @@ -95,7 +95,7 @@ const CippBrandingSettings = () => { return ( - - - {/* Main offcanvas */} - setOffcanvasVisible(false)} - title={`${cat}.${obj} Endpoints`} - > - - - Listed below are the available API endpoints based on permission level. - ReadWrite level includes endpoints under Read. - - {Object.keys(apiPermissions[cat][obj]).map((type, typeIndex) => { - var items = []; - for (var api in apiPermissions[cat][obj][type]) { - const apiFunction = apiPermissions[cat][obj][type][api]; - items.push({ - name: apiFunction.Name, - description: apiFunction.Description?.[0]?.Text || null - }); - } - return ( - - {type} - - {items.map((item, idx) => ( - - - {item.name} - - {item.description && ( - - )} - - ))} - - - ); - })} - - - - {/* Description offcanvas */} - setDescriptionOffcanvasVisible(false)} - title="Function Description" - > - - - {selectedDescription.name} - - - {selectedDescription.description} - - - - - ); - }; - - return ( - <> - - - - ({ - label: role.RowKey, - value: role.RowKey, - }))} - isFetching={customRoleListFetching} - refreshFunction={() => refetchCustomRoleList()} - creatable={true} - formControl={formControl} - multiple={false} - fullWidth={true} - /> - {cippApiRoleSelected && ( - - This is the default role for all API clients in the CIPP-API integration. If you - would like different permissions for specific applications, create a role per - application and select it from the CIPP-API integrations page. - - )} - - - - {allTenantSelected && blockedTenants?.length == 0 && ( - - All tenants selected, no tenant restrictions will be applied unless blocked tenants - are specified. - - )} - - {allTenantSelected && ( - - - - )} - - {currentRole && ( - <> - {apiPermissionFetching && } - {apiPermissionSuccess && ( - <> - API Permissions - - Set All Permissions - - - - - - - <> - {Object.keys(apiPermissions) - .sort() - .map((cat, catIndex) => ( - - }> - {cat} - - - {Object.keys(apiPermissions[cat]) - .sort() - .map((obj, index) => { - return ( - - - - ); - })} - - - ))} - - - - )} - - )} - - - - {selectedRole && selectedTenant?.length > 0 && ( - <> -
Allowed Tenants
-
    - {selectedTenant.map((tenant, idx) => ( -
  • {tenant?.label}
  • - ))} -
- - )} - {selectedRole && blockedTenants?.length > 0 && ( - <> -
Blocked Tenants
-
    - {blockedTenants.map((tenant, idx) => ( -
  • {tenant?.label}
  • - ))} -
- - )} - {selectedRole && selectedPermissions && ( - <> -
Selected Permissions
-
    - {selectedPermissions && - Object.keys(selectedPermissions) - ?.sort() - .map((cat, idx) => ( - <> - {selectedPermissions?.[cat] && - !selectedPermissions?.[cat]?.includes("None") && ( -
  • {selectedPermissions[cat]}
  • - )} - - ))} -
- - )} -
-
- - - - - {currentRole && ( - - )} - - - - ); -}; - -export default CippCustomRoles; diff --git a/src/components/CippSettings/CippDnsSettings.jsx b/src/components/CippSettings/CippDnsSettings.jsx index 847c0451bf87..666337169266 100644 --- a/src/components/CippSettings/CippDnsSettings.jsx +++ b/src/components/CippSettings/CippDnsSettings.jsx @@ -1,6 +1,6 @@ import { Button, ButtonGroup, SvgIcon, Typography } from "@mui/material"; -import CippButtonCard from "/src/components/CippCards/CippButtonCard"; -import { ApiGetCall, ApiPostCall } from "/src/api/ApiCall"; +import CippButtonCard from "../CippCards/CippButtonCard"; +import { ApiGetCall, ApiPostCall } from "../../api/ApiCall"; import { Dns } from "@mui/icons-material"; const CippDnsSettings = () => { @@ -23,7 +23,7 @@ const CippDnsSettings = () => { }; const DnsButtons = () => { - const resolvers = ["Google", "Cloudflare", "Quad9"]; + const resolvers = ["Google", "Cloudflare"]; return resolvers.map((resolver, index) => ( + } + > + + + Configure maximum allowed duration for Just-In-Time (JIT) admin accounts. This setting + helps enforce security policies by preventing technicians from creating JIT admin accounts + with excessively long lifespans. + + + {/* Maximum Duration Section */} + + + Maximum Duration + + { + // Allow empty value (no limit) + if (!value || typeof value !== "string" || value.trim() === "") { + return true; + } + const iso8601Regex = + /^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/; + if (!iso8601Regex.test(value)) { + return "Invalid format. Use PT1H, P1D, P7D, P28D, etc."; + } + return true; + }, + }, + }} + formControl={formControl} + /> + + + + + Leave empty for no limit on JIT admin account duration. When set, technicians cannot + create JIT admin accounts with durations exceeding this limit. This setting applies + globally to all tenants. + + + + {/* API Results */} + + + + ); +}; + +export default CippJitAdminSettings; diff --git a/src/components/CippSettings/CippPasswordSettings.jsx b/src/components/CippSettings/CippPasswordSettings.jsx index 0f3a8d6f4976..1394beff3fe5 100644 --- a/src/components/CippSettings/CippPasswordSettings.jsx +++ b/src/components/CippSettings/CippPasswordSettings.jsx @@ -1,6 +1,6 @@ import { Button, ButtonGroup, SvgIcon, Typography } from "@mui/material"; -import CippButtonCard from "/src/components/CippCards/CippButtonCard"; -import { ApiGetCall, ApiPostCall } from "/src/api/ApiCall"; +import CippButtonCard from "../CippCards/CippButtonCard"; +import { ApiGetCall, ApiPostCall } from "../../api/ApiCall"; import { KeyIcon } from "@heroicons/react/24/outline"; const CippPasswordSettings = () => { diff --git a/src/components/CippSettings/CippPermissionCheck.jsx b/src/components/CippSettings/CippPermissionCheck.jsx index db0c81c7ad38..c38f8aa49d5a 100644 --- a/src/components/CippSettings/CippPermissionCheck.jsx +++ b/src/components/CippSettings/CippPermissionCheck.jsx @@ -9,8 +9,8 @@ import { SvgIcon, Typography, } from "@mui/material"; -import CippButtonCard from "/src/components/CippCards/CippButtonCard"; -import { ApiGetCall } from "/src/api/ApiCall"; +import CippButtonCard from "../CippCards/CippButtonCard"; +import { ApiGetCall } from "../../api/ApiCall"; import { useEffect, useState } from "react"; import { CippPermissionResults } from "./CippPermissionResults"; import { CippGDAPResults } from "./CippGDAPResults"; diff --git a/src/components/CippSettings/CippPermissionResults.jsx b/src/components/CippSettings/CippPermissionResults.jsx index 22c0b3d425c4..375dca112f2a 100644 --- a/src/components/CippSettings/CippPermissionResults.jsx +++ b/src/components/CippSettings/CippPermissionResults.jsx @@ -1,10 +1,10 @@ import { Button, Link, List, ListItem, Skeleton, SvgIcon, Typography } from "@mui/material"; import { Cancel, CheckCircle } from "@mui/icons-material"; -import { CippPropertyList } from "/src/components/CippComponents/CippPropertyList"; +import { CippPropertyList } from "../CippComponents/CippPropertyList"; import { WrenchIcon, XMarkIcon } from "@heroicons/react/24/outline"; import { CippOffCanvas } from "../CippComponents/CippOffCanvas"; import { CippPropertyListCard } from "../CippCards/CippPropertyListCard"; -import { CippDataTable } from "/src/components/CippTable/CippDataTable"; +import { CippDataTable } from "../CippTable/CippDataTable"; import { ApiPostCall } from "../../api/ApiCall"; import { CippApiResults } from "../CippComponents/CippApiResults"; import { useEffect, useState } from "react"; @@ -143,9 +143,6 @@ export const CippPermissionResults = (props) => { }} extendedInfo={[]} > - - Permission Details - {results?.Results?.Links.length > 0 && ( { const setDefaults = useWatch({ control: formControl.control, name: "Defaults" }); const selectedPermissions = useWatch({ control: formControl.control, name: "Permissions" }); const selectedEntraGroup = useWatch({ control: formControl.control, name: "EntraGroup" }); + const ipRanges = useWatch({ control: formControl.control, name: "IPRange" }); const { data: apiPermissions = [], @@ -87,7 +88,11 @@ export const CippRoleAddEdit = ({ selectedRole }) => { queryKey: "customRoleList", }); - const { data: { pages = [] } = {}, isSuccess: tenantsSuccess } = ApiGetCallWithPagination({ + const { + data: { pages = [] } = {}, + isSuccess: tenantsSuccess, + isFetching: tenantsFetching, + } = ApiGetCallWithPagination({ url: "/api/ListTenants?AllTenantSelector=true", queryKey: "ListTenants-All", }); @@ -240,6 +245,13 @@ export const CippRoleAddEdit = ({ selectedRole }) => { value: endpoint, })) || []; + // Process IP ranges + const processedIPRanges = + currentPermissions?.IPRange?.map((ip) => ({ + label: ip, + value: ip, + })) || []; + formControl.reset({ Permissions: basePermissions && Object.keys(basePermissions).length > 0 @@ -249,6 +261,7 @@ export const CippRoleAddEdit = ({ selectedRole }) => { allowedTenants: newAllowedTenants, blockedTenants: newBlockedTenants, BlockedEndpoints: processedBlockedEndpoints, + IPRange: processedIPRanges, EntraGroup: currentPermissions?.EntraGroup, }); } @@ -340,6 +353,11 @@ export const CippRoleAddEdit = ({ selectedRole }) => { return endpoint.value || endpoint; }) || []; + const processedIPRanges = + ipRanges?.map((ip) => { + return ip?.value || ip; + }) || []; + updatePermissions.mutate({ url: "/api/ExecCustomRole?Action=AddUpdate", data: { @@ -349,6 +367,7 @@ export const CippRoleAddEdit = ({ selectedRole }) => { AllowedTenants: processedAllowedTenants, BlockedTenants: processedBlockedTenants, BlockedEndpoints: processedBlockedEndpoints, + IPRange: processedIPRanges, }, }); }; @@ -509,6 +528,7 @@ export const CippRoleAddEdit = ({ selectedRole }) => { dataKey: "Results", labelField: "displayName", valueField: "id", + showRefresh: true, }} formControl={formControl} fullWidth={true} @@ -612,6 +632,26 @@ export const CippRoleAddEdit = ({ selectedRole }) => {
)} + + + {apiPermissionFetching && ( <> @@ -821,6 +861,16 @@ export const CippRoleAddEdit = ({ selectedRole }) => { )} + {ipRanges?.length > 0 && ( + <> +
Allowed IP Ranges
+
    + {ipRanges.map((ip, idx) => ( +
  • {ip?.value || ip?.label || ip}
  • + ))} +
+ + )} {selectedPermissions && apiPermissionSuccess && ( <>
Selected Permissions
@@ -849,7 +899,13 @@ export const CippRoleAddEdit = ({ selectedRole }) => { className="me-2" type="submit" variant="contained" - disabled={updatePermissions.isPending || customRoleListFetching || !formState.isValid} + disabled={ + updatePermissions.isPending || + customRoleListFetching || + apiPermissionFetching || + tenantsFetching || + !formState.isValid + } startIcon={ diff --git a/src/components/CippSettings/CippRoles.jsx b/src/components/CippSettings/CippRoles.jsx index c155064b634a..34b1f08dcd78 100644 --- a/src/components/CippSettings/CippRoles.jsx +++ b/src/components/CippSettings/CippRoles.jsx @@ -44,7 +44,7 @@ const CippRoles = () => { disableVariables: true, }, ], - relatedQueryKeys: ["customRoleList"], + relatedQueryKeys: ["customRoleList", "customRoleTable"], confirmText: "Are you sure you want to clone this custom role?", condition: (row) => row?.Type === "Custom", }, @@ -63,7 +63,7 @@ const CippRoles = () => { RoleName: "RoleName", }, condition: (row) => row?.Type === "Custom", - relatedQueryKeys: ["customRoleList"], + relatedQueryKeys: ["customRoleList", "customRoleTable"], }, ]; diff --git a/src/components/CippSettings/CippVersionProperties.jsx b/src/components/CippSettings/CippVersionProperties.jsx index 6fbf88a39311..4b19e1d9e328 100644 --- a/src/components/CippSettings/CippVersionProperties.jsx +++ b/src/components/CippSettings/CippVersionProperties.jsx @@ -1,7 +1,7 @@ import { Box, Button, SvgIcon } from "@mui/material"; -import { CippPropertyListCard } from "/src/components/CippCards/CippPropertyListCard"; +import { CippPropertyListCard } from "../CippCards/CippPropertyListCard"; import { CheckCircle, SystemUpdateAlt, Warning } from "@mui/icons-material"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../api/ApiCall"; import { useEffect } from "react"; const CippVersionProperties = () => { diff --git a/src/components/CippStandards/CippStandardAccordion.jsx b/src/components/CippStandards/CippStandardAccordion.jsx index afbd54594256..dd59cee0bccc 100644 --- a/src/components/CippStandards/CippStandardAccordion.jsx +++ b/src/components/CippStandards/CippStandardAccordion.jsx @@ -29,7 +29,7 @@ import { Construction, } from "@mui/icons-material"; import { Grid } from "@mui/system"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "../CippComponents/CippFormComponent"; import { useWatch, useFormState } from "react-hook-form"; import _ from "lodash"; import Microsoft from "../../icons/iconly/bulk/microsoft"; @@ -37,9 +37,9 @@ import Azure from "../../icons/iconly/bulk/azure"; import Exchange from "../../icons/iconly/bulk/exchange"; import Defender from "../../icons/iconly/bulk/defender"; import Intune from "../../icons/iconly/bulk/intune"; -import GDAPRoles from "/src/data/GDAPRoles"; -import timezoneList from "/src/data/timezoneList"; -import standards from "/src/data/standards.json"; +import GDAPRoles from "../../data/GDAPRoles"; +import timezoneList from "../../data/timezoneList"; +import standards from "../../data/standards.json"; import { CippFormCondition } from "../CippComponents/CippFormCondition"; import { CippPolicyImportDrawer } from "../CippComponents/CippPolicyImportDrawer"; import ReactMarkdown from "react-markdown"; @@ -714,6 +714,14 @@ const CippStandardAccordion = ({ {accordionTitle} + {standard.deprecated && ( + + )} {/* Hide action chips in drift mode */} {!isDriftMode && selectedActions && selectedActions?.length > 0 && ( <> @@ -780,10 +788,21 @@ const CippStandardAccordion = ({ {standard.multiple && ( - - handleAddMultipleStandard(standardName)}> - - + + + handleAddMultipleStandard(standardName)} + disabled={standard.deprecated} + > + + + )} - + {standard.deprecated && ( + + + ⚠️ This standard is deprecated and cannot be configured. Please remove it + from your template and use an alternative standard if available. + + + )} + {isDriftMode ? ( /* Drift mode layout - full width with slider first */ diff --git a/src/components/CippStandards/CippStandardDialog.jsx b/src/components/CippStandards/CippStandardDialog.jsx index d74d6f4d3630..6873936d9cda 100644 --- a/src/components/CippStandards/CippStandardDialog.jsx +++ b/src/components/CippStandards/CippStandardDialog.jsx @@ -102,9 +102,6 @@ const StandardCard = memo( height: "100%", display: "flex", flexDirection: "column", - ...(isNewStandard(standard.addedDate) && { - mt: 1.2, // Add top margin to accommodate the "New" label - }), }} > {isNewStandard(standard.addedDate) && ( @@ -123,6 +120,22 @@ const StandardCard = memo( }} /> )} + {standard.deprecated && ( + + )} @@ -243,7 +262,34 @@ const StandardCard = memo( - {standard.multiple ? ( + {standard.deprecated ? ( + + + } + label={ + isSelected + ? "Remove this standard from the template" + : "This standard is deprecated" + } + /> + {!isSelected && ( + + This standard is deprecated and cannot be added. Please use an alternative + standard if available. + + )} + + ) : standard.multiple ? ( } label="Add this standard to the template" @@ -329,7 +376,7 @@ const VirtualizedStandardGrid = memo(({ items, renderItem }) => { overscan={5} defaultItemHeight={320} // Provide estimated row height for better virtualization itemContent={(index) => ( - + {standard.label}
+ {standard.deprecated && ( + + )} {isNewStandard(standard.addedDate) && ( - {standard.multiple ? ( + {standard.deprecated ? ( + isSelected ? ( + + } + label="Remove" + sx={{ mr: 1 }} + /> + ) : ( + + Deprecated - Cannot be added + + ) + ) : standard.multiple ? ( { return ( @@ -336,6 +340,39 @@ export const CIPPTableToptoolbar = ({ restoredFiltersRef.current.clear(); }, [pageName]); + // Detect overflow and switch to compact mode + useEffect(() => { + const checkOverflow = () => { + if (!leftContainerRef.current || !actionsContainerRef.current) { + return; + } + + const leftContainerWidth = leftContainerRef.current.offsetWidth; + const leftContainerScrollWidth = leftContainerRef.current.scrollWidth; + const actionsWidth = actionsContainerRef.current.scrollWidth; + const isOverflowing = leftContainerScrollWidth > leftContainerWidth; + const shouldBeCompact = isOverflowing || actionsWidth > leftContainerWidth * 0.6; // Actions taking > 60% of left container + + setUseCompactMode(shouldBeCompact); + }; + + // Check immediately on mount and when dependencies change + checkOverflow(); + + // Also check after a brief delay to ensure elements are fully rendered + const timeoutId = setTimeout(checkOverflow, 100); + + const resizeObserver = new ResizeObserver(checkOverflow); + if (leftContainerRef.current) { + resizeObserver.observe(leftContainerRef.current); + } + + return () => { + clearTimeout(timeoutId); + resizeObserver.disconnect(); + }; + }, [hasSelection, customBulkActions.length, exportEnabled, filters?.length, usedColumns?.length]); + // Restore last used filter on mount if persistFilters is enabled (non-graph filters) useEffect(() => { // Wait for table to be initialized and data to be available @@ -593,6 +630,7 @@ export const CIPPTableToptoolbar = ({ return ( <> {/* Left side - Main controls */} @@ -675,9 +714,22 @@ export const CIPPTableToptoolbar = ({ /> - {/* Desktop Buttons */} + {/* Desktop Buttons - always render for measurement, hide when in compact mode */} {!mdDown && ( - <> + {/* Filters Button */} } @@ -811,7 +863,17 @@ export const CIPPTableToptoolbar = ({ Export )} - + + )} + + {/* Mobile/Compact Action Button */} + {(mdDown || useCompactMode) && !hasSelection && ( + setActionMenuAnchor(event.currentTarget)} + sx={{ flexShrink: 0 }} + > + + )} {/* Mobile Action Menu */} @@ -1065,24 +1127,6 @@ export const CIPPTableToptoolbar = ({ )} - - {/* Mobile Action Menu */} - {mdDown && ( - setActionMenuAnchor(event.currentTarget)} - size="small" - sx={{ - height: "40px", - width: "40px", - border: "1px solid", - borderColor: "divider", - borderRadius: "8px", - ml: "auto", - }} - > - - - )} {/* Right side - Additional controls */} @@ -1249,7 +1293,6 @@ export const CIPPTableToptoolbar = ({ }} > - API Response { simple = false, cardButton, offCanvas = false, + offCanvasOnRowClick = false, noCard = false, hideTitle = false, refreshFunction, @@ -114,6 +115,8 @@ export const CippDataTable = (props) => { const [usedColumns, setUsedColumns] = useState([]); const [offcanvasVisible, setOffcanvasVisible] = useState(false); const [offCanvasData, setOffCanvasData] = useState({}); + const [offCanvasRowIndex, setOffCanvasRowIndex] = useState(0); + const [filteredRows, setFilteredRows] = useState([]); const [customComponentData, setCustomComponentData] = useState({}); const [customComponentVisible, setCustomComponentVisible] = useState(false); const [actionData, setActionData] = useState({ data: {}, action: {}, ready: false }); @@ -280,12 +283,45 @@ export const CippDataTable = (props) => { baseBackgroundColor: theme.palette.background.paper, }), muiTablePaperProps: ({ table }) => ({ - //not sx - style: { - zIndex: table.getState().isFullScreen ? 1000 : undefined, - top: table.getState().isFullScreen ? 64 : undefined, + sx: { + ...(table.getState().isFullScreen && { + position: 'fixed !important', + top: '64px !important', + bottom: '0 !important', + left: { xs: '0 !important', lg: settings?.sidebarCollapse ? '73px !important' : '270px !important' }, + right: '0 !important', + zIndex: '1300 !important', + m: '0 !important', + p: '16px !important', + overflow: 'auto', + bgcolor: 'background.paper', + maxWidth: 'none !important', + width: 'auto !important', + height: 'auto !important', + }), }, }), + muiTableBodyRowProps: + offCanvasOnRowClick && offCanvas + ? ({ row }) => ({ + onClick: () => { + setOffCanvasData(row.original); + // Find the index of this row in the filtered rows + const filteredRowsArray = table.getFilteredRowModel().rows; + const indexInFiltered = filteredRowsArray.findIndex( + (r) => r.original === row.original + ); + setOffCanvasRowIndex(indexInFiltered >= 0 ? indexInFiltered : 0); + setOffcanvasVisible(true); + }, + sx: { + cursor: "pointer", + "&:hover": { + backgroundColor: "action.hover", + }, + }, + }) + : undefined, // Add global styles to target the specific filter components enableColumnFilterModes: true, muiTableHeadCellProps: { @@ -437,6 +473,12 @@ export const CippDataTable = (props) => { onClick={() => { closeMenu(); setOffCanvasData(row.original); + // Find the index of this row in the filtered rows + const filteredRowsArray = table.getFilteredRowModel().rows; + const indexInFiltered = filteredRowsArray.findIndex( + (r) => r.original === row.original + ); + setOffCanvasRowIndex(indexInFiltered >= 0 ? indexInFiltered : 0); setOffcanvasVisible(true); }} > @@ -452,6 +494,12 @@ export const CippDataTable = (props) => { onClick={() => { closeMenu(); setOffCanvasData(row.original); + // Find the index of this row in the filtered rows + const filteredRowsArray = table.getFilteredRowModel().rows; + const indexInFiltered = filteredRowsArray.findIndex( + (r) => r.original === row.original + ); + setOffCanvasRowIndex(indexInFiltered >= 0 ? indexInFiltered : 0); setOffcanvasVisible(true); }} > @@ -668,6 +716,19 @@ export const CippDataTable = (props) => { } }, [table.getSelectedRowModel().rows]); + useEffect(() => { + // Update filtered rows whenever table filtering/sorting changes + if (table && table.getFilteredRowModel) { + const rows = table.getFilteredRowModel().rows; + setFilteredRows(rows.map((row) => row.original)); + } + }, [ + table, + table.getState().columnFilters, + table.getState().globalFilter, + table.getState().sorting, + ]); + useEffect(() => { //check if the simplecolumns are an array, if (Array.isArray(simpleColumns) && simpleColumns.length > 0) { @@ -742,8 +803,27 @@ export const CippDataTable = (props) => { extendedData={offCanvasData} extendedInfoFields={offCanvas?.extendedInfoFields} actions={actions} - children={offCanvas?.children} + title={offCanvasData?.Name || offCanvas?.title || "Extended Info"} + children={ + offCanvas?.children ? (row) => offCanvas.children(row, offCanvasRowIndex) : undefined + } customComponent={offCanvas?.customComponent} + onNavigateUp={() => { + const newIndex = offCanvasRowIndex - 1; + if (newIndex >= 0 && filteredRows && filteredRows[newIndex]) { + setOffCanvasRowIndex(newIndex); + setOffCanvasData(filteredRows[newIndex]); + } + }} + onNavigateDown={() => { + const newIndex = offCanvasRowIndex + 1; + if (filteredRows && newIndex < filteredRows.length) { + setOffCanvasRowIndex(newIndex); + setOffCanvasData(filteredRows[newIndex]); + } + }} + canNavigateUp={offCanvasRowIndex > 0} + canNavigateDown={filteredRows && offCanvasRowIndex < filteredRows.length - 1} {...offCanvas} /> {/* Render custom component */} diff --git a/src/components/CippTable/CippGraphExplorerFilter.js b/src/components/CippTable/CippGraphExplorerFilter.js index 3167e5b7ca91..9d296e50ef2f 100644 --- a/src/components/CippTable/CippGraphExplorerFilter.js +++ b/src/components/CippTable/CippGraphExplorerFilter.js @@ -10,8 +10,8 @@ import { } from "@mui/icons-material"; import { useForm, useWatch } from "react-hook-form"; import { debounce } from "lodash"; -import CippButtonCard from "/src/components/CippCards/CippButtonCard"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippButtonCard from "../CippCards/CippButtonCard"; +import CippFormComponent from "../CippComponents/CippFormComponent"; import { ApiGetCall, ApiPostCall } from "../../api/ApiCall"; import { useSettings } from "../../hooks/use-settings"; import { CippApiResults } from "../CippComponents/CippApiResults"; @@ -47,6 +47,7 @@ const CippGraphExplorerFilter = ({ $expand: "", $top: "", $search: "", + $orderby: "", $format: "", NoPagination: false, ReverseTenantLookup: false, @@ -326,6 +327,10 @@ const CippGraphExplorerFilter = ({ Key: "$expand", Value: formParameters.$expand, }, + { + Key: "$orderby", + Value: formParameters.$orderby, + }, { Key: "$format", Value: formParameters.$format, @@ -722,6 +727,17 @@ const CippGraphExplorerFilter = ({ /> + {/* OrderBy Field */} + + + + {/* Format Field */} { + switch (status?.toLowerCase()) { + case "passed": + return "success"; + case "failed": + return "error"; + case "investigate": + return "warning"; + case "skipped": + return "default"; + default: + return "default"; + } +}; + +const getRiskColor = (risk) => { + switch (risk?.toLowerCase()) { + case "high": + return "error"; + case "medium": + return "warning"; + case "low": + return "info"; + default: + return "default"; + } +}; + +const getImpactColor = (impact) => { + switch (impact?.toLowerCase()) { + case "high": + return "error"; + case "medium": + return "warning"; + case "low": + return "info"; + default: + return "default"; + } +}; + +const checkCIPPStandardAvailable = (testName) => { + if (!testName) return "No"; + console.log(testName); + // Check if any standard's tag array contains a reference to this test + const hasStandard = standardsData.some((standard) => { + if (!standard.tag || !Array.isArray(standard.tag)) return false; + // Check if any tag matches the test name or contains it + return standard.tag.some((tag) => { + const tagLower = tag.toLowerCase(); + const testLower = testName.toLowerCase(); + return tagLower.includes(testLower) || testLower.includes(tagLower); + }); + }); + + return hasStandard ? "Yes" : "No"; +}; + +// Shared markdown styling for consistent rendering +const markdownStyles = { + "& a": { + color: (theme) => theme.palette.primary.main, + textDecoration: "underline", + "&:hover": { + textDecoration: "none", + }, + }, + color: "text.secondary", + fontSize: "0.875rem", + lineHeight: 1.43, + "& p": { + my: 1, + }, + "& ul": { + my: 1, + pl: 2, + }, + "& li": { + my: 0.5, + }, + "& h1, & h2, & h3, & h4, & h5, & h6": { + mt: 2, + mb: 1, + fontWeight: "bold", + }, + "& table": { + width: "100%", + borderCollapse: "collapse", + marginTop: 2, + marginBottom: 2, + }, + "& th, & td": { + border: 1, + borderColor: "divider", + padding: 1, + textAlign: "left", + }, + "& th": { + backgroundColor: "action.hover", + fontWeight: "bold", + }, + "& code": { + backgroundColor: "action.hover", + padding: "2px 6px", + borderRadius: 1, + fontSize: "0.85em", + }, + "& pre": { + backgroundColor: "action.hover", + padding: 2, + borderRadius: 1, + overflow: "auto", + }, +}; + +export const CippTestDetailOffCanvas = ({ row }) => { + return ( + + + + ({ + xs: `1px solid ${theme.palette.divider}`, + md: "none", + }), + borderRight: (theme) => ({ + md: `1px solid ${theme.palette.divider}`, + }), + }} + > + + + + Risk + + + + + + + + ({ + xs: `1px solid ${theme.palette.divider}`, + md: "none", + }), + borderRight: (theme) => ({ + md: `1px solid ${theme.palette.divider}`, + }), + }} + > + + + + User Impact + + + + + + + + ({ + xs: `1px solid ${theme.palette.divider}`, + md: "none", + }), + borderRight: (theme) => ({ + md: `1px solid ${theme.palette.divider}`, + }), + }} + > + + + + Effort + + + + + + + + + + + + Standard Available + + + + + + + + + + + {row.ResultMarkdown && ( + + + + {row.Name} + + + + ( + + {children} + + ), + }} + > + {row.ResultMarkdown} + + + + + )} + + + + + + What did we check + + + {row.Category && ( + + + Category + + {row.Category} + + )} + + {row.Description && ( + + ( + + {children} + + ), + }} + > + {row.Description} + + + )} + + + + + ); +}; diff --git a/src/components/CippWizard/CIPPDeploymentUpdateTokens.jsx b/src/components/CippWizard/CIPPDeploymentUpdateTokens.jsx index 50fef63317f2..6c863f7cac49 100644 --- a/src/components/CippWizard/CIPPDeploymentUpdateTokens.jsx +++ b/src/components/CippWizard/CIPPDeploymentUpdateTokens.jsx @@ -1,10 +1,11 @@ import { useState } from "react"; -import { Stack, Typography, CircularProgress, SvgIcon, Box } from "@mui/material"; -import { CheckCircle } from "@mui/icons-material"; +import { Stack, Typography, CircularProgress, SvgIcon, Box, Chip, Skeleton } from "@mui/material"; +import { CheckCircle, Person, Apartment } from "@mui/icons-material"; import CippButtonCard from "../CippCards/CippButtonCard"; import { ApiGetCall } from "../../api/ApiCall"; import { CippApiResults } from "../CippComponents/CippApiResults"; import { CIPPM365OAuthButton } from "../CippComponents/CIPPM365OAuthButton"; +import { getCippTranslation } from "../../utils/get-cipp-translation"; export const CIPPDeploymentUpdateTokens = ({ formControl }) => { const [tokens, setTokens] = useState(null); @@ -12,7 +13,7 @@ export const CIPPDeploymentUpdateTokens = ({ formControl }) => { // Get application ID information for the card header const appId = ApiGetCall({ url: `/api/ExecListAppId`, - queryKey: `ExecListAppId`, + queryKey: "listAppId", waiting: true, }); @@ -46,10 +47,79 @@ export const CIPPDeploymentUpdateTokens = ({ formControl }) => { /> } > - + Click the button to refresh the Graph token for your tenants using popup authentication. - This method opens a popup window where you can sign in to your Microsoft account. + Use this to update your refresh token or change the logged in user. This method opens a + popup window where you can sign in to your Microsoft account. + + + Current Tenant Info + + + {(appId.isLoading || appId.isFetching) && ( + + + + + + + + )} + {!appId.isLoading && !appId.isFetching && appId?.data?.orgName && ( + + + + + + + + {appId.data.orgName} + + + {appId.data.tenantId} + + + {appId.data.authenticatedUserDisplayName && ( + + + + + + {appId.data.authenticatedUserDisplayName} + + + {appId.data.authenticatedUserPrincipalName} + + + )} + + + {appId.data.isPartnerTenant ? ( + + ) : ( + + )} + + + )} + 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/components/CippWizard/CippTenantModeDeploy.jsx b/src/components/CippWizard/CippTenantModeDeploy.jsx index 8f8683af405e..52f9722e6254 100644 --- a/src/components/CippWizard/CippTenantModeDeploy.jsx +++ b/src/components/CippWizard/CippTenantModeDeploy.jsx @@ -1,10 +1,22 @@ import { useEffect } from "react"; -import { Stack, Box, Typography, Link } from "@mui/material"; +import { + Stack, + Box, + Typography, + Link, + Chip, + Skeleton, + SvgIcon, + IconButton, + Tooltip, +} from "@mui/material"; +import { Person, Apartment, Sync } from "@mui/icons-material"; import { CIPPM365OAuthButton } from "../CippComponents/CIPPM365OAuthButton"; import { CippApiResults } from "../CippComponents/CippApiResults"; -import { ApiPostCall } from "../../api/ApiCall"; +import { ApiPostCall, ApiGetCall } from "../../api/ApiCall"; import { CippWizardStepButtons } from "./CippWizardStepButtons"; import { CippTenantTable } from "./CippTenantTable"; +import { getCippTranslation } from "../../utils/get-cipp-translation"; export const CippTenantModeDeploy = (props) => { const { formControl, currentStep, onPreviousStep, onNextStep } = props; @@ -13,8 +25,15 @@ export const CippTenantModeDeploy = (props) => { required: true, }); - const updateRefreshToken = ApiPostCall({ urlfromdata: true }); - const addTenant = ApiPostCall({ urlfromdata: true }); + const updateRefreshToken = ApiPostCall({ urlfromdata: true, relatedQueryKeys: ["listAppId"] }); + const addTenant = ApiPostCall({ urlfromdata: true, relatedQueryKeys: ["tenants-table"] }); + + // Get partner tenant info using the same API call as CIPPM365OAuthButton + const partnerTenantInfo = ApiGetCall({ + url: `/api/ExecListAppId`, + queryKey: "listAppId", + waiting: true, + }); useEffect(() => { if (updateRefreshToken.isSuccess) { @@ -30,18 +49,28 @@ export const CippTenantModeDeploy = (props) => { return ( - - {/* Partner Tenant (GDAP) */} - - Partner Tenant - + + + Partner Tenant + + + partnerTenantInfo.refetch()} + disabled={partnerTenantInfo.isLoading} + > + + + + Using GDAP is recommended for CIPP, however you can also authenticate to individual - tenants. It is still highly recommended to connect to your partner tenant first, even if - you are not a Microsoft CSP. This allows CIPP to send notifications, perform permission - checks, and update permissions when required. + tenants. It is required to connect to your partner tenant first, even if you are not a + Microsoft CSP. This is where the multi-tenant App Registration (CIPP-SAM) is installed. It + also allows CIPP to send notifications, perform permission checks, and update permissions + when required. Please remember to log onto a service account dedicated for CIPP. More info? Check out the{" "} @@ -55,26 +84,142 @@ export const CippTenantModeDeploy = (props) => { . - - - { - const updatedTokenData = { - ...tokenData, - tenantMode: "GDAP", - }; - updateRefreshToken.mutate({ - url: "/api/ExecUpdateRefreshToken", - data: updatedTokenData, - }); + {(partnerTenantInfo.isLoading || partnerTenantInfo.isFetching) && ( + + - + > + + + + + + + + + + )} + + {!partnerTenantInfo.isLoading && + !partnerTenantInfo.isFetching && + partnerTenantInfo?.data?.orgName && ( + + + + + + + + + + {partnerTenantInfo.data.orgName} + + + {partnerTenantInfo.data.tenantId} + + + {partnerTenantInfo.data.authenticatedUserDisplayName && ( + + + + + + {partnerTenantInfo.data.authenticatedUserDisplayName} + + + {partnerTenantInfo.data.authenticatedUserPrincipalName} + + + )} + + + {partnerTenantInfo.data.isPartnerTenant ? ( + + ) : ( + + )} + + + + + )} + + {!partnerTenantInfo.isLoading && + !partnerTenantInfo.isFetching && + !partnerTenantInfo?.data?.orgName && ( + + + + + No partner tenant connected. Click the button below to authenticate with your + partner tenant. + + + + + )} + + + { + const updatedTokenData = { + ...tokenData, + tenantMode: "GDAP", + }; + updateRefreshToken.mutate({ + url: "/api/ExecUpdateRefreshToken", + data: updatedTokenData, + }); + }} + buttonText={ + partnerTenantInfo?.data?.orgName + ? "Change Partner Tenant" + : "Connect to Partner Tenant" + } + showSuccessAlert={false} + promptBeforeAuth={ + partnerTenantInfo?.data?.orgName + ? `Are you sure you want to change the partner tenant from '${partnerTenantInfo?.data?.orgName}'? If you are trying to add another tenant, use the per-tenant authentication below.` + : false + } + scope="https://graph.microsoft.com/DelegatedPermissionGrant.ReadWrite.All https://graph.microsoft.com/Directory.ReadWrite.All https://graph.microsoft.com/AppRoleAssignment.ReadWrite.All offline_access profile openid" + /> + + {/* Per-Tenant */} @@ -86,34 +231,65 @@ export const CippTenantModeDeploy = (props) => { wrong tenant? Use the table below to remove it. + {!partnerTenantInfo?.data?.orgName && ( + + + Please connect to your partner tenant first before adding separate tenants. + + + )} + - { - const updatedTokenData = { - ...tokenData, - tenantMode: "perTenant", - }; - addTenant.mutate({ - url: "/api/ExecAddTenant", - data: updatedTokenData, - }); - }} - buttonText="Connect to Separate Tenants" - showSuccessAlert={false} - /> + + {!partnerTenantInfo?.data?.orgName && ( + + )} + + { + if (!partnerTenantInfo?.data?.orgName) return; + const updatedTokenData = { + ...tokenData, + tenantMode: "perTenant", + }; + addTenant.mutate({ + url: "/api/ExecAddTenant", + data: updatedTokenData, + }); + }} + buttonText="Connect to Separate Tenants" + showSuccessAlert={false} + scope="https://graph.microsoft.com/DelegatedPermissionGrant.ReadWrite.All https://graph.microsoft.com/Directory.ReadWrite.All https://graph.microsoft.com/AppRoleAssignment.ReadWrite.All offline_access profile openid" + /> + + - + + + + + { const { postUrl, formControl, onPreviousStep, onNextStep, currentStep } = props; diff --git a/src/components/ExecutiveReportButton.js b/src/components/ExecutiveReportButton.js index e7d0cdde65de..922a3c550850 100644 --- a/src/components/ExecutiveReportButton.js +++ b/src/components/ExecutiveReportButton.js @@ -2533,7 +2533,7 @@ const ExecutiveReportDocument = ({ }; export const ExecutiveReportButton = (props) => { - const { tenantName, tenantId, userStats, standardsData, organizationData, ...other } = props; + const { ...other } = props; const settings = useSettings(); const brandingSettings = settings.customBranding; @@ -2550,6 +2550,22 @@ export const ExecutiveReportButton = (props) => { infographics: true, }); + // Fetch organization data - only when preview is open + const organization = ApiGetCall({ + url: "/api/ListOrg", + queryKey: `${settings.currentTenant}-ListOrg-report`, + data: { tenantFilter: settings.currentTenant }, + waiting: previewOpen, + }); + + // Fetch user counts - only when preview is open + const dashboard = ApiGetCall({ + url: "/api/ListuserCounts", + data: { tenantFilter: settings.currentTenant }, + queryKey: `${settings.currentTenant}-ListuserCounts-report`, + waiting: previewOpen, + }); + // Only fetch additional data when preview dialog is opened const secureScore = useSecureScore({ waiting: previewOpen }); @@ -2606,7 +2622,9 @@ export const ExecutiveReportButton = (props) => { // Check if all data is loaded (either successful or failed) - only relevant when preview is open const isDataLoading = previewOpen && - (secureScore.isFetching || + (organization.isFetching || + dashboard.isFetching || + secureScore.isFetching || licenseData.isFetching || deviceData.isFetching || conditionalAccessData.isFetching || @@ -2615,7 +2633,9 @@ export const ExecutiveReportButton = (props) => { const hasAllDataFinished = !previewOpen || - ((secureScore.isSuccess || secureScore.isError) && + ((organization.isSuccess || organization.isError) && + (dashboard.isSuccess || dashboard.isError) && + (secureScore.isSuccess || secureScore.isError) && (licenseData.isSuccess || licenseData.isError) && (deviceData.isSuccess || deviceData.isError) && (conditionalAccessData.isSuccess || conditionalAccessData.isError) && @@ -2625,6 +2645,18 @@ export const ExecutiveReportButton = (props) => { // Button is always available now since we don't need to wait for data const shouldShowButton = true; + const tenantName = organization.data?.displayName || "Tenant"; + const tenantId = organization.data?.id; + const userStats = { + licensedUsers: dashboard.data?.LicUsers || 0, + unlicensedUsers: + dashboard.data?.Users && dashboard.data?.LicUsers + ? dashboard.data?.Users - dashboard.data?.LicUsers + : 0, + guests: dashboard.data?.Guests || 0, + globalAdmins: dashboard.data?.Gas || 0, + }; + const fileName = `Executive_Report_${tenantName?.replace(/[^a-zA-Z0-9]/g, "_") || "Tenant"}_${ new Date().toISOString().split("T")[0] }.pdf`; @@ -2655,8 +2687,8 @@ export const ExecutiveReportButton = (props) => { tenantName={tenantName} tenantId={tenantId} userStats={userStats} - standardsData={standardsData} - organizationData={organizationData} + standardsData={driftComplianceData.data} + organizationData={organization.data} brandingSettings={brandingSettings} secureScoreData={secureScore.isSuccess ? secureScore : null} licensingData={licenseData.isSuccess ? licenseData?.data : null} @@ -2687,8 +2719,8 @@ export const ExecutiveReportButton = (props) => { tenantName, tenantId, userStats, - standardsData, - organizationData, + organization.data, + dashboard.data, brandingSettings, secureScore?.isSuccess, licenseData?.isSuccess, @@ -3007,8 +3039,8 @@ export const ExecutiveReportButton = (props) => { tenantName={tenantName} tenantId={tenantId} userStats={userStats} - standardsData={standardsData} - organizationData={organizationData} + standardsData={driftComplianceData.data} + organizationData={organization.data} brandingSettings={brandingSettings} secureScoreData={secureScore.isSuccess ? secureScore : null} licensingData={licenseData.isSuccess ? licenseData?.data : null} diff --git a/src/components/pdfExportButton.js b/src/components/pdfExportButton.js index 0755b6037079..f0c0ce803b08 100644 --- a/src/components/pdfExportButton.js +++ b/src/components/pdfExportButton.js @@ -5,6 +5,22 @@ import autoTable from "jspdf-autotable"; import { getCippFormatting } from "../utils/get-cipp-formatting"; import { useSettings } from "../hooks/use-settings"; +// Flatten nested objects so deeply nested properties export properly. +// This function only restructures data without formatting - formatting happens later in one pass. +const flattenObject = (obj, parentKey = "") => { + const flattened = {}; + Object.keys(obj).forEach((key) => { + const fullKey = parentKey ? `${parentKey}.${key}` : key; + if (typeof obj[key] === "object" && obj[key] !== null && !Array.isArray(obj[key])) { + Object.assign(flattened, flattenObject(obj[key], fullKey)); + } else { + // Store the raw value - formatting will happen in a single pass later + flattened[fullKey] = obj[key]; + } + }); + return flattened; +}; + // Shared helper so the toolbar buttons and bulk export path share the same PDF logic. export const exportRowsToPdf = ({ rows = [], @@ -21,7 +37,7 @@ export const exportRowsToPdf = ({ const size = "A3"; const orientation = "landscape"; const doc = new jsPDF(orientation, unit, size); - const tableData = rows.map((row) => row.original ?? row); + const tableData = rows.map((row) => flattenObject(row.original ?? row)); const exportColumns = columns .filter((c) => columnVisibility[c.id]) @@ -30,8 +46,11 @@ export const exportRowsToPdf = ({ // Use the existing formatting helper so PDF output mirrors table formatting. const formattedData = tableData.map((row) => { const formattedRow = {}; - Object.keys(row).forEach((key) => { - formattedRow[key] = getCippFormatting(row[key], key, "text", false); + exportColumns.forEach((col) => { + const key = col.dataKey; + if (key in row) { + formattedRow[key] = getCippFormatting(row[key], key, "text", false); + } }); return formattedRow; }); @@ -58,7 +77,7 @@ export const exportRowsToPdf = ({ const columnWidths = exportColumns.map((col) => { const headerLength = col.header.length; const maxContentLength = Math.max( - ...formattedData.map((row) => String(row[col.dataKey] || "").length) + ...formattedData.map((row) => String(row[col.dataKey] || "").length), ); const estimatedWidth = Math.max(headerLength, maxContentLength) * 6; return Math.min(estimatedWidth, (availableWidth / columnCount) * 1.5); @@ -66,7 +85,7 @@ export const exportRowsToPdf = ({ const totalEstimatedWidth = columnWidths.reduce((sum, width) => sum + width, 0); const normalizedWidths = columnWidths.map( - (width) => (width / totalEstimatedWidth) * availableWidth + (width) => (width / totalEstimatedWidth) * availableWidth, ); // Honor tenant branding colors when present so exports stay on-brand. diff --git a/src/data/Extensions.json b/src/data/Extensions.json index c6d8ced44146..21471f5e295c 100644 --- a/src/data/Extensions.json +++ b/src/data/Extensions.json @@ -274,7 +274,7 @@ "links": [ { "name": "HaloPSA Documentation", - "url": "https://halopsa.com/guides/" + "url": "https://usehalo.com/halopsa/guides/2697" } ], "SettingOptions": [ diff --git a/src/data/alerts.json b/src/data/alerts.json index 497436c7d6a3..40cb347adeb3 100644 --- a/src/data/alerts.json +++ b/src/data/alerts.json @@ -31,12 +31,22 @@ }, { "name": "InactiveLicensedUsers", - "label": "Alert on licensed users that have not logged in for 90 days", + "label": "Alert on licensed users that have not logged in for X days", + "recommendedRunInterval": "1d", "requiresInput": true, - "inputType": "switch", - "inputLabel": "Exclude disabled users?", - "inputName": "InactiveLicensedUsersExcludeDisabled", - "recommendedRunInterval": "1d" + "multipleInput": true, + "inputs": [ + { + "inputType": "number", + "inputLabel": "Days since last login (default: 90)", + "inputName": "DaysSinceLastLogin" + }, + { + "inputType": "switch", + "inputLabel": "Exclude disabled users?", + "inputName": "ExcludeDisabled" + } + ] }, { "name": "EntraConnectSyncStatus", @@ -103,6 +113,11 @@ "label": "Alert on Security Defaults automatic enablement", "recommendedRunInterval": "1d" }, + { + "name": "SecDefaultsDisabled", + "label": "Alert when Security Defaults is disabled with no Conditional Access policies", + "recommendedRunInterval": "1d" + }, { "name": "DefenderStatus", "label": "Alert if Defender is not running (Tenant must be on-boarded in Lighthouse)", @@ -187,6 +202,41 @@ "label": "Alert on device compliance issues", "recommendedRunInterval": "4h" }, + { + "name": "IntunePolicyConflicts", + "label": "Alert on Intune policy or app conflicts/errors", + "recommendedRunInterval": "4h", + "requiresInput": true, + "multipleInput": true, + "inputs": [ + { + "inputType": "switch", + "inputLabel": "Alert per issue (off = aggregated)", + "inputName": "AlertEachIssue" + }, + { + "inputType": "switch", + "inputLabel": "Include policy status issues", + "inputName": "IncludePolicies" + }, + { + "inputType": "switch", + "inputLabel": "Include app install issues", + "inputName": "IncludeApplications" + }, + { + "inputType": "switch", + "inputLabel": "Alert on conflicts", + "inputName": "AlertConflicts" + }, + { + "inputType": "switch", + "inputLabel": "Alert on errors/failures", + "inputName": "AlertErrors" + } + ], + "description": "Monitors Intune policy assignment states and app install statuses for conflicts or errors. Defaults to aggregated alerts with all mechanisms enabled and both conflicts and errors included." + }, { "name": "BreachAlert", "label": "Alert on (new) potentially breached passwords. Generates an alert if a password is found to be breached.", @@ -291,5 +341,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/data/standards.json b/src/data/standards.json index 92d605cf2036..890c59891c67 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -116,7 +116,13 @@ { "name": "standards.AuditLog", "cat": "Global Standards", - "tag": ["CIS M365 5.0 (3.1.1)", "mip_search_auditlog", "NIST CSF 2.0 (DE.CM-09)"], + "tag": [ + "CIS M365 5.0 (3.1.1)", + "mip_search_auditlog", + "NIST CSF 2.0 (DE.CM-09)", + "CISAMSEXO171", + "CISAMSEXO173" + ], "helpText": "Enables the Unified Audit Log for tracking and auditing activities. Also runs Enable-OrganizationCustomization if necessary.", "executiveText": "Activates comprehensive activity logging across Microsoft 365 services to track user actions, system changes, and security events. This provides essential audit trails for compliance requirements, security investigations, and regulatory reporting.", "addedComponent": [], @@ -319,7 +325,10 @@ "EIDSCA.AP14", "EIDSCA.ST08", "EIDSCA.ST09", - "NIST CSF 2.0 (PR.AA-05)" + "NIST CSF 2.0 (PR.AA-05)", + "EIDSCAAP07", + "EIDSCAST08", + "EIDSCAST09" ], "helpText": "Disables Guest access to enumerate directory objects. This prevents guest users from seeing other users or guests in the directory.", "docsDescription": "Sets it so guests can view only their own user profile. Permission to view other users isn't allowed. Also restricts guest users from seeing the membership of groups they're in. See exactly what get locked down in the [Microsoft documentation.](https://learn.microsoft.com/en-us/entra/fundamentals/users-default-permissions)", @@ -335,7 +344,7 @@ { "name": "standards.DisableBasicAuthSMTP", "cat": "Global Standards", - "tag": ["CIS M365 5.0 (6.5.4)", "NIST CSF 2.0 (PR.IR-01)"], + "tag": ["CIS M365 5.0 (6.5.4)", "NIST CSF 2.0 (PR.IR-01)", "ZTNA21799", "CISAMSEXO51"], "helpText": "Disables SMTP AUTH organization-wide, impacting POP and IMAP clients that rely on SMTP for sending emails. Default for new tenants. For more information, see the [Microsoft documentation](https://learn.microsoft.com/en-us/exchange/clients-and-mobile-in-exchange-online/authenticated-client-smtp-submission)", "docsDescription": "Disables tenant-wide SMTP basic authentication, including for all explicitly enabled users, impacting POP and IMAP clients that rely on SMTP for sending emails. For more information, see the [Microsoft documentation](https://learn.microsoft.com/en-us/exchange/clients-and-mobile-in-exchange-online/authenticated-client-smtp-submission).", "executiveText": "Disables outdated email authentication methods that are vulnerable to security attacks, forcing applications and devices to use modern, more secure authentication protocols. This reduces the risk of email-based security breaches and credential theft.", @@ -350,7 +359,14 @@ { "name": "standards.ActivityBasedTimeout", "cat": "Global Standards", - "tag": ["CIS M365 5.0 (1.3.2)", "spo_idle_session_timeout", "NIST CSF 2.0 (PR.AA-03)"], + "tag": [ + "CIS M365 5.0 (1.3.2)", + "spo_idle_session_timeout", + "NIST CSF 2.0 (PR.AA-03)", + "ZTNA21813", + "ZTNA21814", + "ZTNA21815" + ], "helpText": "Enables and sets Idle session timeout for Microsoft 365 to 1 hour. This policy affects most M365 web apps", "executiveText": "Automatically logs out inactive users from Microsoft 365 applications after a specified time period to prevent unauthorized access to company data on unattended devices. This security measure protects against data breaches when employees leave workstations unlocked.", "addedComponent": [ @@ -394,7 +410,7 @@ { "name": "standards.AuthMethodsSettings", "cat": "Entra (AAD) Standards", - "tag": ["EIDSCA.AG01", "EIDSCA.AG02", "EIDSCA.AG03"], + "tag": ["EIDSCA.AG01", "EIDSCA.AG02", "EIDSCA.AG03", "EIDSCAAG02", "EIDSCAAG03"], "helpText": "Configures the report suspicious activity settings and system credential preferences in the authentication methods policy.", "docsDescription": "Controls the authentication methods policy settings for reporting suspicious activity and system credential preferences. These settings help enhance the security of authentication in your organization.", "executiveText": "Configures security settings that allow users to report suspicious login attempts and manages how the system handles authentication credentials. This enhances overall security by enabling early detection of potential security threats and optimizing authentication processes.", @@ -454,7 +470,7 @@ { "name": "standards.AuthMethodsPolicyMigration", "cat": "Entra (AAD) Standards", - "tag": [], + "tag": ["EIDSCAAG01"], "helpText": "Completes the migration of authentication methods policy to the new format", "docsDescription": "Sets the authentication methods policy migration state to complete. This is required when migrating from legacy authentication policies to the new unified authentication methods policy.", "executiveText": "Completes the transition from legacy authentication policies to Microsoft's modern unified authentication methods policy, ensuring the organization benefits from the latest security features and management capabilities. This migration enables enhanced security controls and simplified policy management.", @@ -533,7 +549,7 @@ { "name": "standards.laps", "cat": "Entra (AAD) Standards", - "tag": [], + "tag": ["ZTNA21953", "ZTNA21955", "ZTNA24560"], "helpText": "Enables the tenant to use LAPS. You must still create a policy for LAPS to be active on all devices. Use the template standards to deploy this by default.", "docsDescription": "Enables the LAPS functionality on the tenant. Prerequisite for using Windows LAPS via Azure AD.", "executiveText": "Enables Local Administrator Password Solution (LAPS) capability, which automatically manages and rotates local administrator passwords on company computers. This significantly improves security by preventing the use of shared or static administrator passwords that could be exploited by attackers.", @@ -556,7 +572,14 @@ "EIDSCA.AM07", "EIDSCA.AM09", "EIDSCA.AM10", - "NIST CSF 2.0 (PR.AA-03)" + "NIST CSF 2.0 (PR.AA-03)", + "EIDSCAAM01", + "EIDSCAAM03", + "EIDSCAAM04", + "EIDSCAAM06", + "EIDSCAAM07", + "EIDSCAAM09", + "EIDSCAAM10" ], "helpText": "Enables the MS authenticator app to display information about the app that is requesting authentication. This displays the application name.", "docsDescription": "Allows users to use Passwordless with Number Matching and adds location information from the last request", @@ -572,7 +595,7 @@ { "name": "standards.allowOTPTokens", "cat": "Entra (AAD) Standards", - "tag": ["EIDSCA.AM02"], + "tag": ["EIDSCA.AM02", "EIDSCAAM02"], "helpText": "Allows you to use MS authenticator OTP token generator", "docsDescription": "Allows you to use Microsoft Authenticator OTP token generator. Useful for using the NPS extension as MFA on VPN clients.", "executiveText": "Enables one-time password generation through Microsoft Authenticator app, providing an additional secure authentication method for employees. This is particularly useful for secure VPN access and other systems requiring multi-factor authentication.", @@ -631,7 +654,13 @@ "EIDSCA.AF04", "EIDSCA.AF05", "EIDSCA.AF06", - "NIST CSF 2.0 (PR.AA-03)" + "NIST CSF 2.0 (PR.AA-03)", + "EIDSCAAF01", + "EIDSCAAF02", + "EIDSCAAF03", + "EIDSCAAF04", + "EIDSCAAF05", + "EIDSCAAF06" ], "helpText": "Enables the FIDO2 authenticationMethod for the tenant", "docsDescription": "Enables FIDO2 capabilities for the tenant. This allows users to use FIDO2 keys like a Yubikey for authentication.", @@ -692,7 +721,7 @@ { "name": "standards.TAP", "cat": "Entra (AAD) Standards", - "tag": [], + "tag": ["ZTNA21845", "ZTNA21846", "EIDSCAAT01", "EIDSCAAT02"], "helpText": "Enables TAP and sets the default TAP lifetime to 1 hour. This configuration also allows you to select if a TAP is single use or multi-logon.", "docsDescription": "Enables Temporary Password generation for the tenant.", "executiveText": "Enables temporary access passwords that IT administrators can generate for employees who are locked out or need emergency access to systems. These time-limited passwords provide a secure way to restore access without compromising long-term security policies.", @@ -740,7 +769,17 @@ { "name": "standards.CustomBannedPasswordList", "cat": "Entra (AAD) Standards", - "tag": ["CIS M365 5.0 (5.2.3.2)"], + "tag": [ + "CIS M365 5.0 (5.2.3.2)", + "ZTNA21848", + "ZTNA21849", + "ZTNA21850", + "EIDSCAPR01", + "EIDSCAPR02", + "EIDSCAPR03", + "EIDSCAPR05", + "EIDSCAPR06" + ], "helpText": "**Requires Entra ID P1.** Updates and enables the Entra ID custom banned password list with the supplied words. Enter words separated by commas or semicolons. Each word must be 4-16 characters long. Maximum 1,000 words allowed.", "docsDescription": "Updates and enables the Entra ID custom banned password list with the supplied words. This supplements the global banned password list maintained by Microsoft. The custom list is limited to 1,000 key base terms of 4-16 characters each. Entra ID will [block variations and common substitutions](https://learn.microsoft.com/en-us/entra/identity/authentication/tutorial-configure-custom-password-protection#configure-custom-banned-passwords) of these words in user passwords. [How are passwords evaluated?](https://learn.microsoft.com/en-us/entra/identity/authentication/concept-password-ban-bad#score-calculation)", "addedComponent": [ @@ -762,7 +801,7 @@ { "name": "standards.ExternalMFATrusted", "cat": "Entra (AAD) Standards", - "tag": [], + "tag": ["ZTNA21803", "ZTNA21804"], "helpText": "Sets the state of the Cross-tenant access setting to trust external MFA. This allows guest users to use their home tenant MFA to access your tenant.", "executiveText": "Allows external partners and vendors to use their own organization's multi-factor authentication when accessing company resources, streamlining collaboration while maintaining security standards. This reduces friction for external users while ensuring they still meet authentication requirements.", "addedComponent": [ @@ -794,7 +833,7 @@ { "name": "standards.DisableTenantCreation", "cat": "Entra (AAD) Standards", - "tag": ["CIS M365 5.0 (1.2.3)", "CISA (MS.AAD.6.1v1)"], + "tag": ["CIS M365 5.0 (1.2.3)", "CISA (MS.AAD.6.1v1)", "ZTNA21772", "ZTNA21787"], "helpText": "Restricts creation of M365 tenants to the Global Administrator or Tenant Creator roles.", "docsDescription": "Users by default are allowed to create M365 tenants. This disables that so only admins can create new M365 tenants.", "executiveText": "Prevents regular employees from creating new Microsoft 365 organizations, ensuring all new tenants are properly managed and controlled by IT administrators. This prevents unauthorized shadow IT environments and maintains centralized governance over Microsoft 365 resources.", @@ -818,7 +857,12 @@ "EIDSCA.CR03", "EIDSCA.CR04", "Essential 8 (1507)", - "NIST CSF 2.0 (PR.AA-05)" + "NIST CSF 2.0 (PR.AA-05)", + "ZTNA21869", + "EIDSCACR01", + "EIDSCACR02", + "EIDSCACR03", + "EIDSCACR04" ], "helpText": "Enables App consent admin requests for the tenant via the GA role. Does not overwrite existing reviewer settings", "docsDescription": "Enables the ability for users to request admin consent for applications. Should be used in conjunction with the \"Require admin consent for applications\" standards", @@ -840,7 +884,7 @@ { "name": "standards.NudgeMFA", "cat": "Entra (AAD) Standards", - "tag": [], + "tag": ["ZTNA21889"], "helpText": "Sets the state of the registration campaign for the tenant", "docsDescription": "Sets the state of the registration campaign for the tenant. If enabled nudges users to set up the Microsoft Authenticator during sign-in.", "executiveText": "Prompts employees to set up multi-factor authentication during login, gradually improving the organization's security posture by encouraging adoption of stronger authentication methods. This helps achieve better security compliance without forcing immediate mandatory changes.", @@ -883,7 +927,7 @@ { "name": "standards.DisableM365GroupUsers", "cat": "Entra (AAD) Standards", - "tag": ["CISA (MS.AAD.21.1v1)"], + "tag": ["CISA (MS.AAD.21.1v1)", "ZTNA21868"], "helpText": "Restricts M365 group creation to certain admin roles. This disables the ability to create Teams, SharePoint sites, Planner, etc", "docsDescription": "Users by default are allowed to create M365 groups. This restricts M365 group creation to certain admin roles. This disables the ability to create Teams, SharePoint sites, Planner, etc", "executiveText": "Restricts the creation of Microsoft 365 groups, Teams, and SharePoint sites to authorized administrators, preventing uncontrolled proliferation of collaboration spaces. This ensures proper governance, naming conventions, and resource management while maintaining oversight of all collaborative environments.", @@ -903,7 +947,8 @@ "CISA (MS.AAD.4.1v1)", "EIDSCA.AP10", "Essential 8 (1175)", - "NIST CSF 2.0 (PR.AA-05)" + "NIST CSF 2.0 (PR.AA-05)", + "EIDSCAAP10" ], "helpText": "Disables the ability for users to create App registrations in the tenant.", "docsDescription": "Disables the ability for users to create applications in Entra. Done to prevent breached accounts from creating an app to maintain access to the tenant, even after the breached account has been secured.", @@ -919,7 +964,7 @@ { "name": "standards.BitLockerKeysForOwnedDevice", "cat": "Entra (AAD) Standards", - "tag": [], + "tag": ["ZTNA21954"], "helpText": "Controls whether standard users can recover BitLocker keys for devices they own.", "docsDescription": "Updates the Microsoft Entra authorization policy that controls whether standard users can read BitLocker recovery keys for devices they own. Choose to restrict access for tighter security or allow self-service recovery when operational needs require it.", "executiveText": "Gives administrators centralized control over BitLocker recovery secrets—restrict access to ensure IT-assisted recovery flows, or allow self-service when rapid device unlocks are a priority.", @@ -952,7 +997,7 @@ { "name": "standards.DisableSecurityGroupUsers", "cat": "Entra (AAD) Standards", - "tag": ["CISA (MS.AAD.20.1v1)", "NIST CSF 2.0 (PR.AA-05)"], + "tag": ["CISA (MS.AAD.20.1v1)", "NIST CSF 2.0 (PR.AA-05)", "ZTNA21868"], "helpText": "Completely disables the creation of security groups by users. This also breaks the ability to manage groups themselves, or create Teams", "executiveText": "Restricts the creation of security groups to IT administrators only, preventing employees from creating unauthorized access groups that could bypass security controls. This ensures proper governance of access permissions and maintains centralized control over who can access what resources.", "addedComponent": [], @@ -1001,7 +1046,7 @@ { "name": "standards.DisableGuests", "cat": "Entra (AAD) Standards", - "tag": [], + "tag": ["ZTNA21858"], "helpText": "Blocks login for guest users that have not logged in for a number of days", "executiveText": "Automatically disables external guest accounts that haven't been used for a number of days, reducing security risks from dormant accounts while maintaining access for active external collaborators. This helps maintain a clean user directory and reduces potential attack vectors.", "addedComponent": [ @@ -1029,7 +1074,15 @@ "EIDSCA.AP08", "EIDSCA.AP09", "Essential 8 (1175)", - "NIST CSF 2.0 (PR.AA-05)" + "NIST CSF 2.0 (PR.AA-05)", + "ZTNA21772", + "ZTNA21774", + "ZTNA21807", + "EIDSCAAP08", + "EIDSCAAP09", + "EIDSCACP01", + "EIDSCACP03", + "EIDSCACP04" ], "helpText": "Disables users from being able to consent to applications, except for those specified in the field below", "docsDescription": "Requires users to get administrator consent before sharing data with applications. You can preapprove specific applications.", @@ -1066,7 +1119,7 @@ { "name": "standards.GuestInvite", "cat": "Entra (AAD) Standards", - "tag": ["CISA (MS.AAD.18.1v1)", "EIDSCA.AP04", "EIDSCA.AP07"], + "tag": ["CISA (MS.AAD.18.1v1)", "EIDSCA.AP04", "EIDSCA.AP07", "EIDSCAAP04"], "helpText": "This setting controls who can invite guests to your directory to collaborate on resources secured by your company, such as SharePoint sites or Azure resources.", "executiveText": "Controls who within the organization can invite external partners and vendors to access company resources, ensuring proper oversight of external access while enabling necessary business collaboration. This helps maintain security while supporting partnership and vendor relationships.", "addedComponent": [ @@ -1150,7 +1203,7 @@ { "name": "standards.SecurityDefaults", "cat": "Entra (AAD) Standards", - "tag": ["CISA (MS.AAD.11.1v1)"], + "tag": ["CISA (MS.AAD.11.1v1)", "ZTNA21843"], "helpText": "Enables security defaults for the tenant, for newer tenants this is enabled by default. Do not enable this feature if you use Conditional Access.", "docsDescription": "Enables SD for the tenant, which disables all forms of basic authentication and enforces users to configure MFA. Users are only prompted for MFA when a logon is considered 'suspect' by Microsoft.", "executiveText": "Activates Microsoft's baseline security configuration that requires multi-factor authentication and blocks legacy authentication methods. This provides essential security protection for organizations without complex conditional access policies, significantly improving security posture with minimal configuration.", @@ -1165,7 +1218,7 @@ { "name": "standards.DisableSMS", "cat": "Entra (AAD) Standards", - "tag": ["CIS M365 5.0 (2.3.5)", "EIDSCA.AS04", "NIST CSF 2.0 (PR.AA-03)"], + "tag": ["CIS M365 5.0 (2.3.5)", "EIDSCA.AS04", "NIST CSF 2.0 (PR.AA-03)", "EIDSCAAS04"], "helpText": "This blocks users from using SMS as an MFA method. If a user only has SMS as a MFA method, they will be unable to log in.", "docsDescription": "Disables SMS as an MFA method for the tenant. If a user only has SMS as a MFA method, they will be unable to sign in.", "executiveText": "Disables SMS text messages as a multi-factor authentication method due to security vulnerabilities like SIM swapping attacks. This forces users to adopt more secure authentication methods like authenticator apps or hardware tokens, significantly improving account security.", @@ -1180,7 +1233,7 @@ { "name": "standards.DisableVoice", "cat": "Entra (AAD) Standards", - "tag": ["CIS M365 5.0 (2.3.5)", "EIDSCA.AV01", "NIST CSF 2.0 (PR.AA-03)"], + "tag": ["CIS M365 5.0 (2.3.5)", "EIDSCA.AV01", "NIST CSF 2.0 (PR.AA-03)", "EIDSCAAV01"], "helpText": "This blocks users from using Voice call as an MFA method. If a user only has Voice as a MFA method, they will be unable to log in.", "docsDescription": "Disables Voice call as an MFA method for the tenant. If a user only has Voice call as a MFA method, they will be unable to sign in.", "executiveText": "Disables voice call authentication due to security vulnerabilities and social engineering risks. This forces users to adopt more secure authentication methods like authenticator apps, improving overall account security by eliminating phone-based attack vectors.", @@ -1248,7 +1301,10 @@ "Essential 8 (1504)", "Essential 8 (1173)", "Essential 8 (1401)", - "NIST CSF 2.0 (PR.AA-03)" + "NIST CSF 2.0 (PR.AA-03)", + "ZTNA21780", + "ZTNA21782", + "ZTNA21796" ], "helpText": "Enables per user MFA for all users.", "executiveText": "Requires all employees to use multi-factor authentication for enhanced account security, significantly reducing the risk of unauthorized access from compromised passwords. This fundamental security measure protects against the majority of account-based attacks and is essential for maintaining strong cybersecurity posture.", @@ -1521,7 +1577,7 @@ { "name": "standards.SpoofWarn", "cat": "Exchange Standards", - "tag": ["CIS M365 5.0 (6.2.3)"], + "tag": ["CIS M365 5.0 (6.2.3)", "ORCA111", "ORCA240", "CISAMSEXO71"], "helpText": "Adds or removes indicators to e-mail messages received from external senders in Outlook. Works on all Outlook clients/OWA", "docsDescription": "Adds or removes indicators to e-mail messages received from external senders in Outlook. You can read more about this feature on [Microsoft's Exchange Team Blog.](https://techcommunity.microsoft.com/t5/exchange-team-blog/native-external-sender-callouts-on-email-in-outlook/ba-p/2250098)", "executiveText": "Displays visual warnings in Outlook when emails come from external senders, helping employees identify potentially suspicious messages and reducing the risk of phishing attacks. This security feature makes it easier for staff to distinguish between internal and external communications.", @@ -1644,7 +1700,7 @@ { "name": "standards.AddDKIM", "cat": "Exchange Standards", - "tag": ["CIS M365 5.0 (2.1.9)"], + "tag": ["CIS M365 5.0 (2.1.9)", "ORCA108", "CISAMSEXO31"], "helpText": "Enables DKIM for all domains that currently support it", "executiveText": "Enables email authentication technology that digitally signs outgoing emails to verify they actually came from your organization. This prevents email spoofing, improves email deliverability, and protects the company's reputation by ensuring recipients can trust emails from your domains.", "addedComponent": [], @@ -1696,7 +1752,8 @@ "exo_mailboxaudit", "Essential 8 (1509)", "Essential 8 (1683)", - "NIST CSF 2.0 (DE.CM-09)" + "NIST CSF 2.0 (DE.CM-09)", + "CISAMSEXO131" ], "helpText": "Enables Mailbox auditing for all mailboxes and on tenant level. Disables audit bypass on all mailboxes. Unified Audit Log needs to be enabled for this standard to function.", "docsDescription": "Enables mailbox auditing on tenant level and for all mailboxes. Disables audit bypass on all mailboxes. By default Microsoft does not enable mailbox auditing for Resource Mailboxes, Public Folder Mailboxes and DiscoverySearch Mailboxes. Unified Audit Log needs to be enabled for this standard to function.", @@ -1735,6 +1792,39 @@ "powershellEquivalent": "Set-OrganizationConfig -AutoArchivingThresholdPercentage 80-100", "recommendedBy": [] }, + { + "name": "standards.AutoArchiveMailbox", + "cat": "Exchange Standards", + "tag": [], + "helpText": "Enables or disables the tenant policy that automatically provisions an archive mailbox when a user's primary mailbox reaches 90% of its quota.", + "docsDescription": "Enables or disables the tenant policy that automatically provisions an archive mailbox when a user's primary mailbox reaches 90% of its quota. This is separate from auto-archiving thresholds and does not enable archives for all users immediately.", + "executiveText": "Automatically provisions archive mailboxes only when users reach 90% of their mailbox capacity, reducing manual intervention and preventing mailbox quota issues without enabling archives for everyone.", + "addedComponent": [ + { + "type": "autoComplete", + "multiple": false, + "creatable": false, + "label": "Select value", + "name": "standards.AutoArchiveMailbox.state", + "options": [ + { + "label": "Enabled", + "value": "enabled" + }, + { + "label": "Disabled", + "value": "disabled" + } + ] + } + ], + "label": "Set auto enable archive mailbox state", + "impact": "Low Impact", + "impactColour": "info", + "addedDate": "2026-01-16", + "powershellEquivalent": "Set-OrganizationConfig -AutoEnableArchiveMailbox $true|$false", + "recommendedBy": [] + }, { "name": "standards.SendReceiveLimitTenant", "cat": "Exchange Standards", @@ -1913,7 +2003,7 @@ { "name": "standards.DisableExternalCalendarSharing", "cat": "Exchange Standards", - "tag": ["CIS M365 5.0 (1.3.3)", "exo_individualsharing"], + "tag": ["CIS M365 5.0 (1.3.3)", "exo_individualsharing", "ZTNA21803", "CISAMSEXO62"], "helpText": "Disables the ability for users to share their calendar with external users. Only for the default policy, so exclusions can be made if needed.", "docsDescription": "Disables external calendar sharing for the entire tenant. This is not a widely used feature, and it's therefore unlikely that this will impact users. Only for the default policy, so exclusions can be made if needed by making a new policy and assigning it to users.", "executiveText": "Prevents employees from sharing their calendars with external parties, protecting sensitive meeting information and internal schedules from unauthorized access. This security measure helps maintain confidentiality of business activities while still allowing internal collaboration.", @@ -1948,7 +2038,7 @@ { "name": "standards.DisableAdditionalStorageProviders", "cat": "Exchange Standards", - "tag": ["CIS M365 5.0 (6.5.3)", "exo_storageproviderrestricted"], + "tag": ["CIS M365 5.0 (6.5.3)", "exo_storageproviderrestricted", "ZTNA21817"], "helpText": "Disables the ability for users to open files in Outlook on the Web, from other providers such as Box, Dropbox, Facebook, Google Drive, OneDrive Personal, etc.", "docsDescription": "Disables additional storage providers in OWA. This is to prevent users from using personal storage providers like Dropbox, Google Drive, etc. Usually this has little user impact.", "executiveText": "Prevents employees from accessing personal cloud storage services like Dropbox or Google Drive through Outlook on the web, reducing data security risks and ensuring company information stays within approved corporate systems. This helps maintain data governance and prevents accidental data leaks.", @@ -2108,7 +2198,8 @@ "CIS M365 5.0 (6.3.1)", "exo_outlookaddins", "NIST CSF 2.0 (PR.AA-05)", - "NIST CSF 2.0 (PR.PS-05)" + "NIST CSF 2.0 (PR.PS-05)", + "ZTNA21817" ], "helpText": "Disables the ability for users to install add-ins in Outlook. This is to prevent users from installing malicious add-ins.", "docsDescription": "Disables users from being able to install add-ins in Outlook. Only admins are able to approve add-ins for the users. This is done to reduce the threat surface for data exfiltration.", @@ -2381,7 +2472,24 @@ "CIS M365 5.0 (2.1.1)", "mdo_safelinksforemail", "mdo_safelinksforOfficeApps", - "NIST CSF 2.0 (DE.CM-09)" + "NIST CSF 2.0 (DE.CM-09)", + "ORCA105", + "ORCA106", + "ORCA107", + "ORCA112", + "ORCA113", + "ORCA114", + "ORCA116", + "ORCA119", + "ORCA156", + "ORCA179", + "ORCA226", + "ORCA236", + "ORCA237", + "ORCA238", + "CISAMSEXO151", + "CISAMSEXO152", + "CISAMSEXO153" ], "helpText": "This creates a Safe Links policy that automatically scans, tracks, and and enables safe links for Email, Office, and Teams for both external and internal senders", "addedComponent": [ @@ -2435,7 +2543,30 @@ "mdo_antiphishingpolicies", "mdo_phishthresholdlevel", "CIS M365 5.0 (2.1.7)", - "NIST CSF 2.0 (DE.CM-09)" + "NIST CSF 2.0 (DE.CM-09)", + "ORCA104", + "ORCA115", + "ORCA180", + "ORCA220", + "ORCA221", + "ORCA222", + "ORCA223", + "ORCA228", + "ORCA229", + "ORCA230", + "ORCA233", + "ORCA234", + "ORCA235", + "ORCA239", + "ORCA242", + "ORCA243", + "ORCA244", + "ZTNA21784", + "ZTNA21817", + "ZTNA21819", + "CISAMSEXO111", + "CISAMSEXO112", + "CISAMSEXO113" ], "helpText": "This creates a Anti-Phishing policy that automatically enables Mailbox Intelligence and spoofing, optional switches for Mail tips.", "addedComponent": [ @@ -2656,7 +2787,9 @@ "mdo_safedocuments", "mdo_commonattachmentsfilter", "mdo_safeattachmentpolicy", - "NIST CSF 2.0 (DE.CM-09)" + "NIST CSF 2.0 (DE.CM-09)", + "ORCA158", + "ORCA227" ], "helpText": "This creates a Safe Attachment policy", "addedComponent": [ @@ -2807,7 +2940,16 @@ "mdo_zapspam", "mdo_zapphish", "mdo_zapmalware", - "NIST CSF 2.0 (DE.CM-09)" + "NIST CSF 2.0 (DE.CM-09)", + "ORCA121", + "ORCA124", + "ORCA232", + "ZTNA21817", + "ZTNA21819", + "CISAMSEXO95", + "CISAMSEXO101", + "CISAMSEXO102", + "CISAMSEXO103" ], "helpText": "This creates a Malware filter policy that enables the default File filter and Zero-hour auto purge for malware.", "addedComponent": [ @@ -2935,7 +3077,25 @@ { "name": "standards.SpamFilterPolicy", "cat": "Defender Standards", - "tag": [], + "tag": [ + "ORCA100", + "ORCA101", + "ORCA102", + "ORCA103", + "ORCA104", + "ORCA123", + "ORCA139", + "ORCA140", + "ORCA141", + "ORCA142", + "ORCA143", + "ORCA224", + "ORCA231", + "ORCA241", + "CISAMSEXO141", + "CISAMSEXO142", + "CISAMSEXO143" + ], "helpText": "This standard creates a Spam filter policy similar to the default strict policy.", "docsDescription": "This standard creates a Spam filter policy similar to the default strict policy, the following settings are configured to on by default: IncreaseScoreWithNumericIps, IncreaseScoreWithRedirectToOtherPort, MarkAsSpamEmptyMessages, MarkAsSpamJavaScriptInHtml, MarkAsSpamSpfRecordHardFail, MarkAsSpamFromAddressAuthFail, MarkAsSpamNdrBackscatter, MarkAsSpamBulkMail, InlineSafetyTipsEnabled, PhishZapEnabled, SpamZapEnabled", "addedComponent": [ @@ -3327,6 +3487,34 @@ "powershellEquivalent": "Set-QuarantinePolicy or New-QuarantinePolicy", "recommendedBy": [] }, + { + "name": "standards.IntuneWindowsDiagnostic", + "cat": "Intune Standards", + "tag": [], + "helpText": "**Some features require Windows E3 or equivalent licenses** Configures Windows diagnostic data settings for Intune. Enables features like Windows update reports, device readiness reports, and driver update reports. More information can be found in [Microsoft's documentation.](https://go.microsoft.com/fwlink/?linkid=2204384)", + "docsDescription": "Enables Windows diagnostic data in processor configuration for your Intune tenant. This setting is required for several Intune features including Windows feature update device readiness reports, compatibility risk reports, driver update reports, and update policy alerts. When enabled, your organization becomes the controller of Windows diagnostic data collected from managed devices, allowing Intune to use this data for reporting and update management features. More information can be found in [Microsoft's documentation.](https://go.microsoft.com/fwlink/?linkid=2204384)", + "executiveText": "Enables access to Windows Update reporting and compatibility analysis features in Intune by allowing the use of Windows diagnostic data. This unlocks important capabilities like device readiness reports for feature updates, driver update reports, and proactive alerts for update failures, helping IT teams plan and monitor Windows updates more effectively across the organization.", + "addedComponent": [ + { + "type": "switch", + "name": "standards.IntuneWindowsDiagnostic.areDataProcessorServiceForWindowsFeaturesEnabled", + "label": "Enable Windows data", + "defaultValue": false + }, + { + "type": "switch", + "name": "standards.IntuneWindowsDiagnostic.hasValidWindowsLicense", + "label": "Confirm ownership of the required Windows E3 or equivalent licenses (Enables Windows update app and driver compatibility reports)", + "defaultValue": false + } + ], + "label": "Set Intune Windows diagnostic data settings", + "impact": "Low Impact", + "impactColour": "info", + "addedDate": "2026-01-27", + "powershellEquivalent": "Graph API", + "recommendedBy": [] + }, { "name": "standards.intuneDeviceRetirementDays", "cat": "Intune Standards", @@ -3769,7 +3957,7 @@ { "name": "standards.intuneDeviceReg", "cat": "Intune Standards", - "tag": ["CISA (MS.AAD.17.1v1)"], + "tag": ["CISA (MS.AAD.17.1v1)", "ZTNA21801", "ZTNA21802"], "helpText": "Sets the maximum number of devices that can be registered by a user. A value of 0 disables device registration by users", "executiveText": "Limits how many devices each employee can register for corporate access, preventing excessive device proliferation while accommodating legitimate business needs. This helps maintain security oversight and prevents potential abuse of device registration privileges.", "addedComponent": [ @@ -3790,7 +3978,7 @@ { "name": "standards.intuneRequireMFA", "cat": "Intune Standards", - "tag": [], + "tag": ["ZTNA21782", "ZTNA21796", "ZTNA21872"], "helpText": "Requires MFA for all users to register devices with Intune. This is useful when not using Conditional Access.", "executiveText": "Requires employees to use multi-factor authentication when registering devices for corporate access, adding an extra security layer to prevent unauthorized device enrollment. This helps ensure only legitimate users can connect their devices to company systems.", "label": "Require Multi-factor Authentication to register or join devices with Microsoft Entra", @@ -3940,7 +4128,7 @@ { "name": "standards.SPDisallowInfectedFiles", "cat": "SharePoint Standards", - "tag": ["CIS M365 5.0 (7.3.1)", "CISA (MS.SPO.3.1v1)", "NIST CSF 2.0 (DE.CM-09)"], + "tag": ["CIS M365 5.0 (7.3.1)", "CISA (MS.SPO.3.1v1)", "NIST CSF 2.0 (DE.CM-09)", "ZTNA21817"], "helpText": "Ensure Office 365 SharePoint infected files are disallowed for download", "executiveText": "Prevents employees from downloading files that have been identified as containing malware or viruses from SharePoint and OneDrive. This security measure protects against malware distribution through file sharing while maintaining access to clean, safe documents.", "addedComponent": [], @@ -3982,7 +4170,7 @@ { "name": "standards.SPExternalUserExpiration", "cat": "SharePoint Standards", - "tag": ["CIS M365 5.0 (7.2.9)", "CISA (MS.SPO.1.5v1)"], + "tag": ["CIS M365 5.0 (7.2.9)", "CISA (MS.SPO.1.5v1)", "ZTNA21803", "ZTNA21804", "ZTNA21858"], "helpText": "Ensure guest access to a site or OneDrive will expire automatically", "executiveText": "Automatically expires external user access to SharePoint sites and OneDrive after a specified period, reducing security risks from forgotten or unnecessary guest accounts. This ensures external access is regularly reviewed and maintained only when actively needed.", "addedComponent": [ @@ -4007,7 +4195,7 @@ { "name": "standards.SPEmailAttestation", "cat": "SharePoint Standards", - "tag": ["CIS M365 5.0 (7.2.10)", "CISA (MS.SPO.1.6v1)"], + "tag": ["CIS M365 5.0 (7.2.10)", "CISA (MS.SPO.1.6v1)", "ZTNA21803", "ZTNA21804"], "helpText": "Ensure re-authentication with verification code is restricted", "executiveText": "Requires external users to periodically re-verify their identity through email verification codes when accessing SharePoint resources, adding an extra security layer for external collaboration. This helps ensure continued legitimacy of external access over time.", "addedComponent": [ @@ -4032,7 +4220,13 @@ { "name": "standards.DefaultSharingLink", "cat": "SharePoint Standards", - "tag": ["CIS M365 5.0 (7.2.7)", "CIS M365 5.0 (7.2.11)", "CISA (MS.SPO.1.4v1)"], + "tag": [ + "CIS M365 5.0 (7.2.7)", + "CIS M365 5.0 (7.2.11)", + "CISA (MS.SPO.1.4v1)", + "ZTNA21803", + "ZTNA21804" + ], "helpText": "Configure the SharePoint default sharing link type and permission. This setting controls both the type of sharing link created by default and the permission level assigned to those links.", "docsDescription": "Sets the default sharing link type (Direct or Internal) and permission (View) in SharePoint and OneDrive. Direct sharing means links only work for specific people, while Internal sharing means links work for anyone in the organization. Setting the view permission as the default ensures that users must deliberately select the edit permission when sharing a link, reducing the risk of unintentionally granting edit privileges.", "executiveText": "Configures SharePoint default sharing links to implement the principle of least privilege for document sharing. This security measure reduces the risk of accidental data modification while maintaining collaboration functionality, requiring users to explicitly select Edit permissions when necessary. The sharing type setting controls whether links are restricted to specific recipients or available to the entire organization. This reduces the risk of accidental data exposure through link sharing.", @@ -4135,7 +4329,9 @@ "CIS M365 5.0 (7.2.1)", "spo_legacy_auth", "CISA (MS.AAD.3.1v1)", - "NIST CSF 2.0 (PR.IR-01)" + "NIST CSF 2.0 (PR.IR-01)", + "ZTNA21776", + "ZTNA21797" ], "helpText": "Disables the ability to authenticate with SharePoint using legacy authentication methods. Any applications that use legacy authentication will need to be updated to use modern authentication.", "docsDescription": "Disables the ability for users and applications to access SharePoint via legacy basic authentication. This will likely not have any user impact, but will block systems/applications depending on basic auth or the SharePointOnlineCredentials class.", @@ -4151,7 +4347,13 @@ { "name": "standards.sharingCapability", "cat": "SharePoint Standards", - "tag": ["CIS M365 5.0 (7.2.3)", "CISA (MS.AAD.14.1v1)", "CISA (MS.SPO.1.1v1)"], + "tag": [ + "CIS M365 5.0 (7.2.3)", + "CISA (MS.AAD.14.1v1)", + "CISA (MS.SPO.1.1v1)", + "ZTNA21803", + "ZTNA21804" + ], "helpText": "Sets the default sharing level for OneDrive and SharePoint. This is a tenant wide setting and overrules any settings set on the site level", "executiveText": "Defines the organization's default policy for sharing files and folders in SharePoint and OneDrive, balancing collaboration needs with security requirements. This fundamental setting determines whether employees can share with external users, anonymous links, or only internal colleagues.", "addedComponent": [ @@ -4190,7 +4392,13 @@ { "name": "standards.DisableReshare", "cat": "SharePoint Standards", - "tag": ["CIS M365 5.0 (7.2.5)", "CISA (MS.AAD.14.2v1)", "CISA (MS.SPO.1.2v1)"], + "tag": [ + "CIS M365 5.0 (7.2.5)", + "CISA (MS.AAD.14.2v1)", + "CISA (MS.SPO.1.2v1)", + "ZTNA21803", + "ZTNA21804" + ], "helpText": "Disables the ability for external users to share files they don't own. Sharing links can only be made for People with existing access", "docsDescription": "Disables the ability for external users to share files they don't own. Sharing links can only be made for People with existing access. This is a tenant wide setting and overrules any settings set on the site level", "executiveText": "Prevents external users from sharing company documents with additional people, maintaining control over document distribution and preventing unauthorized access expansion. This security measure ensures that external sharing remains within intended boundaries set by internal employees.", @@ -4254,7 +4462,7 @@ { "name": "standards.unmanagedSync", "cat": "SharePoint Standards", - "tag": ["CIS M365 5.0 (7.2.3)", "CISA (MS.SPO.2.1v1)", "NIST CSF 2.0 (PR.AA-05)"], + "tag": ["CIS M365 5.0 (7.2.3)", "CISA (MS.SPO.2.1v1)", "NIST CSF 2.0 (PR.AA-05)", "ZTNA24824"], "helpText": "Entra P1 required. Block or limit access to SharePoint and OneDrive content from unmanaged devices (those not hybrid AD joined or compliant in Intune). These controls rely on Microsoft Entra Conditional Access policies and can take up to 24 hours to take effect.", "docsDescription": "Entra P1 required. Block or limit access to SharePoint and OneDrive content from unmanaged devices (those not hybrid AD joined or compliant in Intune). These controls rely on Microsoft Entra Conditional Access policies and can take up to 24 hours to take effect. 0 = Allow Access, 1 = Allow limited, web-only access, 2 = Block access. All information about this can be found in Microsofts documentation [here.](https://learn.microsoft.com/en-us/sharepoint/control-access-from-unmanaged-devices)", "executiveText": "Restricts access to company files from personal or unmanaged devices, ensuring corporate data can only be accessed from properly secured and monitored devices. This critical security control prevents data leaks while allowing controlled access through web browsers when necessary.", @@ -4288,7 +4496,13 @@ { "name": "standards.sharingDomainRestriction", "cat": "SharePoint Standards", - "tag": ["CIS M365 5.0 (7.2.6)", "CISA (MS.AAD.14.3v1)", "CISA (MS.SPO.1.3v1)"], + "tag": [ + "CIS M365 5.0 (7.2.6)", + "CISA (MS.AAD.14.3v1)", + "CISA (MS.SPO.1.3v1)", + "ZTNA21803", + "ZTNA21804" + ], "helpText": "Restricts sharing to only users with the specified domain. This is useful for organizations that only want to share with their own domain.", "executiveText": "Controls which external domains employees can share files with, enabling secure collaboration with trusted partners while blocking sharing with unauthorized organizations. This targeted approach maintains necessary business relationships while preventing data exposure to unknown entities.", "addedComponent": [ @@ -5149,6 +5363,12 @@ "valueField": "GUID", "queryKey": "ListTransportRulesTemplates" } + }, + { + "type": "switch", + "label": "Overwrite existing transport rules", + "name": "overwrite", + "defaultValue": true } ] }, @@ -5375,6 +5595,20 @@ "powershellEquivalent": "Set-OwaMailboxPolicy -Identity \"OwaMailboxPolicy-Default\" -ConditionalAccessPolicy ReadOnlyPlusAttachmentsBlocked", "recommendedBy": ["Microsoft Zero Trust", "CIPP"] }, + { + "name": "standards.LegacyEmailReportAddins", + "deprecated": true, + "cat": "Exchange Standards", + "tag": [], + "helpText": "Removes legacy Report Phishing and Report Message Outlook add-ins.", + "executiveText": "The legacy Report Phishing and Report Message Outlook add-ins are security issues with the add-in which makes them unsafe for the organization.", + "label": "Remove legacy Outlook Report add-ins", + "impact": "Low Impact", + "impactColour": "info", + "addedDate": "2025-08-26", + "powershellEquivalent": "None", + "recommendedBy": ["Microsoft"] + }, { "name": "standards.DeployCheckChromeExtension", "cat": "Intune Standards", @@ -5401,13 +5635,6 @@ "label": "Enable CIPP reporting", "defaultValue": true }, - { - "type": "textField", - "name": "standards.DeployCheckChromeExtension.cippServerUrl", - "label": "CIPP Server URL", - "placeholder": "https://YOUR-CIPP-SERVER-URL", - "required": false - }, { "type": "textField", "name": "standards.DeployCheckChromeExtension.customRulesUrl", diff --git a/src/hooks/use-guid-resolver.js b/src/hooks/use-guid-resolver.js index 1325722fc872..f51a9a2bb201 100644 --- a/src/hooks/use-guid-resolver.js +++ b/src/hooks/use-guid-resolver.js @@ -1,6 +1,6 @@ import { useState, useCallback, useRef, useEffect } from "react"; -import { ApiPostCall } from "/src/api/ApiCall"; -import { useSettings } from "/src/hooks/use-settings"; +import { ApiPostCall } from "../api/ApiCall"; +import { useSettings } from "./use-settings"; // Function to check if a string is a GUID const isGuid = (str) => { diff --git a/src/hooks/use-permissions.js b/src/hooks/use-permissions.js index 6b7973c164f8..3fa9b6185707 100644 --- a/src/hooks/use-permissions.js +++ b/src/hooks/use-permissions.js @@ -1,6 +1,6 @@ import { useCallback } from "react"; -import { ApiGetCall } from "/src/api/ApiCall"; -import { hasAccess, hasPermission, hasRole } from "/src/utils/permissions"; +import { ApiGetCall } from "../api/ApiCall"; +import { hasAccess, hasPermission, hasRole } from "../utils/permissions"; /** * Hook for checking user permissions and roles diff --git a/src/hooks/use-securescore.js b/src/hooks/use-securescore.js index a51dc18d9138..66287c8f87bd 100644 --- a/src/hooks/use-securescore.js +++ b/src/hooks/use-securescore.js @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { ApiGetCall } from "../api/ApiCall"; import { useSettings } from "./use-settings"; -import standards from "/src/data/standards.json"; +import standards from "../data/standards.json"; export function useSecureScore({ waiting = true } = {}) { const currentTenant = useSettings().currentTenant; diff --git a/src/index.js b/src/index.js index 6f78fb85ec69..141824e0c90c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "./layouts/index.js"; const Page = () => { const pageTitle = "Dashboard"; diff --git a/src/layouts/HeaderedTabbedLayout.jsx b/src/layouts/HeaderedTabbedLayout.jsx index ece1d0659924..1b5585a6812a 100644 --- a/src/layouts/HeaderedTabbedLayout.jsx +++ b/src/layouts/HeaderedTabbedLayout.jsx @@ -15,7 +15,7 @@ import { Tabs, Typography, } from "@mui/material"; -import { ActionsMenu } from "/src/components/actions-menu"; +import { ActionsMenu } from "../components/actions-menu"; import { useMediaQuery } from "@mui/material"; export const HeaderedTabbedLayout = (props) => { diff --git a/src/layouts/TabbedLayout.jsx b/src/layouts/TabbedLayout.jsx index f92712a89127..b268d6680cd5 100644 --- a/src/layouts/TabbedLayout.jsx +++ b/src/layouts/TabbedLayout.jsx @@ -1,13 +1,19 @@ import { usePathname, useRouter } from "next/navigation"; import { Box, Divider, Stack, Tab, Tabs } from "@mui/material"; +import { useSearchParams } from "next/navigation"; export const TabbedLayout = (props) => { const { tabOptions, children } = props; const router = useRouter(); const pathname = usePathname(); + const searchParams = useSearchParams(); const handleTabsChange = (event, value) => { - router.push(value); + // Preserve existing query parameters when changing tabs + const currentParams = new URLSearchParams(searchParams.toString()); + const queryString = currentParams.toString(); + const newPath = queryString ? `${value}?${queryString}` : value; + router.push(newPath); }; const currentTab = tabOptions.find((option) => option.path === pathname); diff --git a/src/layouts/config.js b/src/layouts/config.js index 8923b99832d1..07ca170abc5b 100644 --- a/src/layouts/config.js +++ b/src/layouts/config.js @@ -73,6 +73,11 @@ export const nativeMenuItems = [ path: "/identity/administration/jit-admin", permissions: ["Identity.Role.*"], }, + { + title: "JIT Admin Templates", + path: "/identity/administration/jit-admin-templates", + permissions: ["Identity.Role.*"], + }, { title: "Offboarding Wizard", path: "/identity/administration/offboarding-wizard", @@ -711,6 +716,16 @@ export const nativeMenuItems = [ path: "/email/reports/mailbox-cas-settings", permissions: ["Exchange.Mailbox.*"], }, + { + title: "Mailbox Permissions", + path: "/email/reports/mailbox-permissions", + permissions: ["Exchange.Mailbox.*"], + }, + { + title: "Calendar Permissions", + path: "/email/reports/calendar-permissions", + permissions: ["Exchange.Mailbox.*"], + }, { title: "Anti-Phishing Filters", path: "/email/reports/antiphishing-filters", @@ -875,7 +890,7 @@ export const nativeMenuItems = [ title: "Setup Wizard", path: "/onboardingv2", roles: ["admin", "superadmin"], - permissions: ["CIPP.Core.*"], + permissions: ["CIPP.AppSettings.*"], }, { title: "Integrations", @@ -887,7 +902,7 @@ export const nativeMenuItems = [ title: "Custom Data", path: "/cipp/custom-data/directory-extensions", roles: ["admin", "superadmin"], - permissions: ["CIPP.Core.*"], + permissions: ["CIPP.AppSettings.*"], }, { title: "Advanced", diff --git a/src/layouts/index.js b/src/layouts/index.js index 92774c200e4d..b33cb452bf4f 100644 --- a/src/layouts/index.js +++ b/src/layouts/index.js @@ -23,7 +23,7 @@ import { Box, Container, Grid } from "@mui/system"; import { CippImageCard } from "../components/CippCards/CippImageCard"; import Page from "../pages/onboardingv2"; import { useDialog } from "../hooks/use-dialog"; -import { nativeMenuItems } from "/src/layouts/config"; +import { nativeMenuItems } from "./config"; import { CippBreadcrumbNav } from "../components/CippComponents/CippBreadcrumbNav"; const SIDE_NAV_WIDTH = 270; diff --git a/src/layouts/side-nav.js b/src/layouts/side-nav.js index 7ecb9dc3c440..c9ef006bea2d 100644 --- a/src/layouts/side-nav.js +++ b/src/layouts/side-nav.js @@ -142,6 +142,12 @@ export const SideNav = (props) => { imagesrc: theme === "light" ? "/sponsors/RoB-light.png" : "/sponsors/RoB.png", priority: 1, }, + { + link: "https://www.relentlesssolutions.com/", + imagesrc: + theme === "light" ? "/sponsors/relentless-light.png" : "/sponsors/relentless-dark.png", + priority: 1, + }, ]; const randomSponsorImage = () => { diff --git a/src/pages/cipp/advanced/diagnostics.js b/src/pages/cipp/advanced/diagnostics.js index c37e5ef5c478..962eced7a27a 100644 --- a/src/pages/cipp/advanced/diagnostics.js +++ b/src/pages/cipp/advanced/diagnostics.js @@ -1,7 +1,7 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import CippDiagnosticsFilter from "/src/components/CippTable/CippDiagnosticsFilter"; -import { CippPropertyListCard } from "/src/components/CippCards/CippPropertyListCard"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import { CippTablePage } from "../../../components/CippComponents/CippTablePage.jsx"; +import CippDiagnosticsFilter from "../../../components/CippTable/CippDiagnosticsFilter"; +import { CippPropertyListCard } from "../../../components/CippCards/CippPropertyListCard"; import { useState } from "react"; import { Grid } from "@mui/system"; import { Box, Typography, Chip, Stack, Divider } from "@mui/material"; diff --git a/src/pages/cipp/advanced/exchange-cmdlets.js b/src/pages/cipp/advanced/exchange-cmdlets.js index 14f4ba74ec91..9e6e61bcd379 100644 --- a/src/pages/cipp/advanced/exchange-cmdlets.js +++ b/src/pages/cipp/advanced/exchange-cmdlets.js @@ -1,4 +1,4 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import { Alert, Button, @@ -12,10 +12,10 @@ import { } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm } from "react-hook-form"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { ApiPostCall } from "/src/api/ApiCall"; -import CippButtonCard from "/src/components/CippCards/CippButtonCard"; -import { CippDataTable } from "/src/components/CippTable/CippDataTable"; +import CippFormComponent from "../../../components/CippComponents/CippFormComponent"; +import { ApiPostCall } from "../../../api/ApiCall"; +import CippButtonCard from "../../../components/CippCards/CippButtonCard"; +import { CippDataTable } from "../../../components/CippTable/CippDataTable"; import { useState, useEffect } from "react"; import { Search, Close } from "@mui/icons-material"; import { CippFormTenantSelector } from "../../../components/CippComponents/CippFormTenantSelector"; diff --git a/src/pages/cipp/advanced/table-maintenance.js b/src/pages/cipp/advanced/table-maintenance.js index d7e317b5c6b5..fea95d6e3774 100644 --- a/src/pages/cipp/advanced/table-maintenance.js +++ b/src/pages/cipp/advanced/table-maintenance.js @@ -1,8 +1,8 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import { useEffect, useState } from "react"; import { ApiPostCall } from "../../../api/ApiCall"; -import { CippPropertyListCard } from "/src/components/CippCards/CippPropertyListCard"; // Fixed import -import { CippDataTable } from "/src/components/CippTable/CippDataTable"; // Fixed import +import { CippPropertyListCard } from "../../../components/CippCards/CippPropertyListCard"; // Fixed import +import { CippDataTable } from "../../../components/CippTable/CippDataTable"; // Fixed import import { useDialog } from "../../../hooks/use-dialog"; import { Box, diff --git a/src/pages/cipp/advanced/timers.js b/src/pages/cipp/advanced/timers.js index 116d27cd836f..669500a24d80 100644 --- a/src/pages/cipp/advanced/timers.js +++ b/src/pages/cipp/advanced/timers.js @@ -1,9 +1,9 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import { SvgIcon, Button } from "@mui/material"; import { Refresh, PlayArrow } from "@mui/icons-material"; import { ApiPostCall } from "../../../api/ApiCall"; import { useEffect, useState } from "react"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage"; +import { CippTablePage } from "../../../components/CippComponents/CippTablePage"; import { useDialog } from "../../../hooks/use-dialog"; import { CippApiDialog } from "../../../components/CippComponents/CippApiDialog"; diff --git a/src/pages/cipp/custom-data/directory-extensions/add.js b/src/pages/cipp/custom-data/directory-extensions/add.js index 975c7be997de..0b7d2e11e6ee 100644 --- a/src/pages/cipp/custom-data/directory-extensions/add.js +++ b/src/pages/cipp/custom-data/directory-extensions/add.js @@ -1,6 +1,6 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useFormState } from "react-hook-form"; -import { ApiPostCall } from "/src/api/ApiCall"; +import { ApiPostCall } from "../../../../api/ApiCall"; import { useRouter } from "next/router"; import { Box, @@ -12,9 +12,9 @@ import { CardActions, } from "@mui/material"; -import CippPageCard from "/src/components/CippCards/CippPageCard"; -import { CippApiResults } from "/src/components/CippComponents/CippApiResults"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippPageCard from "../../../../components/CippCards/CippPageCard"; +import { CippApiResults } from "../../../../components/CippComponents/CippApiResults"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; const availableTargetObjects = [ { value: "User", label: "User" }, diff --git a/src/pages/cipp/custom-data/directory-extensions/index.js b/src/pages/cipp/custom-data/directory-extensions/index.js index 891342e97ce1..5a223007c863 100644 --- a/src/pages/cipp/custom-data/directory-extensions/index.js +++ b/src/pages/cipp/custom-data/directory-extensions/index.js @@ -1,6 +1,6 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Alert, Button, Link, SvgIcon } from "@mui/material"; import { Add } from "@mui/icons-material"; import tabOptions from "../tabOptions"; diff --git a/src/pages/cipp/custom-data/mappings/add.js b/src/pages/cipp/custom-data/mappings/add.js index cd77fc9eff6e..601fd777aa9e 100644 --- a/src/pages/cipp/custom-data/mappings/add.js +++ b/src/pages/cipp/custom-data/mappings/add.js @@ -1,12 +1,12 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useFormState } from "react-hook-form"; -import { ApiPostCall } from "/src/api/ApiCall"; +import { ApiPostCall } from "../../../../api/ApiCall"; import { useRouter } from "next/router"; import { Button, Stack, CardContent, CardActions } from "@mui/material"; -import CippPageCard from "/src/components/CippCards/CippPageCard"; -import { CippApiResults } from "/src/components/CippComponents/CippApiResults"; -import CippCustomDataMappingForm from "/src/components/CippFormPages/CippCustomDataMappingForm"; +import CippPageCard from "../../../../components/CippCards/CippPageCard"; +import { CippApiResults } from "../../../../components/CippComponents/CippApiResults"; +import CippCustomDataMappingForm from "../../../../components/CippFormPages/CippCustomDataMappingForm"; const Page = () => { const router = useRouter(); diff --git a/src/pages/cipp/custom-data/mappings/edit.js b/src/pages/cipp/custom-data/mappings/edit.js index 55a2bbc648e8..ca48da77ed14 100644 --- a/src/pages/cipp/custom-data/mappings/edit.js +++ b/src/pages/cipp/custom-data/mappings/edit.js @@ -1,13 +1,13 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useFormState } from "react-hook-form"; import { useRouter } from "next/router"; import { useEffect } from "react"; -import { ApiPostCall, ApiGetCall } from "/src/api/ApiCall"; +import { ApiPostCall, ApiGetCall } from "../../../../api/ApiCall"; import { Button, Stack, CardContent, CardActions, Skeleton } from "@mui/material"; -import CippPageCard from "/src/components/CippCards/CippPageCard"; -import { CippApiResults } from "/src/components/CippComponents/CippApiResults"; -import CippCustomDataMappingForm from "/src/components/CippFormPages/CippCustomDataMappingForm"; +import CippPageCard from "../../../../components/CippCards/CippPageCard"; +import { CippApiResults } from "../../../../components/CippComponents/CippApiResults"; +import CippCustomDataMappingForm from "../../../../components/CippFormPages/CippCustomDataMappingForm"; const Page = () => { const router = useRouter(); diff --git a/src/pages/cipp/custom-data/mappings/index.js b/src/pages/cipp/custom-data/mappings/index.js index 7579685d1f62..f25c17fd83e1 100644 --- a/src/pages/cipp/custom-data/mappings/index.js +++ b/src/pages/cipp/custom-data/mappings/index.js @@ -1,6 +1,6 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Alert, Button, SvgIcon, Typography } from "@mui/material"; import { Add } from "@mui/icons-material"; import tabOptions from "../tabOptions"; diff --git a/src/pages/cipp/custom-data/schema-extensions/add.js b/src/pages/cipp/custom-data/schema-extensions/add.js index 07be1c409067..4d8fb8760c80 100644 --- a/src/pages/cipp/custom-data/schema-extensions/add.js +++ b/src/pages/cipp/custom-data/schema-extensions/add.js @@ -1,6 +1,6 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useWatch, useFormState } from "react-hook-form"; -import { ApiPostCall } from "/src/api/ApiCall"; +import { ApiPostCall } from "../../../../api/ApiCall"; import { useRouter } from "next/router"; import { Box, @@ -14,9 +14,9 @@ import { } from "@mui/material"; import { AddCircle, RemoveCircle } from "@mui/icons-material"; -import CippPageCard from "/src/components/CippCards/CippPageCard"; -import { CippApiResults } from "/src/components/CippComponents/CippApiResults"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippPageCard from "../../../../components/CippCards/CippPageCard"; +import { CippApiResults } from "../../../../components/CippComponents/CippApiResults"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; const availableTargetTypes = [ { value: "user", label: "User" }, diff --git a/src/pages/cipp/custom-data/schema-extensions/index.js b/src/pages/cipp/custom-data/schema-extensions/index.js index daca34251209..fab26e70f51c 100644 --- a/src/pages/cipp/custom-data/schema-extensions/index.js +++ b/src/pages/cipp/custom-data/schema-extensions/index.js @@ -1,6 +1,6 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Alert, Button, Link, SvgIcon, Typography } from "@mui/material"; import { Add, Block, CheckCircleOutline } from "@mui/icons-material"; import tabOptions from "../tabOptions"; diff --git a/src/pages/cipp/extension-sync/index.js b/src/pages/cipp/extension-sync/index.js index c4d91f784d45..7fe6fa1c271e 100644 --- a/src/pages/cipp/extension-sync/index.js +++ b/src/pages/cipp/extension-sync/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import { CippTablePage } from "../../../components/CippComponents/CippTablePage.jsx"; import { Button } from "@mui/material"; import { Refresh } from "@mui/icons-material"; diff --git a/src/pages/cipp/integrations/configure.js b/src/pages/cipp/integrations/configure.js index 3cae174bbb56..330e03d55b13 100644 --- a/src/pages/cipp/integrations/configure.js +++ b/src/pages/cipp/integrations/configure.js @@ -9,18 +9,18 @@ import { Tabs, Typography, } from "@mui/material"; -import CippIntegrationSettings from "/src/components/CippIntegrations/CippIntegrationSettings"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippIntegrationSettings from "../../../components/CippIntegrations/CippIntegrationSettings"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import { useForm } from "react-hook-form"; -import { useSettings } from "/src/hooks/use-settings"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { useSettings } from "../../../hooks/use-settings"; +import { ApiGetCall } from "../../../api/ApiCall"; import { useRouter } from "next/router"; -import extensions from "/src/data/Extensions.json"; +import extensions from "../../../data/Extensions.json"; import { useEffect } from "react"; import { ArrowPathIcon, ArrowTopRightOnSquareIcon, BeakerIcon } from "@heroicons/react/24/outline"; import { SvgIcon } from "@mui/material"; import { useState } from "react"; -import { CippApiResults } from "/src/components/CippComponents/CippApiResults"; +import { CippApiResults } from "../../../components/CippComponents/CippApiResults"; import CippPageCard from "../../../components/CippCards/CippPageCard"; import CippIntegrationTenantMapping from "../../../components/CippIntegrations/CippIntegrationTenantMapping"; import CippIntegrationFieldMapping from "../../../components/CippIntegrations/CippIntegrationFieldMapping"; diff --git a/src/pages/cipp/integrations/index.js b/src/pages/cipp/integrations/index.js index 033dac2aaf06..e6ae38f8de12 100644 --- a/src/pages/cipp/integrations/index.js +++ b/src/pages/cipp/integrations/index.js @@ -1,4 +1,4 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import { Box, Button, @@ -11,13 +11,13 @@ import { Stack, Typography, } from "@mui/material"; -import extensions from "/src/data/Extensions"; +import extensions from "../../../data/Extensions"; import { Sync } from "@mui/icons-material"; -import { useSettings } from "/src/hooks/use-settings"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { useSettings } from "../../../hooks/use-settings"; +import { ApiGetCall } from "../../../api/ApiCall"; import Link from "next/link"; import { Grid } from "@mui/system"; -import { CippHead } from "/src/components/CippComponents/CippHead"; +import { CippHead } from "../../../components/CippComponents/CippHead"; const Page = () => { const settings = useSettings(); diff --git a/src/pages/cipp/integrations/sync.js b/src/pages/cipp/integrations/sync.js index 27b6fb1dfa8f..952dae276db2 100644 --- a/src/pages/cipp/integrations/sync.js +++ b/src/pages/cipp/integrations/sync.js @@ -1,7 +1,7 @@ import { Box } from "@mui/system"; import CippPageCard from "../../../components/CippCards/CippPageCard"; import { CippDataTable } from "../../../components/CippTable/CippDataTable"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; const Page = () => { const simpleColumns = [ diff --git a/src/pages/cipp/logs/index.js b/src/pages/cipp/logs/index.js index 18f8ad68174c..55dc8bdcac75 100644 --- a/src/pages/cipp/logs/index.js +++ b/src/pages/cipp/logs/index.js @@ -1,6 +1,6 @@ import { useState } from "react"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import { CippTablePage } from "../../../components/CippComponents/CippTablePage.jsx"; import { Button, Accordion, @@ -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..521026693fd8 100644 --- a/src/pages/cipp/logs/logentry.js +++ b/src/pages/cipp/logs/logentry.js @@ -1,22 +1,23 @@ import { useRouter } from "next/router"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import { ApiGetCall } from "../../../api/ApiCall"; import { Button, SvgIcon, Box, Container, Chip } from "@mui/material"; import { Stack } from "@mui/system"; import ArrowLeftIcon from "@mui/icons-material/ArrowLeft"; -import { CippPropertyListCard } from "/src/components/CippCards/CippPropertyListCard"; -import { CippInfoBar } from "/src/components/CippCards/CippInfoBar"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; +import { CippPropertyListCard } from "../../../components/CippCards/CippPropertyListCard"; +import { CippInfoBar } from "../../../components/CippCards/CippInfoBar"; +import CippFormSkeleton from "../../../components/CippFormPages/CippFormSkeleton"; 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/cipp/preferences.js b/src/pages/cipp/preferences.js index 011acfb4c724..bd15fbf9ae70 100644 --- a/src/pages/cipp/preferences.js +++ b/src/pages/cipp/preferences.js @@ -1,14 +1,14 @@ import Head from "next/head"; import { Box, Container, Stack } from "@mui/material"; import { Grid } from "@mui/system"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../layouts/index.js"; import { CippPropertyListCard } from "../../components/CippCards/CippPropertyListCard"; import CippFormComponent from "../../components/CippComponents/CippFormComponent"; import { useForm, useWatch } from "react-hook-form"; import { useSettings } from "../../hooks/use-settings"; import countryList from "../../data/countryList.json"; import { CippSettingsSideBar } from "../../components/CippComponents/CippSettingsSideBar"; -import CippDevOptions from "/src/components/CippComponents/CippDevOptions"; +import CippDevOptions from "../../components/CippComponents/CippDevOptions"; import { CippOffboardingDefaultSettings } from "../../components/CippComponents/CippOffboardingDefaultSettings"; import { ApiGetCall } from "../../api/ApiCall"; import { getCippFormatting } from "../../utils/get-cipp-formatting"; diff --git a/src/pages/cipp/scheduler/index.js b/src/pages/cipp/scheduler/index.js index eaf46486abe6..f3a877ea79ba 100644 --- a/src/pages/cipp/scheduler/index.js +++ b/src/pages/cipp/scheduler/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippTablePage from "/src/components/CippComponents/CippTablePage"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import CippTablePage from "../../../components/CippComponents/CippTablePage"; import { Button } from "@mui/material"; import { CalendarDaysIcon } from "@heroicons/react/24/outline"; import { useState } from "react"; @@ -46,7 +46,9 @@ const Page = () => { ]; const offCanvas = { - children: (extendedData) => , + children: (extendedData) => ( + + ), size: "xl", actions: actions, }; diff --git a/src/pages/cipp/scheduler/job.jsx b/src/pages/cipp/scheduler/job.jsx index afef6c8a4c9c..11b00051a48d 100644 --- a/src/pages/cipp/scheduler/job.jsx +++ b/src/pages/cipp/scheduler/job.jsx @@ -1,6 +1,6 @@ import { useForm } from "react-hook-form"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "../../../components/CippFormPages/CippFormPage"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import CippSchedulerForm from "../../../components/CippFormPages/CippSchedulerForm"; import { useRouter } from "next/router"; diff --git a/src/pages/cipp/scheduler/task.js b/src/pages/cipp/scheduler/task.js index 8f52ae443ece..dd9fdfbcee9e 100644 --- a/src/pages/cipp/scheduler/task.js +++ b/src/pages/cipp/scheduler/task.js @@ -1,5 +1,5 @@ import { useRouter } from "next/router"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import ScheduledTaskDetails from "../../../components/CippComponents/ScheduledTaskDetails"; import CippPageCard from "../../../components/CippCards/CippPageCard"; import { CardContent } from "@mui/material"; diff --git a/src/pages/cipp/settings/backend.js b/src/pages/cipp/settings/backend.js index 6cb35a490f1a..a988cb463a1c 100644 --- a/src/pages/cipp/settings/backend.js +++ b/src/pages/cipp/settings/backend.js @@ -1,11 +1,11 @@ import { Container } from "@mui/material"; import { Grid } from "@mui/system"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import tabOptions from "./tabOptions"; -import { ApiGetCall } from "/src/api/ApiCall.jsx"; -import { CippBackendCard } from "/src/components/CippSettings/CippBackendCard"; -import { CippCodeBlock } from "/src/components/CippComponents/CippCodeBlock"; +import { ApiGetCall } from "../../../api/ApiCall.jsx"; +import { CippBackendCard } from "../../../components/CippSettings/CippBackendCard"; +import { CippCodeBlock } from "../../../components/CippComponents/CippCodeBlock"; import { CommandLineIcon } from "@heroicons/react/24/outline"; const Page = () => { diff --git a/src/pages/cipp/settings/backup.js b/src/pages/cipp/settings/backup.js index 4c5249cf51ec..6de4144029b6 100644 --- a/src/pages/cipp/settings/backup.js +++ b/src/pages/cipp/settings/backup.js @@ -11,7 +11,7 @@ import { FormControl, FormLabel, } from "@mui/material"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import CippPageCard from "../../../components/CippCards/CippPageCard"; import { ApiGetCall, ApiPostCall } from "../../../api/ApiCall"; import { CippInfoBar } from "../../../components/CippCards/CippInfoBar"; diff --git a/src/pages/cipp/settings/index.js b/src/pages/cipp/settings/index.js index e6e5447ba12f..07f6cd63a0fe 100644 --- a/src/pages/cipp/settings/index.js +++ b/src/pages/cipp/settings/index.js @@ -1,16 +1,17 @@ import { Container } from "@mui/material"; import { Grid } from "@mui/system"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import tabOptions from "./tabOptions"; -import CippVersionProperties from "/src/components/CippSettings/CippVersionProperties"; -import CippPasswordSettings from "/src/components/CippSettings/CippPasswordSettings"; -import CippDnsSettings from "/src/components/CippSettings/CippDnsSettings"; -import CippCacheSettings from "/src/components/CippSettings/CippCacheSettings"; -import CippBackupSettings from "/src/components/CippSettings/CippBackupSettings"; -import CippBrandingSettings from "/src/components/CippSettings/CippBrandingSettings"; -import CippBackupRetentionSettings from "/src/components/CippSettings/CippBackupRetentionSettings"; +import CippVersionProperties from "../../../components/CippSettings/CippVersionProperties"; +import CippPasswordSettings from "../../../components/CippSettings/CippPasswordSettings"; +import CippDnsSettings from "../../../components/CippSettings/CippDnsSettings"; +import CippCacheSettings from "../../../components/CippSettings/CippCacheSettings"; +import CippBackupSettings from "../../../components/CippSettings/CippBackupSettings"; +import CippBrandingSettings from "../../../components/CippSettings/CippBrandingSettings"; +import CippBackupRetentionSettings from "../../../components/CippSettings/CippBackupRetentionSettings"; +import CippJitAdminSettings from "../../../components/CippSettings/CippJitAdminSettings"; const Page = () => { return ( @@ -36,6 +37,9 @@ const Page = () => { + + + ); diff --git a/src/pages/cipp/settings/licenses.js b/src/pages/cipp/settings/licenses.js index f59032fbcba5..b5816cadde66 100644 --- a/src/pages/cipp/settings/licenses.js +++ b/src/pages/cipp/settings/licenses.js @@ -1,26 +1,26 @@ import tabOptions from "./tabOptions"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { Button, SvgIcon } from "@mui/material"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import { CippTablePage } from "../../../components/CippComponents/CippTablePage.jsx"; +import { Button, SvgIcon, Stack } from "@mui/material"; import { TrashIcon } from "@heroicons/react/24/outline"; -import { Add } from "@mui/icons-material"; +import { Add, RestartAlt } from "@mui/icons-material"; import { CippApiDialog } from "../../../components/CippComponents/CippApiDialog"; import { useDialog } from "../../../hooks/use-dialog"; const Page = () => { const pageTitle = "Excluded Licenses"; - const apiUrl = "/api/ExecExcludeLicenses"; - const apiData = { List: true }; + const apiUrl = "/api/ListExcludedLicenses"; const createDialog = useDialog(); + const resetDialog = useDialog(); const simpleColumns = ["Product_Display_Name", "GUID"]; const actions = [ { label: "Delete Exclusion", type: "POST", - url: "/api/ExecExcludeLicenses?RemoveExclusion=true", - data: { GUID: "GUID" }, + url: "/api/ExecExcludeLicenses", + data: { Action: "!RemoveExclusion", GUID: "GUID" }, confirmText: "Do you want to delete this exclusion?", color: "error", icon: ( @@ -31,21 +31,32 @@ const Page = () => { }, ]; - const AddExcludedLicense = () => { + const CardButtons = () => { return ( - + + + + ); }; @@ -60,9 +71,9 @@ const Page = () => { title={pageTitle} queryKey="ExcludedLicenses" apiUrl={apiUrl} - cardButton={} - apiData={apiData} + cardButton={} actions={actions} + apiDataKey="Results" offCanvas={offCanvas} simpleColumns={simpleColumns} tenantInTitle={false} @@ -85,15 +96,34 @@ const Page = () => { }, ]} api={{ - url: "/api/ExecExcludeLicenses?AddExclusion=true", + url: "/api/ExecExcludeLicenses", confirmText: "Add a license to the exclusion table, make sure to enter the correct GUID and SKU Name", type: "POST", - data: {}, + data: { Action: "!AddExclusion" }, replacementBehaviour: "removeNulls", relatedQueryKeys: ["ExcludedLicenses"], }} /> + ); }; diff --git a/src/pages/cipp/settings/notifications.js b/src/pages/cipp/settings/notifications.js index 295012322952..99f2d9441bff 100644 --- a/src/pages/cipp/settings/notifications.js +++ b/src/pages/cipp/settings/notifications.js @@ -1,7 +1,7 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import tabOptions from "./tabOptions"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; +import CippFormPage from "../../../components/CippFormPages/CippFormPage"; import { useForm } from "react-hook-form"; import { useDialog } from "../../../hooks/use-dialog"; import { CippNotificationForm } from "../../../components/CippComponents/CippNotificationForm"; diff --git a/src/pages/cipp/settings/partner-webhooks.js b/src/pages/cipp/settings/partner-webhooks.js index 6bd90229f237..f6771b76c49a 100644 --- a/src/pages/cipp/settings/partner-webhooks.js +++ b/src/pages/cipp/settings/partner-webhooks.js @@ -1,7 +1,7 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import tabOptions from "./tabOptions"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; +import CippFormPage from "../../../components/CippFormPages/CippFormPage"; import { useForm } from "react-hook-form"; import { Box, @@ -17,7 +17,7 @@ import { SvgIcon, } from "@mui/material"; import { Grid } from "@mui/system"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "../../../components/CippComponents/CippFormComponent"; import { ApiGetCall, ApiPostCall } from "../../../api/ApiCall"; import { useEffect } from "react"; import { CippPropertyList } from "../../../components/CippComponents/CippPropertyList"; diff --git a/src/pages/cipp/settings/permissions.js b/src/pages/cipp/settings/permissions.js index 44ac6ea5b644..29f7ceb452e1 100644 --- a/src/pages/cipp/settings/permissions.js +++ b/src/pages/cipp/settings/permissions.js @@ -1,9 +1,9 @@ import { Container } from "@mui/material"; import { Grid } from "@mui/system"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import tabOptions from "./tabOptions"; -import CippPermissionCheck from "/src/components/CippSettings/CippPermissionCheck"; +import CippPermissionCheck from "../../../components/CippSettings/CippPermissionCheck"; import { CippPermissionReport } from "../../../components/CippSettings/CippPermissionReport"; import { useState } from "react"; diff --git a/src/pages/cipp/settings/tenants.js b/src/pages/cipp/settings/tenants.js index a5495b4b77e8..3ce10da19c2d 100644 --- a/src/pages/cipp/settings/tenants.js +++ b/src/pages/cipp/settings/tenants.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; import tabOptions from "./tabOptions"; import { CippTenantTable } from "../../../components/CippWizard/CippTenantTable"; diff --git a/src/pages/cipp/statistics/index.js b/src/pages/cipp/statistics/index.js index 0fb2f0f9fae9..445f0ee704e2 100644 --- a/src/pages/cipp/statistics/index.js +++ b/src/pages/cipp/statistics/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; const Page = () => { const pageTitle = "Statistics"; diff --git a/src/pages/cipp/super-admin/cipp-roles/add.js b/src/pages/cipp/super-admin/cipp-roles/add.js index 1734bd047245..4f140837d2cd 100644 --- a/src/pages/cipp/super-admin/cipp-roles/add.js +++ b/src/pages/cipp/super-admin/cipp-roles/add.js @@ -1,6 +1,6 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippPageCard from "/src/components/CippCards/CippPageCard"; -import { CippRoleAddEdit } from "/src/components/CippSettings/CippRoleAddEdit"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippPageCard from "../../../../components/CippCards/CippPageCard"; +import { CippRoleAddEdit } from "../../../../components/CippSettings/CippRoleAddEdit"; import { CardContent, Stack, Alert } from "@mui/material"; const AddRolePage = () => { diff --git a/src/pages/cipp/super-admin/cipp-roles/edit.js b/src/pages/cipp/super-admin/cipp-roles/edit.js index 85a4b2e0c431..cfb95f8be947 100644 --- a/src/pages/cipp/super-admin/cipp-roles/edit.js +++ b/src/pages/cipp/super-admin/cipp-roles/edit.js @@ -1,7 +1,7 @@ import { useRouter } from "next/router"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippPageCard from "/src/components/CippCards/CippPageCard"; -import { CippRoleAddEdit } from "/src/components/CippSettings/CippRoleAddEdit"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippPageCard from "../../../../components/CippCards/CippPageCard"; +import { CippRoleAddEdit } from "../../../../components/CippSettings/CippRoleAddEdit"; import { CardContent, Stack, Alert } from "@mui/material"; const EditRolePage = () => { diff --git a/src/pages/cipp/super-admin/cipp-roles/index.js b/src/pages/cipp/super-admin/cipp-roles/index.js index 9af891be7049..594dc13be696 100644 --- a/src/pages/cipp/super-admin/cipp-roles/index.js +++ b/src/pages/cipp/super-admin/cipp-roles/index.js @@ -1,8 +1,8 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import tabOptions from "../tabOptions"; -import CippPageCard from "/src/components/CippCards/CippPageCard"; -import CippRoles from "/src/components/CippSettings/CippRoles"; +import CippPageCard from "../../../../components/CippCards/CippPageCard"; +import CippRoles from "../../../../components/CippSettings/CippRoles"; import { CardContent, Stack, Alert } from "@mui/material"; const Page = () => { diff --git a/src/pages/cipp/super-admin/function-offloading.js b/src/pages/cipp/super-admin/function-offloading.js index 8ac30a79831e..ee4ed7fa6d05 100644 --- a/src/pages/cipp/super-admin/function-offloading.js +++ b/src/pages/cipp/super-admin/function-offloading.js @@ -1,15 +1,15 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import tabOptions from "./tabOptions"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; +import CippFormPage from "../../../components/CippFormPages/CippFormPage"; import { useForm } from "react-hook-form"; import { Alert, Link } from "@mui/material"; import { Grid } from "@mui/system"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "../../../components/CippComponents/CippFormComponent"; import { ApiGetCall, ApiPostCall } from "../../../api/ApiCall"; import { useEffect } from "react"; import NextLink from "next/link"; -import { CippDataTable } from "/src/components/CippTable/CippDataTable"; +import { CippDataTable } from "../../../components/CippTable/CippDataTable"; import { TrashIcon } from "@heroicons/react/24/outline"; const Page = () => { diff --git a/src/pages/cipp/super-admin/jit-admin-settings.js b/src/pages/cipp/super-admin/jit-admin-settings.js new file mode 100644 index 000000000000..c2f7766e670e --- /dev/null +++ b/src/pages/cipp/super-admin/jit-admin-settings.js @@ -0,0 +1,146 @@ +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import tabOptions from "./tabOptions"; +import CippFormPage from "../../../components/CippFormPages/CippFormPage"; +import { useForm } from "react-hook-form"; +import { Typography, Alert } from "@mui/material"; +import { Grid } from "@mui/system"; +import CippFormComponent from "../../../components/CippComponents/CippFormComponent"; +import { ApiGetCall } from "../../../api/ApiCall"; +import { useEffect } from "react"; + +const Page = () => { + const pageTitle = "JIT Admin Settings"; + + const formControl = useForm({ + mode: "onChange", + defaultValues: { + MaxDuration: "", + }, + }); + + const jitSettings = ApiGetCall({ + url: "/api/ExecJITAdminSettings?Action=Get", + queryKey: "jitAdminSettings", + }); + + useEffect(() => { + if (jitSettings.isSuccess && jitSettings.data) { + formControl.reset({ + MaxDuration: jitSettings.data?.MaxDuration || [], + }); + } + }, [jitSettings.isSuccess, jitSettings.data]); + + return ( + ({ + Action: "Set", + MaxDuration: values.MaxDuration || null, + })} + > + + + + Configure maximum allowed duration for Just-In-Time (JIT) admin accounts. This setting + helps enforce security policies by preventing technicians from creating JIT admin + accounts with excessively long lifespans. Validation is performed on the backend when + creating JIT admin accounts. + + + + + + Set the maximum duration in ISO 8601 format, or leave empty for no limit (default). The + backend will validate that the difference between the start and end dates of any JIT + admin account does not exceed the configured maximum duration. + + + + + { + // Allow empty value (no limit) + if (typeof value !== "string" || value.trim() === "") { + return true; + } + const iso8601Regex = /^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/; + if (!iso8601Regex.test(value)) { + return "Invalid format. Use PT1H, P1D, P7D, P28D, etc."; + } + return true; + }, + }, + }} + formControl={formControl} + helperText="ISO 8601 format: PT1H (1 hour), P1D (1 day), P7D (1 week), P28D (4 weeks). Leave empty for no limit." + /> + + + + + + Important Notes: + + +
      +
    • Leave empty for no limit on JIT admin account duration (default behavior)
    • +
    • + The duration is calculated from the start date to the expiration date of the JIT + admin +
    • +
    • + If a technician attempts to exceed this limit, the backend will reject the + request with an error message +
    • +
    • This setting applies globally to all tenants and all JIT admin creations
    • +
    +
    +
    +
    + + + + Example: If maximum duration is set to P28D (4 weeks), and a + technician tries to create a JIT admin account lasting 1.5 months, the backend will + reject the request with an error: "Requested JIT Admin duration (56 days) exceeds the + maximum allowed duration of P28D (28 days)". + + +
    +
    + ); +}; + +Page.getLayout = (page) => ( + + {page} + +); + +export default Page; diff --git a/src/pages/cipp/super-admin/sam-app-permissions.js b/src/pages/cipp/super-admin/sam-app-permissions.js index f52b0a0711d5..90bb414701df 100644 --- a/src/pages/cipp/super-admin/sam-app-permissions.js +++ b/src/pages/cipp/super-admin/sam-app-permissions.js @@ -1,10 +1,10 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import tabOptions from "./tabOptions"; import { useForm } from "react-hook-form"; import { ApiGetCall, ApiPostCall } from "../../../api/ApiCall"; -import CippAppPermissionBuilder from "/src/components/CippComponents/CippAppPermissionBuilder"; -import CippPageCard from "/src/components/CippCards/CippPageCard"; +import CippAppPermissionBuilder from "../../../components/CippComponents/CippAppPermissionBuilder"; +import CippPageCard from "../../../components/CippCards/CippPageCard"; import { Alert, CardContent, Skeleton, Stack, Typography } from "@mui/material"; import { WarningAmberOutlined } from "@mui/icons-material"; diff --git a/src/pages/cipp/super-admin/sam-app-roles.js b/src/pages/cipp/super-admin/sam-app-roles.js index a5c0632206eb..b48efc3dcf9a 100644 --- a/src/pages/cipp/super-admin/sam-app-roles.js +++ b/src/pages/cipp/super-admin/sam-app-roles.js @@ -1,14 +1,14 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import tabOptions from "./tabOptions"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; +import CippFormPage from "../../../components/CippFormPages/CippFormPage"; import { Alert, CardContent, Stack, Typography } from "@mui/material"; import { WarningAmberOutlined } from "@mui/icons-material"; import { useForm } from "react-hook-form"; import { ApiGetCall, ApiGetCallWithPagination } from "../../../api/ApiCall"; import { useEffect } from "react"; import CippFormComponent from "../../../components/CippComponents/CippFormComponent"; -import GDAPRoles from "/src/data/GDAPRoles"; +import GDAPRoles from "../../../data/GDAPRoles"; import { CippFormTenantSelector } from "../../../components/CippComponents/CippFormTenantSelector"; const Page = () => { diff --git a/src/pages/cipp/super-admin/tenant-mode.js b/src/pages/cipp/super-admin/tenant-mode.js index 185ae5896ad3..ea4468ce1ff5 100644 --- a/src/pages/cipp/super-admin/tenant-mode.js +++ b/src/pages/cipp/super-admin/tenant-mode.js @@ -1,11 +1,11 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import tabOptions from "./tabOptions"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; +import CippFormPage from "../../../components/CippFormPages/CippFormPage"; import { useForm } from "react-hook-form"; import { Typography } from "@mui/material"; import { Grid } from "@mui/system"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "../../../components/CippComponents/CippFormComponent"; import { ApiGetCall } from "../../../api/ApiCall"; import { useEffect } from "react"; import Link from "next/link"; diff --git a/src/pages/cipp/super-admin/time-settings.js b/src/pages/cipp/super-admin/time-settings.js index d1cde1737f66..10245f89f3e2 100644 --- a/src/pages/cipp/super-admin/time-settings.js +++ b/src/pages/cipp/super-admin/time-settings.js @@ -1,13 +1,13 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import { useForm } from "react-hook-form"; import { Alert, Typography } from "@mui/material"; import { Grid } from "@mui/system"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "../../../components/CippComponents/CippFormComponent"; import { ApiGetCall } from "../../../api/ApiCall"; import { useEffect, useMemo } from "react"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import { useTimezones } from "/src/hooks/use-timezones"; +import CippFormPage from "../../../components/CippFormPages/CippFormPage"; +import { useTimezones } from "../../../hooks/use-timezones"; import tabOptions from "./tabOptions"; const Page = () => { diff --git a/src/pages/dashboardv1.js b/src/pages/dashboardv1.js new file mode 100644 index 000000000000..a2054d962e5e --- /dev/null +++ b/src/pages/dashboardv1.js @@ -0,0 +1,457 @@ +import Head from "next/head"; +import { useEffect, useState } from "react"; +import { Box, Container, Button, Card, CardContent } from "@mui/material"; +import { Grid } from "@mui/system"; +import { CippInfoBar } from "../components/CippCards/CippInfoBar"; +import { CippChartCard } from "../components/CippCards/CippChartCard"; +import { CippPropertyListCard } from "../components/CippCards/CippPropertyListCard"; +import { Layout as DashboardLayout } from "../layouts/index.js"; +import { useSettings } from "../hooks/use-settings"; +import { getCippFormatting } from "../utils/get-cipp-formatting.js"; +import Portals from "../data/portals"; +import { BulkActionsMenu } from "../components/bulk-actions-menu.js"; +import { CippUniversalSearch } from "../components/CippCards/CippUniversalSearch.jsx"; +import { ApiGetCall } from "../api/ApiCall.jsx"; +import { CippCopyToClipBoard } from "../components/CippComponents/CippCopyToClipboard.jsx"; +import { ExecutiveReportButton } from "../components/ExecutiveReportButton.js"; + +const Page = () => { + const settings = useSettings(); + const { currentTenant } = settings; + const [domainVisible, setDomainVisible] = useState(false); + + const organization = ApiGetCall({ + url: "/api/ListOrg", + queryKey: `${currentTenant}-ListOrg`, + data: { tenantFilter: currentTenant }, + }); + + const dashboard = ApiGetCall({ + url: "/api/ListuserCounts", + data: { tenantFilter: currentTenant }, + queryKey: `${currentTenant}-ListuserCounts`, + }); + + const sharepoint = ApiGetCall({ + url: "/api/ListSharepointQuota", + queryKey: `${currentTenant}-ListSharepointQuota`, + data: { tenantFilter: currentTenant }, + }); + + const standards = ApiGetCall({ + url: "/api/ListStandardTemplates", + queryKey: `${currentTenant}-ListStandardTemplates`, + }); + + const driftApi = ApiGetCall({ + url: "/api/listTenantDrift", + data: { + TenantFilter: currentTenant, + }, + queryKey: `TenantDrift-${currentTenant}`, + }); + + const partners = ApiGetCall({ + url: "/api/ListGraphRequest", + queryKey: `${currentTenant}-ListPartners`, + data: { + Endpoint: "policies/crossTenantAccessPolicy/partners", + tenantFilter: currentTenant, + ReverseTenantLookup: true, + }, + }); + + const currentTenantInfo = ApiGetCall({ + url: "/api/ListTenants", + queryKey: `ListTenants`, + }); + + // Top bar data + const tenantInfo = [ + { name: "Tenant Name", data: organization.data?.displayName }, + { + name: "Tenant ID", + data: ( + <> + + + ), + }, + { + name: "Default Domain", + data: ( + <> + domain.isDefault === true)?.name + } + type="chip" + /> + + ), + }, + { + name: "AD Sync Enabled", + data: getCippFormatting(organization.data?.onPremisesSyncEnabled, "dirsync"), + }, + ]; + + // Process drift data for chart - filter by current tenant and aggregate + const processDriftDataForTenant = (driftData, currentTenant) => { + if (!driftData) { + return { + alignedCount: 0, + acceptedDeviationsCount: 0, + currentDeviationsCount: 0, + customerSpecificDeviations: 0, + hasData: false, + }; + } + + const rawDriftData = driftData || []; + const tenantDriftData = Array.isArray(rawDriftData) + ? rawDriftData.filter((item) => item.tenantFilter === currentTenant) + : []; + + const hasData = tenantDriftData.length > 0; + + // Aggregate data across all standards for this tenant + const aggregatedData = tenantDriftData.reduce( + (acc, item) => { + acc.acceptedDeviationsCount += item.acceptedDeviationsCount || 0; + acc.currentDeviationsCount += item.currentDeviationsCount || 0; + acc.alignedCount += item.alignedCount || 0; + acc.customerSpecificDeviations += item.customerSpecificDeviationsCount || 0; + return acc; + }, + { + acceptedDeviationsCount: 0, + currentDeviationsCount: 0, + alignedCount: 0, + customerSpecificDeviations: 0, + } + ); + + return { ...aggregatedData, hasData }; + }; + + function getActionCountsForTenant(standardsData, currentTenant) { + if (!standardsData) { + return { + remediateCount: 0, + alertCount: 0, + reportCount: 0, + total: 0, + }; + } + + const applicableTemplates = standardsData.filter((template) => { + const tenantFilterArr = Array.isArray(template?.tenantFilter) ? template.tenantFilter : []; + const excludedTenantsArr = Array.isArray(template?.excludedTenants) + ? template.excludedTenants + : []; + + const tenantInFilter = + tenantFilterArr.length > 0 && tenantFilterArr.some((tf) => tf.value === currentTenant); + + const allTenantsTemplate = + tenantFilterArr.some((tf) => tf.value === "AllTenants") && + (excludedTenantsArr.length === 0 || + !excludedTenantsArr.some((et) => et.value === currentTenant)); + + return tenantInFilter || allTenantsTemplate; + }); + + // Combine standards from all applicable templates: + let combinedStandards = {}; + for (const template of applicableTemplates) { + for (const [standardKey, standardValue] of Object.entries(template.standards)) { + combinedStandards[standardKey] = standardValue; + } + } + + // Count each action type: + let remediateCount = 0; + let alertCount = 0; + let reportCount = 0; + + for (const [, standard] of Object.entries(combinedStandards)) { + let actions = standard.action || []; + if (!Array.isArray(actions)) { + actions = [actions]; + } + actions.forEach((actionObj) => { + if (actionObj?.value === "Remediate") { + remediateCount++; + } else if (actionObj?.value === "Alert") { + alertCount++; + } else if (actionObj?.value === "Report") { + reportCount++; + } + }); + } + + const total = Object.keys(combinedStandards).length; + + return { remediateCount, alertCount, reportCount, total }; + } + + const driftData = processDriftDataForTenant(driftApi.data, currentTenant); + const { remediateCount, alertCount, reportCount, total } = getActionCountsForTenant( + standards.data, + currentTenant + ); + + const [PortalMenuItems, setPortalMenuItems] = useState([]); + const [partnersVisible, setPartnersVisible] = useState(false); + + const formatStorageSize = (sizeInMB) => { + if (sizeInMB >= 1024) { + return `${(sizeInMB / 1024).toFixed(2)}GB`; + } + return `${sizeInMB}MB`; + }; + + // Function to filter portals based on user preferences + const getFilteredPortals = () => { + const defaultLinks = { + M365_Portal: true, + Exchange_Portal: true, + Entra_Portal: true, + Teams_Portal: true, + Azure_Portal: true, + Intune_Portal: true, + SharePoint_Admin: true, + Security_Portal: true, + Compliance_Portal: true, + Power_Platform_Portal: true, + Power_BI_Portal: true, + }; + + let portalLinks; + if (settings.UserSpecificSettings?.portalLinks) { + portalLinks = { ...defaultLinks, ...settings.UserSpecificSettings.portalLinks }; + } else if (settings.portalLinks) { + portalLinks = { ...defaultLinks, ...settings.portalLinks }; + } else { + portalLinks = defaultLinks; + } + + // Filter the portals based on user settings + return Portals.filter((portal) => { + const settingKey = portal.name; + return settingKey ? portalLinks[settingKey] === true : true; + }); + }; + + useEffect(() => { + if (currentTenantInfo.isSuccess) { + const tenantLookup = currentTenantInfo.data?.find( + (tenant) => tenant.defaultDomainName === currentTenant + ); + + // Get filtered portals based on user preferences + const filteredPortals = getFilteredPortals(); + + const menuItems = filteredPortals.map((portal) => ({ + label: portal.label, + target: "_blank", + link: portal.url.replace(portal.variable, tenantLookup?.[portal.variable]), + icon: portal.icon, + })); + setPortalMenuItems(menuItems); + } + }, [ + currentTenantInfo.isSuccess, + currentTenant, + settings.portalLinks, + settings.UserSpecificSettings, + ]); + + return ( + <> + + Dashboard + + + + + + + + + + + {/* TODO: Remove Card from inside CippUniversalSearch to avoid double border */} + + + + + + + + + + + + + + + + + + + + + {/* Converted Domain Names to Property List */} + + ({ + label: "", + value: domain.name, + }))} + actionButton={ + organization.data?.verifiedDomains?.length > 3 && ( + + ) + } + /> + + + + ({ + label: partner.TenantInfo?.displayName, + value: partner.TenantInfo?.defaultDomainName, + }))} + actionButton={ + partners.data?.Results?.length > 3 && ( + + ) + } + /> + + + + + plan.capabilityStatus === "Enabled" && + ["exchange", "AADPremiumService", "WindowsDefenderATP"].includes( + plan.service + ) + ) + .reduce((uniqueServices, curr) => { + const serviceLabel = + curr.service === "exchange" + ? "Exchange" + : curr.service === "AADPremiumService" + ? "AAD Premium" + : curr.service === "Windows Defender" + ? "Windows Defender" + : curr.service; + + if (!uniqueServices.includes(serviceLabel)) { + uniqueServices.push(serviceLabel); + } + return uniqueServices; + }, []) + .join(", "), + }, + ]} + /> + + + + + + ); +}; + +Page.getLayout = (page) => {page}; + +export default Page; diff --git a/src/pages/dashboardv2/devices/index.js b/src/pages/dashboardv2/devices/index.js index a4c96d300f0d..b3766dcf6c4c 100644 --- a/src/pages/dashboardv2/devices/index.js +++ b/src/pages/dashboardv2/devices/index.js @@ -1,28 +1,137 @@ -import { Container, Typography, Card, CardContent, CardHeader, Box } from "@mui/material"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import React from "react"; +import { + Container, + Typography, + Card, + CardContent, + CardHeader, + Box, + Stack, + Chip, +} from "@mui/material"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import tabOptions from "../tabOptions"; +import { useSettings } from "../../../hooks/use-settings"; +import { ApiGetCall } from "../../../api/ApiCall.jsx"; +import { CippDataTable } from "../../../components/CippTable/CippDataTable"; +import { CippTestDetailOffCanvas } from "../../../components/CippTestDetail/CippTestDetailOffCanvas"; +import { useRouter } from "next/router"; const Page = () => { + const settings = useSettings(); + const { currentTenant } = settings; + const router = useRouter(); + // Only use default if router is ready and reportId is still not present + const selectedReport = + router.isReady && !router.query.reportId ? "ztna" : router.query.reportId || "ztna"; + + const testsApi = ApiGetCall({ + url: "/api/ListTests", + data: { tenantFilter: currentTenant, reportId: selectedReport }, + queryKey: `${currentTenant}-ListTests-${selectedReport}`, + waiting: !!currentTenant && !!selectedReport, + }); + + const DevicesTests = + testsApi.data?.TestResults?.filter((test) => test.TestType === "Devices") || []; + + const getStatusColor = (status) => { + switch (status?.toLowerCase()) { + case "passed": + return "success"; + case "failed": + return "error"; + case "investigate": + return "warning"; + case "skipped": + return "default"; + default: + return "default"; + } + }; + + const getRiskColor = (risk) => { + switch (risk?.toLowerCase()) { + case "high": + return "error"; + case "medium": + return "warning"; + case "low": + return "info"; + default: + return "default"; + } + }; + + const getImpactColor = (impact) => { + switch (impact?.toLowerCase()) { + case "high": + return "error"; + case "medium": + return "warning"; + case "low": + return "info"; + default: + return "default"; + } + }; + + const offCanvas = { + size: "lg", + children: (row) => , + }; + + const filters = [ + { + filterName: "Passed", + value: [{ id: "Status", value: "Passed" }], + type: "column", + }, + { + filterName: "Failed", + value: [{ id: "Status", value: "Failed" }], + type: "column", + }, + { + filterName: "Investigate", + value: [{ id: "Status", value: "Investigate" }], + type: "column", + }, + { + filterName: "Skipped", + value: [{ id: "Status", value: "Skipped" }], + type: "column", + }, + { + filterName: "High Risk", + value: [{ id: "Risk", value: "High" }], + type: "column", + }, + { + filterName: "Medium Risk", + value: [{ id: "Risk", value: "Medium" }], + type: "column", + }, + { + filterName: "Low Risk", + value: [{ id: "Risk", value: "Low" }], + type: "column", + }, + ]; + return ( - - - - - - Device Test Results - - - This tab will display detailed device test results and recommendations. - - - Review device compliance policies, enrollment restrictions, and management - configurations to enhance your device security posture. - - - - + ); }; diff --git a/src/pages/dashboardv2/identity/index.js b/src/pages/dashboardv2/identity/index.js index 78b76b13e131..bdc4f0e8ddb4 100644 --- a/src/pages/dashboardv2/identity/index.js +++ b/src/pages/dashboardv2/identity/index.js @@ -1,28 +1,86 @@ -import { Container, Typography, Card, CardContent, CardHeader, Box } from "@mui/material"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Container } from "@mui/material"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import tabOptions from "../tabOptions"; +import { useSettings } from "../../../hooks/use-settings"; +import { ApiGetCall } from "../../../api/ApiCall.jsx"; +import { CippDataTable } from "../../../components/CippTable/CippDataTable"; +import { CippTestDetailOffCanvas } from "../../../components/CippTestDetail/CippTestDetailOffCanvas"; +import { useRouter } from "next/router"; const Page = () => { + const settings = useSettings(); + const { currentTenant } = settings; + const router = useRouter(); + // Only use default if router is ready and reportId is still not present + const selectedReport = + router.isReady && !router.query.reportId ? "ztna" : router.query.reportId || "ztna"; + + const testsApi = ApiGetCall({ + url: "/api/ListTests", + data: { tenantFilter: currentTenant, reportId: selectedReport }, + queryKey: `${currentTenant}-ListTests-${selectedReport}`, + waiting: !!currentTenant && !!selectedReport, + }); + + const identityTests = + testsApi.data?.TestResults?.filter((test) => test.TestType === "Identity") || []; + + const offCanvas = { + size: "lg", + children: (row) => , + }; + + const filters = [ + { + filterName: "Passed", + value: [{ id: "Status", value: "Passed" }], + type: "column", + }, + { + filterName: "Failed", + value: [{ id: "Status", value: "Failed" }], + type: "column", + }, + { + filterName: "Investigate", + value: [{ id: "Status", value: "Investigate" }], + type: "column", + }, + { + filterName: "Skipped", + value: [{ id: "Status", value: "Skipped" }], + type: "column", + }, + { + filterName: "High Risk", + value: [{ id: "Risk", value: "High" }], + type: "column", + }, + { + filterName: "Medium Risk", + value: [{ id: "Risk", value: "Medium" }], + type: "column", + }, + { + filterName: "Low Risk", + value: [{ id: "Risk", value: "Low" }], + type: "column", + }, + ]; + return ( - - - - - - Identity Test Results - - - This tab will display detailed identity test results and recommendations. - - - Configure your identity policies and authentication methods to improve your zero trust - posture. - - - - + ); }; diff --git a/src/pages/dashboardv2/index.js b/src/pages/dashboardv2/index.js index 3b29d86dcaac..1a169586fe21 100644 --- a/src/pages/dashboardv2/index.js +++ b/src/pages/dashboardv2/index.js @@ -1,56 +1,196 @@ -import { - Box, - Card, - CardContent, - CardHeader, - Container, - Typography, - Avatar, - Divider, - Tooltip, -} from "@mui/material"; +import { Box, Card, CardContent, Container, Button, Tooltip } from "@mui/material"; +import { useState, useEffect } from "react"; +import { useRouter } from "next/router"; +import { useForm, useWatch } from "react-hook-form"; import { Grid } from "@mui/system"; -import { - BarChart, - Bar, - PieChart, - Pie, - Cell, - RadialBarChart, - RadialBar, - PolarAngleAxis, - XAxis, - YAxis, - ResponsiveContainer, - Tooltip as RechartsTooltip, - LabelList, -} from "recharts"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { useSettings } from "../../hooks/use-settings"; +import { ApiGetCall } from "../../api/ApiCall.jsx"; +import Portals from "../../data/portals"; +import { BulkActionsMenu } from "../../components/bulk-actions-menu.js"; +import { ExecutiveReportButton } from "../../components/ExecutiveReportButton.js"; +import { TabbedLayout } from "../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../layouts/index.js"; import tabOptions from "./tabOptions"; -import { dashboardDemoData } from "/src/data/dashboardv2-demo-data"; -import { CaSankey } from "/src/components/CippComponents/CaSankey"; -import { CaDeviceSankey } from "/src/components/CippComponents/CaDeviceSankey"; -import { AuthMethodSankey } from "/src/components/CippComponents/AuthMethodSankey"; -import { DesktopDevicesSankey } from "/src/components/CippComponents/DesktopDevicesSankey"; -import { MobileSankey } from "/src/components/CippComponents/MobileSankey"; +import { dashboardDemoData } from "../../data/dashboardv2-demo-data"; +import { SecureScoreCard } from "../../components/CippComponents/SecureScoreCard"; +import { MFACard } from "../../components/CippComponents/MFACard"; +import { AuthMethodCard } from "../../components/CippComponents/AuthMethodCard"; +import { LicenseCard } from "../../components/CippComponents/LicenseCard"; +import { TenantInfoCard } from "../../components/CippComponents/TenantInfoCard"; +import { TenantMetricsGrid } from "../../components/CippComponents/TenantMetricsGrid"; +import { AssessmentCard } from "../../components/CippComponents/AssessmentCard"; +import { CippApiDialog } from "../../components/CippComponents/CippApiDialog"; +import { CippAddTestReportDrawer } from "../../components/CippComponents/CippAddTestReportDrawer"; +import CippFormComponent from "../../components/CippComponents/CippFormComponent"; import { - People as UsersIcon, - Person as UserIcon, - PersonOutline as GuestIcon, - Group as GroupIcon, - Apps as AppsIcon, Devices as DevicesIcon, - PhoneAndroid as ManagedIcon, - Security as SecurityIcon, - Business as BuildingIcon, CheckCircle as CheckCircleIcon, - Laptop as MonitorIcon, Work as BriefcaseIcon, + Assessment as AssessmentIcon, + Refresh as RefreshIcon, } from "@mui/icons-material"; const Page = () => { - const reportData = dashboardDemoData; + const settings = useSettings(); + const router = useRouter(); + const { currentTenant } = settings; + const [portalMenuItems, setPortalMenuItems] = useState([]); + const [deleteDialog, setDeleteDialog] = useState({ open: false }); + const [refreshDialog, setRefreshDialog] = useState({ open: false }); + + // Get reportId from query params or default to "ztna" + // Only use default if router is ready and reportId is still not present + const selectedReport = + router.isReady && !router.query.reportId ? "ztna" : router.query.reportId || "ztna"; + + const formControl = useForm({ + mode: "onChange", + }); + + const reportIdValue = useWatch({ control: formControl.control }); + + // Fetch available reports + const reportsApi = ApiGetCall({ + url: "/api/ListTestReports", + queryKey: "ListTestReports", + }); + + const reports = reportsApi.data || []; + + // Update form when selectedReport changes (from URL) + useEffect(() => { + if (selectedReport && router.isReady && reports.length > 0) { + const matchingReport = reports.find((r) => r.id === selectedReport); + if (matchingReport) { + formControl.setValue("reportId", { + value: matchingReport.id, + label: matchingReport.name, + }); + } + } + }, [selectedReport, router.isReady, reports]); + + // Update URL when form value changes (e.g., user selects different report from dropdown) + useEffect(() => { + console.log("reportIdValue changed:", reportIdValue); + if (reportIdValue?.reportId?.value && reportIdValue.reportId.value !== selectedReport) { + router.push( + { + pathname: router.pathname, + query: { ...router.query, reportId: reportIdValue.reportId.value }, + }, + undefined, + { shallow: true }, + ); + } + }, [reportIdValue]); + + const organization = ApiGetCall({ + url: "/api/ListOrg", + queryKey: `${currentTenant}-ListOrg`, + data: { tenantFilter: currentTenant }, + }); + + const testsApi = ApiGetCall({ + url: "/api/ListTests", + data: { tenantFilter: currentTenant, reportId: selectedReport }, + queryKey: `${currentTenant}-ListTests-${selectedReport}`, + waiting: !!currentTenant && !!selectedReport, + }); + + const currentTenantInfo = ApiGetCall({ + url: "/api/ListTenants", + queryKey: `ListTenants`, + }); + + const reportData = + testsApi.isSuccess && testsApi.data?.TenantCounts + ? { + ExecutedAt: testsApi.data?.LatestReportTimeStamp || null, + TenantName: organization.data?.displayName || "", + Domain: currentTenant || "", + TestResultSummary: { + IdentityPassed: testsApi.data.TestCounts?.Identity?.Passed || 0, + IdentityTotal: testsApi.data.TestCounts?.Identity?.Total || 0, + DevicesPassed: testsApi.data.TestCounts?.Devices?.Passed || 0, + DevicesTotal: testsApi.data.TestCounts?.Devices?.Total || 0, + DataPassed: 0, + DataTotal: 0, + }, + SecureScore: testsApi.data.SecureScore || [], + TenantInfo: { + TenantOverview: { + UserCount: testsApi.data.TenantCounts.Users || 0, + GuestCount: testsApi.data.TenantCounts.Guests || 0, + GroupCount: testsApi.data.TenantCounts.Groups || 0, + ApplicationCount: testsApi.data.TenantCounts.ServicePrincipals || 0, + DeviceCount: testsApi.data.TenantCounts.Devices || 0, + ManagedDeviceCount: testsApi.data.TenantCounts.ManagedDevices || 0, + }, + MFAState: testsApi.data.MFAState, + OverviewCaDevicesAllUsers: dashboardDemoData.TenantInfo.OverviewCaDevicesAllUsers, + OverviewAuthMethodsPrivilegedUsers: + dashboardDemoData.TenantInfo.OverviewAuthMethodsPrivilegedUsers, + DeviceOverview: dashboardDemoData.TenantInfo.DeviceOverview, + }, + } + : dashboardDemoData; + + // Function to filter portals based on user preferences + const getFilteredPortals = () => { + const defaultLinks = { + M365_Portal: true, + Exchange_Portal: true, + Entra_Portal: true, + Teams_Portal: true, + Azure_Portal: true, + Intune_Portal: true, + SharePoint_Admin: true, + Security_Portal: true, + Compliance_Portal: true, + Power_Platform_Portal: true, + Power_BI_Portal: true, + }; + + let portalLinks; + if (settings.UserSpecificSettings?.portalLinks) { + portalLinks = { ...defaultLinks, ...settings.UserSpecificSettings.portalLinks }; + } else if (settings.portalLinks) { + portalLinks = { ...defaultLinks, ...settings.portalLinks }; + } else { + portalLinks = defaultLinks; + } + + // Filter the portals based on user settings + return Portals.filter((portal) => { + const settingKey = portal.name; + return settingKey ? portalLinks[settingKey] === true : true; + }); + }; + + useEffect(() => { + if (currentTenantInfo.isSuccess) { + const tenantLookup = currentTenantInfo.data?.find( + (tenant) => tenant.defaultDomainName === currentTenant, + ); + + // Get filtered portals based on user preferences + const filteredPortals = getFilteredPortals(); + + const menuItems = filteredPortals.map((portal) => ({ + label: portal.label, + target: "_blank", + link: portal.url.replace(portal.variable, tenantLookup?.[portal.variable]), + icon: portal.icon, + })); + setPortalMenuItems(menuItems); + } + }, [ + currentTenantInfo.isSuccess, + currentTenant, + settings.portalLinks, + settings.UserSpecificSettings, + ]); const formatNumber = (num) => { if (!num && num !== 0) return "0"; @@ -60,313 +200,134 @@ const Page = () => { return num.toLocaleString(); }; - const metricDescriptions = { - users: "Total number of users in your tenant", - guests: "External users with guest access", - groups: "Microsoft 365 and security groups", - apps: "Registered applications", - devices: "All devices accessing tenant resources", - managed: "Devices enrolled in Intune", - }; - return ( - {/* Tenant Overview Section - 3 Column Layout */} - - {/* Column 1: Tenant Information */} - + + + + + + + + + + + + + + + - - - Tenant - - } - sx={{ pb: 1.5 }} - /> - - - - - Name - - - {reportData.TenantName || "Not Available"} - - - - - Tenant ID - - - {reportData.TenantId || "Not Available"} - - - - - Primary Domain - - - {reportData.Domain || "Not Available"} - - + + + ({ + label: r.name, + value: r.id, + description: r.description, + }))} + placeholder="Choose a report" + /> + + + + + + {/* Tenant Overview Section - 3 Column Layout */} + + {/* Column 1: Tenant Information */} + + + {/* Column 2: Tenant Metrics - 2x3 Grid */} - - - - - - - - - - Users - - - {formatNumber(reportData.TenantInfo.TenantOverview.UserCount)} - - - - - - - - - - - - - - Guests - - - {formatNumber(reportData.TenantInfo.TenantOverview.GuestCount)} - - - - - - - - - - - - - - Groups - - - {formatNumber(reportData.TenantInfo.TenantOverview.GroupCount)} - - - - - - - - - - - - - - Apps - - - {formatNumber(reportData.TenantInfo.TenantOverview.ApplicationCount)} - - - - - - - - - - - - - - Devices - - - {formatNumber(reportData.TenantInfo.TenantOverview.DeviceCount)} - - - - - - - - - - - - - - Managed - - - {formatNumber(reportData.TenantInfo.TenantOverview.ManagedDeviceCount)} - - - - - - + {/* Column 3: Assessment Results */} - - - - Assessment - - } - sx={{ pb: 1.5 }} - /> - - - - - - Identity - - - {reportData.TestResultSummary.IdentityPassed}/ - {reportData.TestResultSummary.IdentityTotal} - - tests - - - - - - Devices - - - {reportData.TestResultSummary.DevicesPassed}/ - {reportData.TestResultSummary.DevicesTotal} - - tests - - - - - - - - - - - - - - - + @@ -376,625 +337,82 @@ const Page = () => { {/* Left Column */} - {/* Privileged users auth methods */} - - - - Privileged users auth methods - - } - sx={{ pb: 1 }} + + - - - {reportData.TenantInfo.OverviewAuthMethodsPrivilegedUsers?.nodes && ( - - )} - - - {reportData.TenantInfo.OverviewAuthMethodsPrivilegedUsers?.description || - "No description available"} - - - - - {/* All Users Auth Methods */} - - - - All users auth methods - - } - sx={{ pb: 1 }} + + + - - - {reportData.TenantInfo.OverviewAuthMethodsAllUsers?.nodes && ( - - )} - - - {reportData.TenantInfo.OverviewAuthMethodsAllUsers?.description || - "No description available"} - - - + {/* Right Column */} - {/* User Authentication */} - - - - User authentication - - } - sx={{ pb: 1 }} + + - - - {reportData.TenantInfo.OverviewCaMfaAllUsers?.nodes && ( - - )} - - - {reportData.TenantInfo.OverviewCaMfaAllUsers?.description || - "No description available"} - - - - - {/* Device Sign-ins */} - - - - Device sign-ins - - } - sx={{ pb: 1 }} + + + - - - {reportData.TenantInfo.OverviewCaDevicesAllUsers?.nodes && ( - - )} - - - {reportData.TenantInfo.OverviewCaDevicesAllUsers?.description || - "No description available"} - - - + + - {/* Devices Section */} - - - {/* Device Summary Chart */} - - - - - Device summary - - } - sx={{ pb: 1 }} - /> - - - - - - - - - - - - - - - - - - Desktops - - - {Math.round( - ((reportData.TenantInfo.DeviceOverview.ManagedDevices.desktopCount || 0) / - (reportData.TenantInfo.DeviceOverview.ManagedDevices.totalCount || 1)) * - 100 - )} - % - - - - - - Mobiles - - - {Math.round( - ((reportData.TenantInfo.DeviceOverview.ManagedDevices.mobileCount || 0) / - (reportData.TenantInfo.DeviceOverview.ManagedDevices.totalCount || 1)) * - 100 - )} - % - - - - - - - - {/* Device Compliance */} - - - - - Device compliance - - } - sx={{ pb: 1 }} - /> - - - - - - - - - - - - - - - - Compliant - - - - {Math.round( - (reportData.TenantInfo.DeviceOverview.DeviceCompliance - .compliantDeviceCount / - (reportData.TenantInfo.DeviceOverview.DeviceCompliance - .compliantDeviceCount + - reportData.TenantInfo.DeviceOverview.DeviceCompliance - .nonCompliantDeviceCount)) * - 100 - )} - % - - - - - - - - Non-compliant - - - - {Math.round( - (reportData.TenantInfo.DeviceOverview.DeviceCompliance - .nonCompliantDeviceCount / - (reportData.TenantInfo.DeviceOverview.DeviceCompliance - .compliantDeviceCount + - reportData.TenantInfo.DeviceOverview.DeviceCompliance - .nonCompliantDeviceCount)) * - 100 - )} - % - - - - - - - - {/* Device Ownership */} - - - - - Device ownership - - } - sx={{ pb: 1 }} - /> - - - - - - - - - - - - - - - - Corporate - - - - {Math.round( - (reportData.TenantInfo.DeviceOverview.DeviceOwnership.corporateCount / - (reportData.TenantInfo.DeviceOverview.DeviceOwnership.corporateCount + - reportData.TenantInfo.DeviceOverview.DeviceOwnership.personalCount)) * - 100 - )} - % - - - - - - - - Personal - - - - {Math.round( - (reportData.TenantInfo.DeviceOverview.DeviceOwnership.personalCount / - (reportData.TenantInfo.DeviceOverview.DeviceOwnership.corporateCount + - reportData.TenantInfo.DeviceOverview.DeviceOwnership.personalCount)) * - 100 - )} - % - - - - - - - - {/* Desktop Devices - Full Width */} - - - - - Desktop devices - - } - sx={{ pb: 1 }} - /> - - - {reportData.TenantInfo.DeviceOverview.DesktopDevicesSummary?.nodes && ( - - )} - - - {reportData.TenantInfo.DeviceOverview.DesktopDevicesSummary?.description || - "No description available"} - - - - - - - - Entra joined - - - {(() => { - const nodes = - reportData.TenantInfo.DeviceOverview.DesktopDevicesSummary?.nodes || []; - const entraJoined = - nodes.find((n) => n.target === "Entra joined")?.value || 0; - const windowsDevices = - nodes.find( - (n) => n.source === "Desktop devices" && n.target === "Windows" - )?.value || 0; - const macOSDevices = - nodes.find( - (n) => n.source === "Desktop devices" && n.target === "macOS" - )?.value || 0; - const total = windowsDevices + macOSDevices; - return Math.round((entraJoined / (total || 1)) * 100); - })()} - % - - - - - - Entra hybrid joined - - - {(() => { - const nodes = - reportData.TenantInfo.DeviceOverview.DesktopDevicesSummary?.nodes || []; - const entraHybrid = - nodes.find((n) => n.target === "Entra hybrid joined")?.value || 0; - const windowsDevices = - nodes.find( - (n) => n.source === "Desktop devices" && n.target === "Windows" - )?.value || 0; - const macOSDevices = - nodes.find( - (n) => n.source === "Desktop devices" && n.target === "macOS" - )?.value || 0; - const total = windowsDevices + macOSDevices; - return Math.round((entraHybrid / (total || 1)) * 100); - })()} - % - - - - - - Entra registered - - - {(() => { - const nodes = - reportData.TenantInfo.DeviceOverview.DesktopDevicesSummary?.nodes || []; - const entraRegistered = - nodes.find((n) => n.target === "Entra registered")?.value || 0; - const windowsDevices = - nodes.find( - (n) => n.source === "Desktop devices" && n.target === "Windows" - )?.value || 0; - const macOSDevices = - nodes.find( - (n) => n.source === "Desktop devices" && n.target === "macOS" - )?.value || 0; - const total = windowsDevices + macOSDevices; - return Math.round((entraRegistered / (total || 1)) * 100); - })()} - % - - - - - - + {/* Delete Report Dialog */} + - {/* Mobile Devices - Full Width */} - - - - - Mobile devices - - } - sx={{ pb: 1 }} - /> - - - {reportData.TenantInfo.DeviceOverview.MobileSummary?.nodes && ( - - )} - - - {reportData.TenantInfo.DeviceOverview.MobileSummary?.description || - "No description available"} - - - - - - - - Android compliant - - - {(() => { - const nodes = - reportData.TenantInfo.DeviceOverview.MobileSummary?.nodes || []; - const androidCompliant = nodes - .filter( - (n) => n.source?.includes("Android") && n.target === "Compliant" - ) - .reduce((sum, n) => sum + (n.value || 0), 0); - const androidTotal = - nodes.find( - (n) => n.source === "Mobile devices" && n.target === "Android" - )?.value || 0; - return androidTotal > 0 - ? Math.round((androidCompliant / androidTotal) * 100) - : 0; - })()} - % - - - - - - iOS compliant - - - {(() => { - const nodes = - reportData.TenantInfo.DeviceOverview.MobileSummary?.nodes || []; - const iosCompliant = nodes - .filter((n) => n.source?.includes("iOS") && n.target === "Compliant") - .reduce((sum, n) => sum + (n.value || 0), 0); - const iosTotal = - nodes.find((n) => n.source === "Mobile devices" && n.target === "iOS") - ?.value || 0; - return iosTotal > 0 ? Math.round((iosCompliant / iosTotal) * 100) : 0; - })()} - % - - - - - - Total devices - - - {(() => { - const nodes = - reportData.TenantInfo.DeviceOverview.MobileSummary?.nodes || []; - const androidTotal = - nodes.find( - (n) => n.source === "Mobile devices" && n.target === "Android" - )?.value || 0; - const iosTotal = - nodes.find((n) => n.source === "Mobile devices" && n.target === "iOS") - ?.value || 0; - return androidTotal + iosTotal; - })()} - - - - - - - - - + {/* Refresh Data Dialog */} + ); }; Page.getLayout = (page) => ( - + {page} ); diff --git a/src/pages/dashboardv2/tabOptions.json b/src/pages/dashboardv2/tabOptions.json index 4c2bb6411b4d..952c392e5c89 100644 --- a/src/pages/dashboardv2/tabOptions.json +++ b/src/pages/dashboardv2/tabOptions.json @@ -10,5 +10,9 @@ { "label": "Devices", "path": "/dashboardv2/devices" + }, + { + "label": "Previous Dashboard Experience", + "path": "/dashboardv1" } ] diff --git a/src/pages/email/administration/contacts-template/add.jsx b/src/pages/email/administration/contacts-template/add.jsx index 1f476fbe1ff7..b05da569e29e 100644 --- a/src/pages/email/administration/contacts-template/add.jsx +++ b/src/pages/email/administration/contacts-template/add.jsx @@ -1,7 +1,7 @@ import { useForm } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import ContactFormLayout from "/src/components/CippFormPages/CippAddEditContact"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import ContactFormLayout from "../../../../components/CippFormPages/CippAddEditContact"; const AddContactTemplates = () => { diff --git a/src/pages/email/administration/contacts-template/edit.jsx b/src/pages/email/administration/contacts-template/edit.jsx index 100eebe46731..987e9f45a3bb 100644 --- a/src/pages/email/administration/contacts-template/edit.jsx +++ b/src/pages/email/administration/contacts-template/edit.jsx @@ -1,11 +1,11 @@ import { useEffect, useMemo, useCallback } from "react"; import { useForm } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; -import ContactFormLayout from "/src/components/CippFormPages/CippAddEditContact"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormSkeleton from "../../../../components/CippFormPages/CippFormSkeleton"; +import ContactFormLayout from "../../../../components/CippFormPages/CippAddEditContact"; import { ApiGetCall } from "../../../../api/ApiCall"; -import countryList from "/src/data/countryList.json"; +import countryList from "../../../../data/countryList.json"; import { useRouter } from "next/router"; const countryLookup = new Map(countryList.map((country) => [country.Name, country.Code])); diff --git a/src/pages/email/administration/contacts-template/index.jsx b/src/pages/email/administration/contacts-template/index.jsx index 718d0c2f8ae5..d24604c9a9af 100644 --- a/src/pages/email/administration/contacts-template/index.jsx +++ b/src/pages/email/administration/contacts-template/index.jsx @@ -1,11 +1,11 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { Button } from "@mui/material"; import Link from "next/link"; import { RocketLaunch } from "@mui/icons-material"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { TrashIcon } from "@heroicons/react/24/outline"; import { GitHub, Edit } from "@mui/icons-material"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../api/ApiCall"; import { CippDeployContactTemplateDrawer } from "../../../../components/CippComponents/CippDeployContactTemplateDrawer"; const Page = () => { diff --git a/src/pages/email/administration/contacts/edit.jsx b/src/pages/email/administration/contacts/edit.jsx index 963b0be981d0..f47ef7c88d34 100644 --- a/src/pages/email/administration/contacts/edit.jsx +++ b/src/pages/email/administration/contacts/edit.jsx @@ -1,14 +1,14 @@ import { useEffect, useMemo, useCallback } from "react"; import { useRouter } from "next/router"; import { useForm } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormSkeleton from "../../../../components/CippFormPages/CippFormSkeleton"; import { useSettings } from "../../../../hooks/use-settings"; import { ApiGetCall } from "../../../../api/ApiCall"; -import countryList from "/src/data/countryList.json"; +import countryList from "../../../../data/countryList.json"; import { Grid } from "@mui/system"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; import { Divider } from "@mui/material"; const countryLookup = new Map(countryList.map((country) => [country.Name, country.Code])); diff --git a/src/pages/email/administration/contacts/index.js b/src/pages/email/administration/contacts/index.js index ce0becd832e1..471a06aa7e85 100644 --- a/src/pages/email/administration/contacts/index.js +++ b/src/pages/email/administration/contacts/index.js @@ -1,6 +1,6 @@ import { useMemo } from "react"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { CloudSync, Edit } from "@mui/icons-material"; import TrashIcon from "@heroicons/react/24/outline/TrashIcon"; import { CippAddContactDrawer } from "../../../../components/CippComponents/CippAddContactDrawer"; diff --git a/src/pages/email/administration/deleted-mailboxes/index.js b/src/pages/email/administration/deleted-mailboxes/index.js index 488c783dd71f..8b7841ddfedf 100644 --- a/src/pages/email/administration/deleted-mailboxes/index.js +++ b/src/pages/email/administration/deleted-mailboxes/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; const Page = () => { const pageTitle = "Deleted Mailboxes"; diff --git a/src/pages/email/administration/exchange-retention/policies/index.js b/src/pages/email/administration/exchange-retention/policies/index.js index ede3ed7034ea..650e79e76ea6 100644 --- a/src/pages/email/administration/exchange-retention/policies/index.js +++ b/src/pages/email/administration/exchange-retention/policies/index.js @@ -1,13 +1,13 @@ import { useMemo } from "react"; -import { Layout as DashboardLayout } from "/src/layouts/index"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage"; +import { Layout as DashboardLayout } from "../../../../../layouts/index"; +import { CippTablePage } from "../../../../../components/CippComponents/CippTablePage"; import { Policy, Edit } from "@mui/icons-material"; import { Button } from "@mui/material"; import Link from "next/link"; import TrashIcon from "@heroicons/react/24/outline/TrashIcon"; -import { HeaderedTabbedLayout } from "/src/layouts/HeaderedTabbedLayout"; +import { HeaderedTabbedLayout } from "../../../../../layouts/HeaderedTabbedLayout"; import tabOptions from "../tabOptions"; -import { useSettings } from "/src/hooks/use-settings"; +import { useSettings } from "../../../../../hooks/use-settings"; const Page = () => { const pageTitle = "Retention Policy Management"; diff --git a/src/pages/email/administration/exchange-retention/policies/policy.jsx b/src/pages/email/administration/exchange-retention/policies/policy.jsx index 7abef58d6a57..5118fbd13160 100644 --- a/src/pages/email/administration/exchange-retention/policies/policy.jsx +++ b/src/pages/email/administration/exchange-retention/policies/policy.jsx @@ -1,14 +1,14 @@ import { useForm } from "react-hook-form"; import { useEffect, useMemo } from "react"; import { useRouter } from "next/router"; -import { Layout as DashboardLayout } from "/src/layouts/index"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; -import { useSettings } from "/src/hooks/use-settings"; +import { Layout as DashboardLayout } from "../../../../../layouts/index"; +import CippFormPage from "../../../../../components/CippFormPages/CippFormPage"; +import CippFormSkeleton from "../../../../../components/CippFormPages/CippFormSkeleton"; +import { useSettings } from "../../../../../hooks/use-settings"; import { Grid } from "@mui/system"; import { Divider } from "@mui/material"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { ApiGetCall } from "/src/api/ApiCall"; +import CippFormComponent from "../../../../../components/CippComponents/CippFormComponent"; +import { ApiGetCall } from "../../../../../api/ApiCall"; const RetentionPolicy = () => { const userSettingsDefaults = useSettings(); diff --git a/src/pages/email/administration/exchange-retention/tags/index.js b/src/pages/email/administration/exchange-retention/tags/index.js index 8daa866d0c8a..9749f8b62aa7 100644 --- a/src/pages/email/administration/exchange-retention/tags/index.js +++ b/src/pages/email/administration/exchange-retention/tags/index.js @@ -1,13 +1,13 @@ import { useMemo } from "react"; -import { Layout as DashboardLayout } from "/src/layouts/index"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage"; +import { Layout as DashboardLayout } from "../../../../../layouts/index"; +import { CippTablePage } from "../../../../../components/CippComponents/CippTablePage"; import { Sell, Edit } from "@mui/icons-material"; import { Button } from "@mui/material"; import Link from "next/link"; import TrashIcon from "@heroicons/react/24/outline/TrashIcon"; -import { HeaderedTabbedLayout } from "/src/layouts/HeaderedTabbedLayout"; +import { HeaderedTabbedLayout } from "../../../../../layouts/HeaderedTabbedLayout"; import tabOptions from "../tabOptions"; -import { useSettings } from "/src/hooks/use-settings"; +import { useSettings } from "../../../../../hooks/use-settings"; const Page = () => { const pageTitle = "Retention Tag Management"; diff --git a/src/pages/email/administration/exchange-retention/tags/tag.jsx b/src/pages/email/administration/exchange-retention/tags/tag.jsx index 81fb60b87505..40d6291257a1 100644 --- a/src/pages/email/administration/exchange-retention/tags/tag.jsx +++ b/src/pages/email/administration/exchange-retention/tags/tag.jsx @@ -1,14 +1,14 @@ import { useForm } from "react-hook-form"; import { useEffect } from "react"; import { useRouter } from "next/router"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; -import { useSettings } from "/src/hooks/use-settings"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; +import CippFormPage from "../../../../../components/CippFormPages/CippFormPage"; +import CippFormSkeleton from "../../../../../components/CippFormPages/CippFormSkeleton"; +import { useSettings } from "../../../../../hooks/use-settings"; import { Grid } from "@mui/system"; import { Divider } from "@mui/material"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { ApiGetCall } from "/src/api/ApiCall"; +import CippFormComponent from "../../../../../components/CippComponents/CippFormComponent"; +import { ApiGetCall } from "../../../../../api/ApiCall"; const RetentionTag = () => { const userSettingsDefaults = useSettings(); diff --git a/src/pages/email/administration/mailbox-rules/index.js b/src/pages/email/administration/mailbox-rules/index.js index 956d9df8aa38..343b810e526c 100644 --- a/src/pages/email/administration/mailbox-rules/index.js +++ b/src/pages/email/administration/mailbox-rules/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { getCippTranslation } from "../../../../utils/get-cipp-translation"; import { getCippFormatting } from "../../../../utils/get-cipp-formatting"; import { CippPropertyListCard } from "../../../../components/CippCards/CippPropertyListCard"; diff --git a/src/pages/email/administration/mailboxes/addshared.jsx b/src/pages/email/administration/mailboxes/addshared.jsx index 7c1e00885b84..5992df015d1f 100644 --- a/src/pages/email/administration/mailboxes/addshared.jsx +++ b/src/pages/email/administration/mailboxes/addshared.jsx @@ -1,9 +1,9 @@ import { Divider } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; import { useSettings } from "../../../../hooks/use-settings"; import { CippFormDomainSelector } from "../../../../components/CippComponents/CippFormDomainSelector"; diff --git a/src/pages/email/administration/mailboxes/index.js b/src/pages/email/administration/mailboxes/index.js index c2abbe1b854e..eb2df5e015ae 100644 --- a/src/pages/email/administration/mailboxes/index.js +++ b/src/pages/email/administration/mailboxes/index.js @@ -1,8 +1,8 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import CippExchangeActions from "../../../../components/CippComponents/CippExchangeActions"; -import { CippHVEUserDrawer } from "/src/components/CippComponents/CippHVEUserDrawer.jsx"; -import { CippSharedMailboxDrawer } from "/src/components/CippComponents/CippSharedMailboxDrawer.jsx"; +import { CippHVEUserDrawer } from "../../../../components/CippComponents/CippHVEUserDrawer.jsx"; +import { CippSharedMailboxDrawer } from "../../../../components/CippComponents/CippSharedMailboxDrawer.jsx"; const Page = () => { const pageTitle = "Mailboxes"; diff --git a/src/pages/email/administration/quarantine/index.js b/src/pages/email/administration/quarantine/index.js index 3009973001a0..a5126764ba47 100644 --- a/src/pages/email/administration/quarantine/index.js +++ b/src/pages/email/administration/quarantine/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { useEffect, useState } from "react"; import { Dialog, @@ -11,11 +11,11 @@ import { CircularProgress, } from "@mui/material"; import { Block, Close, Done, DoneAll } from "@mui/icons-material"; -import { CippMessageViewer } from "/src/components/CippComponents/CippMessageViewer.jsx"; -import { ApiGetCall, ApiPostCall } from "/src/api/ApiCall"; -import { useSettings } from "/src/hooks/use-settings"; +import { CippMessageViewer } from "../../../../components/CippComponents/CippMessageViewer.jsx"; +import { ApiGetCall, ApiPostCall } from "../../../../api/ApiCall"; +import { useSettings } from "../../../../hooks/use-settings"; import { EyeIcon, DocumentTextIcon } from "@heroicons/react/24/outline"; -import { CippDataTable } from "/src/components/CippTable/CippDataTable"; +import { CippDataTable } from "../../../../components/CippTable/CippDataTable"; const simpleColumns = [ "ReceivedTime", diff --git a/src/pages/email/administration/restricted-users/index.js b/src/pages/email/administration/restricted-users/index.js index f02e27356442..8adf4ecdef57 100644 --- a/src/pages/email/administration/restricted-users/index.js +++ b/src/pages/email/administration/restricted-users/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Alert, Link, Typography, List, ListItem, ListItemText } from "@mui/material"; import { Block as BlockIcon } from "@mui/icons-material"; diff --git a/src/pages/email/administration/tenant-allow-block-lists/index.js b/src/pages/email/administration/tenant-allow-block-lists/index.js index 628eacd1be83..00b5c690792e 100644 --- a/src/pages/email/administration/tenant-allow-block-lists/index.js +++ b/src/pages/email/administration/tenant-allow-block-lists/index.js @@ -1,7 +1,7 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Delete } from "@mui/icons-material"; -import { CippAddTenantAllowBlockListDrawer } from "/src/components/CippComponents/CippAddTenantAllowBlockListDrawer.jsx"; +import { CippAddTenantAllowBlockListDrawer } from "../../../../components/CippComponents/CippAddTenantAllowBlockListDrawer.jsx"; const Page = () => { const pageTitle = "Tenant Allow/Block Lists"; diff --git a/src/pages/email/reports/SharedMailboxEnabledAccount/index.js b/src/pages/email/reports/SharedMailboxEnabledAccount/index.js index 4323f53703aa..3dd3d08c5398 100644 --- a/src/pages/email/reports/SharedMailboxEnabledAccount/index.js +++ b/src/pages/email/reports/SharedMailboxEnabledAccount/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Block } from "@mui/icons-material"; const Page = () => { diff --git a/src/pages/email/reports/antiphishing-filters/index.js b/src/pages/email/reports/antiphishing-filters/index.js index c691d035088b..d23dea441539 100644 --- a/src/pages/email/reports/antiphishing-filters/index.js +++ b/src/pages/email/reports/antiphishing-filters/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Block, Check } from "@mui/icons-material"; const Page = () => { 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..2eef1d98477d --- /dev/null +++ b/src/pages/email/reports/calendar-permissions/index.js @@ -0,0 +1,117 @@ +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../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/email/reports/global-address-list/index.js b/src/pages/email/reports/global-address-list/index.js index c8b7ff18d0d7..adbe3ae6b86d 100644 --- a/src/pages/email/reports/global-address-list/index.js +++ b/src/pages/email/reports/global-address-list/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Visibility, VisibilityOff } from "@mui/icons-material"; const Page = () => { diff --git a/src/pages/email/reports/mailbox-activity/index.js b/src/pages/email/reports/mailbox-activity/index.js index 554f6337b717..6010951bd406 100644 --- a/src/pages/email/reports/mailbox-activity/index.js +++ b/src/pages/email/reports/mailbox-activity/index.js @@ -1,6 +1,6 @@ import { useState } from "react"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Button, Accordion, @@ -14,7 +14,7 @@ import { Grid } from "@mui/system"; import { ExpandMore, Sort } from "@mui/icons-material"; import { FunnelIcon, XMarkIcon } from "@heroicons/react/24/outline"; import { useForm } from "react-hook-form"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; const Page = () => { const formControl = useForm({ diff --git a/src/pages/email/reports/mailbox-cas-settings/index.js b/src/pages/email/reports/mailbox-cas-settings/index.js index 10cb93963b58..1058c83e93b2 100644 --- a/src/pages/email/reports/mailbox-cas-settings/index.js +++ b/src/pages/email/reports/mailbox-cas-settings/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; const Page = () => { return ( diff --git a/src/pages/email/reports/mailbox-permissions/index.js b/src/pages/email/reports/mailbox-permissions/index.js new file mode 100644 index 000000000000..2d88aae48d04 --- /dev/null +++ b/src/pages/email/reports/mailbox-permissions/index.js @@ -0,0 +1,117 @@ +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../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"] : []), + "MailboxUPN", + "MailboxDisplayName", + "MailboxType", + "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 mailbox permissions. + )} + + + ); +}; + +Page.getLayout = (page) => {page}; + +export default Page; diff --git a/src/pages/email/reports/mailbox-statistics/index.js b/src/pages/email/reports/mailbox-statistics/index.js index 9291843bab97..18167383ee6f 100644 --- a/src/pages/email/reports/mailbox-statistics/index.js +++ b/src/pages/email/reports/mailbox-statistics/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; const Page = () => { return ( diff --git a/src/pages/email/reports/malware-filters/index.js b/src/pages/email/reports/malware-filters/index.js index 20f09161b7d1..b049cef12444 100644 --- a/src/pages/email/reports/malware-filters/index.js +++ b/src/pages/email/reports/malware-filters/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Block, Check } from "@mui/icons-material"; const Page = () => { diff --git a/src/pages/email/reports/safeattachments-filters/index.js b/src/pages/email/reports/safeattachments-filters/index.js index a35d329be6ca..b8212f38cc74 100644 --- a/src/pages/email/reports/safeattachments-filters/index.js +++ b/src/pages/email/reports/safeattachments-filters/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Block, Check } from "@mui/icons-material"; const Page = () => { diff --git a/src/pages/email/resources/management/equipment/edit.jsx b/src/pages/email/resources/management/equipment/edit.jsx index a29c8b84ac0f..1e6cd1f2a5e6 100644 --- a/src/pages/email/resources/management/equipment/edit.jsx +++ b/src/pages/email/resources/management/equipment/edit.jsx @@ -2,15 +2,15 @@ import React, { useEffect } from "react"; import { Divider, Typography } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; -import { useSettings } from "/src/hooks/use-settings"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; +import CippFormPage from "../../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../../components/CippComponents/CippFormComponent"; +import CippFormSkeleton from "../../../../../components/CippFormPages/CippFormSkeleton"; +import { useSettings } from "../../../../../hooks/use-settings"; import { useRouter } from "next/router"; -import { ApiGetCall } from "/src/api/ApiCall"; -import countryList from "/src/data/countryList.json"; -import timezoneList from "/src/data/timezoneList.json"; +import { ApiGetCall } from "../../../../../api/ApiCall"; +import countryList from "../../../../../data/countryList.json"; +import timezoneList from "../../../../../data/timezoneList.json"; // Work days options const workDaysOptions = [ diff --git a/src/pages/email/resources/management/equipment/index.js b/src/pages/email/resources/management/equipment/index.js index 538143f24b96..4a1b45a30413 100644 --- a/src/pages/email/resources/management/equipment/index.js +++ b/src/pages/email/resources/management/equipment/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; +import { CippTablePage } from "../../../../../components/CippComponents/CippTablePage.jsx"; import { Edit, Block, LockOpen, Key } from "@mui/icons-material"; import { TrashIcon } from "@heroicons/react/24/outline"; import { CippAddEquipmentDrawer } from "../../../../../components/CippComponents/CippAddEquipmentDrawer"; diff --git a/src/pages/email/resources/management/list-rooms/edit.jsx b/src/pages/email/resources/management/list-rooms/edit.jsx index 97cb3f35078e..a8125a3f8be8 100644 --- a/src/pages/email/resources/management/list-rooms/edit.jsx +++ b/src/pages/email/resources/management/list-rooms/edit.jsx @@ -1,16 +1,17 @@ import { useEffect } from "react"; -import { Divider, Typography } from "@mui/material"; +import { Box, Divider, IconButton, Tooltip, Typography } from "@mui/material"; +import { Sync } from "@mui/icons-material"; import { Grid } from "@mui/system"; import { useForm } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; -import { useSettings } from "/src/hooks/use-settings"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; +import CippFormPage from "../../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../../components/CippComponents/CippFormComponent"; +import CippFormSkeleton from "../../../../../components/CippFormPages/CippFormSkeleton"; +import { useSettings } from "../../../../../hooks/use-settings"; import { useRouter } from "next/router"; -import { ApiGetCall } from "/src/api/ApiCall"; -import countryList from "/src/data/countryList.json"; -import timezoneList from "/src/data/timezoneList.json"; +import { ApiGetCall } from "../../../../../api/ApiCall"; +import countryList from "../../../../../data/countryList.json"; +import timezoneList from "../../../../../data/timezoneList.json"; // Work days options const workDaysOptions = [ @@ -90,6 +91,9 @@ const EditRoomMailbox = () => { ForwardRequestsToDelegates: room.ForwardRequestsToDelegates, ScheduleOnlyDuringWorkHours: room.ScheduleOnlyDuringWorkHours, AutomateProcessing: room.AutomateProcessing, + AddOrganizerToSubject: room.AddOrganizerToSubject, + DeleteSubject: room.DeleteSubject, + RemoveCanceledMeetings: room.RemoveCanceledMeetings, // Calendar Configuration WorkDays: @@ -166,6 +170,9 @@ const EditRoomMailbox = () => { ForwardRequestsToDelegates: values.ForwardRequestsToDelegates, ScheduleOnlyDuringWorkHours: values.ScheduleOnlyDuringWorkHours, AutomateProcessing: values.AutomateProcessing?.value || values.AutomateProcessing, + AddOrganizerToSubject: values.AddOrganizerToSubject, + DeleteSubject: values.DeleteSubject, + RemoveCanceledMeetings: values.RemoveCanceledMeetings, // Calendar Configuration WorkDays: values.WorkDays?.map((day) => day.value).join(","), @@ -174,16 +181,21 @@ const EditRoomMailbox = () => { WorkingHoursTimeZone: values.WorkingHoursTimeZone?.value || values.WorkingHoursTimeZone, })} > - {roomInfo.isLoading && ( + {roomInfo.isFetching && ( )} - {roomInfo.isSuccess && ( + {roomInfo.isSuccess && !roomInfo.isFetching && ( {/* Basic Information */} - - Basic Information - + + Basic Information + + roomInfo.refetch()}> + + + + @@ -305,6 +317,30 @@ const EditRoomMailbox = () => { formControl={formControl} /> + + + + + + + + + {/* Working Hours */} diff --git a/src/pages/email/resources/management/list-rooms/index.js b/src/pages/email/resources/management/list-rooms/index.js index 61201421734a..5348f25ce5dc 100644 --- a/src/pages/email/resources/management/list-rooms/index.js +++ b/src/pages/email/resources/management/list-rooms/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; +import { CippTablePage } from "../../../../../components/CippComponents/CippTablePage.jsx"; import { Edit, Block, LockOpen, Key } from "@mui/icons-material"; import { TrashIcon } from "@heroicons/react/24/outline"; import { CippAddRoomDrawer } from "../../../../../components/CippComponents/CippAddRoomDrawer"; diff --git a/src/pages/email/resources/management/room-lists/edit.jsx b/src/pages/email/resources/management/room-lists/edit.jsx index b1d77015f017..7fa8c786936e 100644 --- a/src/pages/email/resources/management/room-lists/edit.jsx +++ b/src/pages/email/resources/management/room-lists/edit.jsx @@ -2,10 +2,10 @@ import { useEffect, useState } from "react"; import { Box, Button, Divider, Typography } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormUserSelector } from "/src/components/CippComponents/CippFormUserSelector"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; +import CippFormPage from "../../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../../components/CippComponents/CippFormComponent"; +import { CippFormUserSelector } from "../../../../../components/CippComponents/CippFormUserSelector"; import { useRouter } from "next/router"; import { ApiGetCall } from "../../../../../api/ApiCall"; import { useSettings } from "../../../../../hooks/use-settings"; diff --git a/src/pages/email/resources/management/room-lists/index.js b/src/pages/email/resources/management/room-lists/index.js index ce8573687897..bff09714fd9f 100644 --- a/src/pages/email/resources/management/room-lists/index.js +++ b/src/pages/email/resources/management/room-lists/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; +import { CippTablePage } from "../../../../../components/CippComponents/CippTablePage.jsx"; import { Edit } from "@mui/icons-material"; import { TrashIcon } from "@heroicons/react/24/outline"; import { CippAddRoomListDrawer } from "../../../../../components/CippComponents/CippAddRoomListDrawer"; diff --git a/src/pages/email/spamfilter/list-connectionfilter-templates/index.js b/src/pages/email/spamfilter/list-connectionfilter-templates/index.js index f5d660457154..ff908fa0bb14 100644 --- a/src/pages/email/spamfilter/list-connectionfilter-templates/index.js +++ b/src/pages/email/spamfilter/list-connectionfilter-templates/index.js @@ -1,8 +1,8 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { TrashIcon } from "@heroicons/react/24/outline"; import { GitHub } from "@mui/icons-material"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../api/ApiCall"; const Page = () => { const pageTitle = "Connection filter Templates"; diff --git a/src/pages/email/spamfilter/list-connectionfilter/add.jsx b/src/pages/email/spamfilter/list-connectionfilter/add.jsx index a0c30550356d..77a9dabfa170 100644 --- a/src/pages/email/spamfilter/list-connectionfilter/add.jsx +++ b/src/pages/email/spamfilter/list-connectionfilter/add.jsx @@ -2,10 +2,10 @@ import React, { useEffect } from "react"; import { Divider } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm, useWatch } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormTenantSelector } from "/src/components/CippComponents/CippFormTenantSelector"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { CippFormTenantSelector } from "../../../../components/CippComponents/CippFormTenantSelector"; const AddPolicy = () => { const formControl = useForm({ diff --git a/src/pages/email/spamfilter/list-connectionfilter/index.js b/src/pages/email/spamfilter/list-connectionfilter/index.js index 3ce94c37ec8b..027dea277e30 100644 --- a/src/pages/email/spamfilter/list-connectionfilter/index.js +++ b/src/pages/email/spamfilter/list-connectionfilter/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Button } from "@mui/material"; import { Book, AddModerator } from "@mui/icons-material"; import Link from "next/link"; diff --git a/src/pages/email/spamfilter/list-quarantine-policies/add.jsx b/src/pages/email/spamfilter/list-quarantine-policies/add.jsx index 69def81cf558..c2fe68b9e437 100644 --- a/src/pages/email/spamfilter/list-quarantine-policies/add.jsx +++ b/src/pages/email/spamfilter/list-quarantine-policies/add.jsx @@ -2,10 +2,10 @@ import { useEffect } from "react"; import { Divider } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm, useWatch } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormTenantSelector } from "/src/components/CippComponents/CippFormTenantSelector"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { CippFormTenantSelector } from "../../../../components/CippComponents/CippFormTenantSelector"; const AddPolicy = () => { const formControl = useForm({ diff --git a/src/pages/email/spamfilter/list-quarantine-policies/index.js b/src/pages/email/spamfilter/list-quarantine-policies/index.js index 021c90820439..33d1101bd445 100644 --- a/src/pages/email/spamfilter/list-quarantine-policies/index.js +++ b/src/pages/email/spamfilter/list-quarantine-policies/index.js @@ -1,8 +1,8 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import CippButtonCard from "/src/components/CippCards/CippButtonCard"; -import { CippInfoBar } from "/src/components/CippCards/CippInfoBar"; -import { CippApiDialog } from "/src/components/CippComponents/CippApiDialog.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import CippButtonCard from "../../../../components/CippCards/CippButtonCard"; +import { CippInfoBar } from "../../../../components/CippCards/CippInfoBar"; +import { CippApiDialog } from "../../../../components/CippComponents/CippApiDialog.jsx"; import { Alert, Typography, Stack, Tooltip, IconButton, SvgIcon, Button } from "@mui/material"; import { Grid } from "@mui/system"; import Link from "next/link"; @@ -16,9 +16,9 @@ import { Edit, Delete, } from "@mui/icons-material"; -import { useSettings } from "/src/hooks/use-settings"; -import { useDialog } from "/src/hooks/use-dialog"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { useSettings } from "../../../../hooks/use-settings"; +import { useDialog } from "../../../../hooks/use-dialog"; +import { ApiGetCall } from "../../../../api/ApiCall"; const Page = () => { const pageTitle = "Quarantine Policies"; diff --git a/src/pages/email/spamfilter/list-spamfilter/add.jsx b/src/pages/email/spamfilter/list-spamfilter/add.jsx index b25c936e8ecc..824844d8bdf0 100644 --- a/src/pages/email/spamfilter/list-spamfilter/add.jsx +++ b/src/pages/email/spamfilter/list-spamfilter/add.jsx @@ -2,10 +2,10 @@ import React, { useEffect } from "react"; import { Divider } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm, useWatch } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormTenantSelector } from "/src/components/CippComponents/CippFormTenantSelector"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { CippFormTenantSelector } from "../../../../components/CippComponents/CippFormTenantSelector"; const AddPolicy = () => { const formControl = useForm({ diff --git a/src/pages/email/spamfilter/list-spamfilter/index.js b/src/pages/email/spamfilter/list-spamfilter/index.js index d6a2fe54dd01..dc2123ea8f93 100644 --- a/src/pages/email/spamfilter/list-spamfilter/index.js +++ b/src/pages/email/spamfilter/list-spamfilter/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Button } from "@mui/material"; import { Book, Block, Check } from "@mui/icons-material"; import { TrashIcon } from "@heroicons/react/24/outline"; diff --git a/src/pages/email/spamfilter/list-templates/index.js b/src/pages/email/spamfilter/list-templates/index.js index adcc2884e065..3abe32d9d210 100644 --- a/src/pages/email/spamfilter/list-templates/index.js +++ b/src/pages/email/spamfilter/list-templates/index.js @@ -1,8 +1,8 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { TrashIcon } from "@heroicons/react/24/outline"; import { GitHub } from "@mui/icons-material"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../api/ApiCall"; const Page = () => { const pageTitle = "Spamfilter Templates"; diff --git a/src/pages/email/tools/mail-test/index.js b/src/pages/email/tools/mail-test/index.js index 9edd92002471..0f9871807038 100644 --- a/src/pages/email/tools/mail-test/index.js +++ b/src/pages/email/tools/mail-test/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; const Page = () => { const pageTitle = "Mail Test"; diff --git a/src/pages/email/tools/mailbox-restore-wizard/index.js b/src/pages/email/tools/mailbox-restore-wizard/index.js index 407eba25f97d..fb7521dfd4cb 100644 --- a/src/pages/email/tools/mailbox-restore-wizard/index.js +++ b/src/pages/email/tools/mailbox-restore-wizard/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; const Page = () => { const pageTitle = "Mailbox Restore Wizard"; diff --git a/src/pages/email/tools/mailbox-restores/add.jsx b/src/pages/email/tools/mailbox-restores/add.jsx index 75602521ec80..a238c0c29b1a 100644 --- a/src/pages/email/tools/mailbox-restores/add.jsx +++ b/src/pages/email/tools/mailbox-restores/add.jsx @@ -1,9 +1,9 @@ import { useEffect } from "react"; import { Grid } from "@mui/system"; import { useForm, useWatch } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; import { useSettings } from "../../../../hooks/use-settings"; import { getCippTranslation } from "../../../../utils/get-cipp-translation"; import { Alert, Divider, Typography } from "@mui/material"; diff --git a/src/pages/email/tools/mailbox-restores/index.js b/src/pages/email/tools/mailbox-restores/index.js index 0cd211b5aa4d..bc2e200ce30c 100644 --- a/src/pages/email/tools/mailbox-restores/index.js +++ b/src/pages/email/tools/mailbox-restores/index.js @@ -1,9 +1,9 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { RestoreFromTrash, PlayArrow, Pause, Delete } from "@mui/icons-material"; import MailboxRestoreDetails from "../../../../components/CippComponents/MailboxRestoreDetails"; import { CippMailboxRestoreDrawer } from "../../../../components/CippComponents/CippMailboxRestoreDrawer"; -import { useSettings } from "/src/hooks/use-settings"; +import { useSettings } from "../../../../hooks/use-settings"; const Page = () => { const pageTitle = "Mailbox Restores"; diff --git a/src/pages/email/tools/message-trace/index.js b/src/pages/email/tools/message-trace/index.js index eabef17792e5..56ccf9bcd20a 100644 --- a/src/pages/email/tools/message-trace/index.js +++ b/src/pages/email/tools/message-trace/index.js @@ -1,4 +1,4 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { Button, Dialog, @@ -10,11 +10,11 @@ import { CircularProgress, } from "@mui/material"; import { useForm } from "react-hook-form"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { ApiPostCall } from "/src/api/ApiCall"; -import { useSettings } from "/src/hooks/use-settings"; -import CippButtonCard from "/src/components/CippCards/CippButtonCard"; -import { CippDataTable } from "/src/components/CippTable/CippDataTable"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { ApiPostCall } from "../../../../api/ApiCall"; +import { useSettings } from "../../../../hooks/use-settings"; +import CippButtonCard from "../../../../components/CippCards/CippButtonCard"; +import { CippDataTable } from "../../../../components/CippTable/CippDataTable"; import { useState } from "react"; import { Search, Close, ClearAll } from "@mui/icons-material"; import { Grid } from "@mui/system"; diff --git a/src/pages/email/tools/message-viewer/index.js b/src/pages/email/tools/message-viewer/index.js index ab5b7499c8a0..2521f67b1e22 100644 --- a/src/pages/email/tools/message-viewer/index.js +++ b/src/pages/email/tools/message-viewer/index.js @@ -1,5 +1,5 @@ import CippMessageViewerPage from "../../../../components/CippComponents/CippMessageViewer"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; const Page = () => { return ; diff --git a/src/pages/email/transport/list-connector-templates/index.js b/src/pages/email/transport/list-connector-templates/index.js index 2e2b3e406959..4357ed52af23 100644 --- a/src/pages/email/transport/list-connector-templates/index.js +++ b/src/pages/email/transport/list-connector-templates/index.js @@ -1,13 +1,13 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { Button } from "@mui/material"; import Link from "next/link"; import { RocketLaunch } from "@mui/icons-material"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { TrashIcon } from "@heroicons/react/24/outline"; import { GitHub } from "@mui/icons-material"; import ConnectorTemplateDetails from "../../../../components/CippComponents/ConnectorTemplateDetails"; import { CippAddConnectorDrawer } from "../../../../components/CippComponents/CippAddConnectorDrawer"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../api/ApiCall"; const Page = () => { const pageTitle = "Exchange Connector Templates"; diff --git a/src/pages/email/transport/list-connectors/index.js b/src/pages/email/transport/list-connectors/index.js index 4a3eeebcc84c..8bc44bbe7f81 100644 --- a/src/pages/email/transport/list-connectors/index.js +++ b/src/pages/email/transport/list-connectors/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Book, Check, Block, Delete } from "@mui/icons-material"; import { CippAddConnectorDrawer } from "../../../../components/CippComponents/CippAddConnectorDrawer"; diff --git a/src/pages/email/transport/list-rules/index.js b/src/pages/email/transport/list-rules/index.js index 2bf7e928c254..3ff3e24d4dd8 100644 --- a/src/pages/email/transport/list-rules/index.js +++ b/src/pages/email/transport/list-rules/index.js @@ -1,10 +1,10 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Book, DoDisturb, Done, Edit } from "@mui/icons-material"; import { TrashIcon } from "@heroicons/react/24/outline"; import { CippAddTransportRuleDrawer } from "../../../../components/CippComponents/CippAddTransportRuleDrawer"; import { CippTransportRuleDrawer } from "../../../../components/CippComponents/CippTransportRuleDrawer"; -import { useSettings } from "/src/hooks/use-settings"; +import { useSettings } from "../../../../hooks/use-settings"; import { useRef } from "react"; const Page = () => { diff --git a/src/pages/email/transport/list-templates/index.js b/src/pages/email/transport/list-templates/index.js index 0f4d417efeae..3a9ee1947f52 100644 --- a/src/pages/email/transport/list-templates/index.js +++ b/src/pages/email/transport/list-templates/index.js @@ -1,8 +1,8 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { TrashIcon } from "@heroicons/react/24/outline"; import { GitHub } from "@mui/icons-material"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../api/ApiCall"; import { CippAddTransportRuleDrawer } from "../../../../components/CippComponents/CippAddTransportRuleDrawer"; const Page = () => { diff --git a/src/pages/endpoint/MEM/assignment-filter-templates/add.jsx b/src/pages/endpoint/MEM/assignment-filter-templates/add.jsx index ca731ffd54cb..e955a271bc99 100644 --- a/src/pages/endpoint/MEM/assignment-filter-templates/add.jsx +++ b/src/pages/endpoint/MEM/assignment-filter-templates/add.jsx @@ -1,6 +1,6 @@ import { Box } from "@mui/material"; import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm } from "react-hook-form"; import { useSettings } from "../../../../hooks/use-settings"; import CippAddAssignmentFilterTemplateForm from "../../../../components/CippFormPages/CippAddAssignmentFilterTemplateForm"; diff --git a/src/pages/endpoint/MEM/assignment-filter-templates/deploy.js b/src/pages/endpoint/MEM/assignment-filter-templates/deploy.js index 5c5f9c0ce1df..4943b2868ef9 100644 --- a/src/pages/endpoint/MEM/assignment-filter-templates/deploy.js +++ b/src/pages/endpoint/MEM/assignment-filter-templates/deploy.js @@ -1,7 +1,7 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippWizardConfirmation } from "/src/components/CippWizard/CippWizardConfirmation"; -import CippWizardPage from "/src/components/CippWizard/CippWizardPage.jsx"; -import { CippTenantStep } from "/src/components/CippWizard/CippTenantStep.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippWizardConfirmation } from "../../../../components/CippWizard/CippWizardConfirmation"; +import CippWizardPage from "../../../../components/CippWizard/CippWizardPage.jsx"; +import { CippTenantStep } from "../../../../components/CippWizard/CippTenantStep.jsx"; import { CippWizardAssignmentFilterTemplates } from "../../../../components/CippWizard/CippWizardAssignmentFilterTemplates"; const Page = () => { diff --git a/src/pages/endpoint/MEM/assignment-filter-templates/edit.jsx b/src/pages/endpoint/MEM/assignment-filter-templates/edit.jsx index 2d10d7502e61..0eb68a160c8a 100644 --- a/src/pages/endpoint/MEM/assignment-filter-templates/edit.jsx +++ b/src/pages/endpoint/MEM/assignment-filter-templates/edit.jsx @@ -1,6 +1,6 @@ import { Box, CircularProgress } from "@mui/material"; import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm } from "react-hook-form"; import { useSettings } from "../../../../hooks/use-settings"; import CippAddAssignmentFilterTemplateForm from "../../../../components/CippFormPages/CippAddAssignmentFilterTemplateForm"; diff --git a/src/pages/endpoint/MEM/assignment-filter-templates/index.js b/src/pages/endpoint/MEM/assignment-filter-templates/index.js index b3736a0fc346..5634b8b7e699 100644 --- a/src/pages/endpoint/MEM/assignment-filter-templates/index.js +++ b/src/pages/endpoint/MEM/assignment-filter-templates/index.js @@ -1,9 +1,9 @@ import { Button } from "@mui/material"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { AddBox, RocketLaunch, Delete, GitHub, Edit } from "@mui/icons-material"; import Link from "next/link"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../api/ApiCall"; import { CippPropertyListCard } from "../../../../components/CippCards/CippPropertyListCard"; import { getCippTranslation } from "../../../../utils/get-cipp-translation"; import { getCippFormatting } from "../../../../utils/get-cipp-formatting"; diff --git a/src/pages/endpoint/MEM/assignment-filters/add.jsx b/src/pages/endpoint/MEM/assignment-filters/add.jsx index 68c8cb027983..c4aead07619d 100644 --- a/src/pages/endpoint/MEM/assignment-filters/add.jsx +++ b/src/pages/endpoint/MEM/assignment-filters/add.jsx @@ -1,6 +1,6 @@ import { Box } from "@mui/material"; import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm } from "react-hook-form"; import { useSettings } from "../../../../hooks/use-settings"; import { useEffect } from "react"; diff --git a/src/pages/endpoint/MEM/assignment-filters/edit.jsx b/src/pages/endpoint/MEM/assignment-filters/edit.jsx index 0fae5e9b6b0d..f5a6dbad163f 100644 --- a/src/pages/endpoint/MEM/assignment-filters/edit.jsx +++ b/src/pages/endpoint/MEM/assignment-filters/edit.jsx @@ -1,8 +1,8 @@ import { useEffect, useState } from "react"; import { Box } from "@mui/material"; import { useForm } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; import { useRouter } from "next/router"; import { ApiGetCall } from "../../../../api/ApiCall"; import { useSettings } from "../../../../hooks/use-settings"; diff --git a/src/pages/endpoint/MEM/assignment-filters/index.js b/src/pages/endpoint/MEM/assignment-filters/index.js index 0260840a9ba9..462647494c98 100644 --- a/src/pages/endpoint/MEM/assignment-filters/index.js +++ b/src/pages/endpoint/MEM/assignment-filters/index.js @@ -1,6 +1,6 @@ import { Button } from "@mui/material"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import Link from "next/link"; import { TrashIcon } from "@heroicons/react/24/outline"; import { Edit, Add, Book } from "@mui/icons-material"; diff --git a/src/pages/endpoint/MEM/devices/index.js b/src/pages/endpoint/MEM/devices/index.js index e2d5c52ef4ef..f2f905a968ce 100644 --- a/src/pages/endpoint/MEM/devices/index.js +++ b/src/pages/endpoint/MEM/devices/index.js @@ -1,6 +1,6 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { useSettings } from "/src/hooks/use-settings"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { useSettings } from "../../../../hooks/use-settings"; import { EyeIcon } from "@heroicons/react/24/outline"; import { Sync, @@ -189,7 +189,7 @@ const Page = () => { url: "/api/ExecDevicePasscodeAction", data: { GUID: "id", - Action: "removeDevicePasscode", + Action: "resetPasscode", }, condition: (row) => row.operatingSystem === "iOS", confirmText: diff --git a/src/pages/endpoint/MEM/list-appprotection-policies/index.js b/src/pages/endpoint/MEM/list-appprotection-policies/index.js index f86460eab062..85349e8668ed 100644 --- a/src/pages/endpoint/MEM/list-appprotection-policies/index.js +++ b/src/pages/endpoint/MEM/list-appprotection-policies/index.js @@ -1,10 +1,10 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Book, LaptopChromebook } from "@mui/icons-material"; import { GlobeAltIcon, TrashIcon, UserIcon, UserGroupIcon } from "@heroicons/react/24/outline"; -import { PermissionButton } from "/src/utils/permissions.js"; -import { CippPolicyDeployDrawer } from "/src/components/CippComponents/CippPolicyDeployDrawer.jsx"; -import { useSettings } from "/src/hooks/use-settings.js"; +import { PermissionButton } from "../../../../utils/permissions.js"; +import { CippPolicyDeployDrawer } from "../../../../components/CippComponents/CippPolicyDeployDrawer.jsx"; +import { useSettings } from "../../../../hooks/use-settings.js"; const assignmentModeOptions = [ { label: "Replace existing assignments", value: "replace" }, diff --git a/src/pages/endpoint/MEM/list-compliance-policies/index.js b/src/pages/endpoint/MEM/list-compliance-policies/index.js index 1a3cf8a86de6..dcb37a6eadc7 100644 --- a/src/pages/endpoint/MEM/list-compliance-policies/index.js +++ b/src/pages/endpoint/MEM/list-compliance-policies/index.js @@ -1,10 +1,10 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Book, LaptopChromebook } from "@mui/icons-material"; import { GlobeAltIcon, TrashIcon, UserIcon, UserGroupIcon } from "@heroicons/react/24/outline"; -import { PermissionButton } from "/src/utils/permissions.js"; -import { CippPolicyDeployDrawer } from "/src/components/CippComponents/CippPolicyDeployDrawer.jsx"; -import { useSettings } from "/src/hooks/use-settings.js"; +import { PermissionButton } from "../../../../utils/permissions.js"; +import { CippPolicyDeployDrawer } from "../../../../components/CippComponents/CippPolicyDeployDrawer.jsx"; +import { useSettings } from "../../../../hooks/use-settings.js"; const assignmentModeOptions = [ { label: "Replace existing assignments", value: "replace" }, diff --git a/src/pages/endpoint/MEM/list-policies/index.js b/src/pages/endpoint/MEM/list-policies/index.js index 9682d72d9ce8..ec6fa08d0d08 100644 --- a/src/pages/endpoint/MEM/list-policies/index.js +++ b/src/pages/endpoint/MEM/list-policies/index.js @@ -1,10 +1,10 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Book, LaptopChromebook } from "@mui/icons-material"; import { GlobeAltIcon, TrashIcon, UserIcon, UserGroupIcon } from "@heroicons/react/24/outline"; -import { PermissionButton } from "/src/utils/permissions.js"; -import { CippPolicyDeployDrawer } from "/src/components/CippComponents/CippPolicyDeployDrawer.jsx"; -import { useSettings } from "/src/hooks/use-settings.js"; +import { PermissionButton } from "../../../../utils/permissions.js"; +import { CippPolicyDeployDrawer } from "../../../../components/CippComponents/CippPolicyDeployDrawer.jsx"; +import { useSettings } from "../../../../hooks/use-settings.js"; const assignmentModeOptions = [ { label: "Replace existing assignments", value: "replace" }, diff --git a/src/pages/endpoint/MEM/list-scripts/index.jsx b/src/pages/endpoint/MEM/list-scripts/index.jsx index 8ace41a37f4f..91eb0bf675a6 100644 --- a/src/pages/endpoint/MEM/list-scripts/index.jsx +++ b/src/pages/endpoint/MEM/list-scripts/index.jsx @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage"; +import { Layout as DashboardLayout } from "../../../../layouts/index"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage"; import { TrashIcon, PencilIcon, @@ -7,7 +7,7 @@ import { UserGroupIcon, GlobeAltIcon, } from "@heroicons/react/24/outline"; -import { showToast } from "/src/store/toasts"; +import { showToast } from "../../../../store/toasts"; import { Button, Dialog, @@ -17,7 +17,7 @@ import { CircularProgress, DialogActions, } from "@mui/material"; -import { CippCodeBlock } from "/src/components/CippComponents/CippCodeBlock"; +import { CippCodeBlock } from "../../../../components/CippComponents/CippCodeBlock"; import { useState, useEffect, useMemo } from "react"; import { useDispatch } from "react-redux"; import { Close, Save, LaptopChromebook } from "@mui/icons-material"; diff --git a/src/pages/endpoint/MEM/list-templates/edit.jsx b/src/pages/endpoint/MEM/list-templates/edit.jsx index c58cbe7e0920..3e0f7c47409d 100644 --- a/src/pages/endpoint/MEM/list-templates/edit.jsx +++ b/src/pages/endpoint/MEM/list-templates/edit.jsx @@ -1,11 +1,11 @@ import { Alert, Box } from "@mui/material"; import { useForm } from "react-hook-form"; import { useRouter } from "next/router"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; -import { ApiGetCall } from "/src/api/ApiCall"; -import CippTemplateFieldRenderer from "/src/components/CippComponents/CippTemplateFieldRenderer"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormSkeleton from "../../../../components/CippFormPages/CippFormSkeleton"; +import { ApiGetCall } from "../../../../api/ApiCall"; +import CippTemplateFieldRenderer from "../../../../components/CippComponents/CippTemplateFieldRenderer"; const EditIntuneTemplate = () => { const router = useRouter(); diff --git a/src/pages/endpoint/MEM/list-templates/index.js b/src/pages/endpoint/MEM/list-templates/index.js index c9de8a83b7f6..fcfc8aa81e5a 100644 --- a/src/pages/endpoint/MEM/list-templates/index.js +++ b/src/pages/endpoint/MEM/list-templates/index.js @@ -1,11 +1,11 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { PencilIcon, TrashIcon } from "@heroicons/react/24/outline"; import { Edit, GitHub, LocalOffer, LocalOfferOutlined, CopyAll } from "@mui/icons-material"; import CippJsonView from "../../../../components/CippFormPages/CippJSONView"; -import { ApiGetCall } from "/src/api/ApiCall"; -import { CippPolicyImportDrawer } from "/src/components/CippComponents/CippPolicyImportDrawer.jsx"; -import { PermissionButton } from "/src/utils/permissions.js"; +import { ApiGetCall } from "../../../../api/ApiCall"; +import { CippPolicyImportDrawer } from "../../../../components/CippComponents/CippPolicyImportDrawer.jsx"; +import { PermissionButton } from "../../../../utils/permissions.js"; const Page = () => { const pageTitle = "Available Endpoint Manager Templates"; diff --git a/src/pages/endpoint/applications/list/index.js b/src/pages/endpoint/applications/list/index.js index 6a5073f1ddef..0405962e188b 100644 --- a/src/pages/endpoint/applications/list/index.js +++ b/src/pages/endpoint/applications/list/index.js @@ -1,12 +1,12 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { CippApiDialog } from "/src/components/CippComponents/CippApiDialog.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { CippApiDialog } from "../../../../components/CippComponents/CippApiDialog.jsx"; import { GlobeAltIcon, TrashIcon, UserIcon, UserGroupIcon } from "@heroicons/react/24/outline"; import { LaptopMac, Sync } from "@mui/icons-material"; -import { CippApplicationDeployDrawer } from "/src/components/CippComponents/CippApplicationDeployDrawer"; +import { CippApplicationDeployDrawer } from "../../../../components/CippComponents/CippApplicationDeployDrawer"; import { Button, Box } from "@mui/material"; -import { useSettings } from "/src/hooks/use-settings.js"; -import { useDialog } from "/src/hooks/use-dialog.js"; +import { useSettings } from "../../../../hooks/use-settings.js"; +import { useDialog } from "../../../../hooks/use-dialog.js"; const assignmentIntentOptions = [ { label: "Required", value: "Required" }, diff --git a/src/pages/endpoint/applications/queue/index.js b/src/pages/endpoint/applications/queue/index.js index 08e24e18ad30..57a8337a0289 100644 --- a/src/pages/endpoint/applications/queue/index.js +++ b/src/pages/endpoint/applications/queue/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Button } from "@mui/material"; import { PlayArrow } from "@mui/icons-material"; import { TrashIcon } from "@heroicons/react/24/outline"; diff --git a/src/pages/endpoint/autopilot/add-device/index.js b/src/pages/endpoint/autopilot/add-device/index.js index 7c1f657e3dcd..5758c4a0507d 100644 --- a/src/pages/endpoint/autopilot/add-device/index.js +++ b/src/pages/endpoint/autopilot/add-device/index.js @@ -1,7 +1,7 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippWizardConfirmation } from "/src/components/CippWizard/CippWizardConfirmation"; -import CippWizardPage from "/src/components/CippWizard/CippWizardPage.jsx"; -import { CippTenantStep } from "/src/components/CippWizard/CippTenantStep.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippWizardConfirmation } from "../../../../components/CippWizard/CippWizardConfirmation"; +import CippWizardPage from "../../../../components/CippWizard/CippWizardPage.jsx"; +import { CippTenantStep } from "../../../../components/CippWizard/CippTenantStep.jsx"; import { CippWizardAutopilotImport } from "../../../../components/CippWizard/CippWizardAutopilotImport"; import { CippWizardAutopilotOptions } from "../../../../components/CippWizard/CippWizardAutopilotOptions"; diff --git a/src/pages/endpoint/autopilot/list-devices/index.js b/src/pages/endpoint/autopilot/list-devices/index.js index cd5d6244aa74..3c25271def7c 100644 --- a/src/pages/endpoint/autopilot/list-devices/index.js +++ b/src/pages/endpoint/autopilot/list-devices/index.js @@ -1,6 +1,6 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { CippApiDialog } from "/src/components/CippComponents/CippApiDialog.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { CippApiDialog } from "../../../../components/CippComponents/CippApiDialog.jsx"; import { Button } from "@mui/material"; import { PersonAdd, Delete, Sync, Add, Edit, Sell } from "@mui/icons-material"; import { useDialog } from "../../../../hooks/use-dialog"; diff --git a/src/pages/endpoint/autopilot/list-profiles/index.js b/src/pages/endpoint/autopilot/list-profiles/index.js index 63626a661e54..12f2369f4e05 100644 --- a/src/pages/endpoint/autopilot/list-profiles/index.js +++ b/src/pages/endpoint/autopilot/list-profiles/index.js @@ -1,8 +1,8 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Delete } from "@mui/icons-material"; import CippJsonView from "../../../../components/CippFormPages/CippJSONView"; -import { CippAutopilotProfileDrawer } from "/src/components/CippComponents/CippAutopilotProfileDrawer"; +import { CippAutopilotProfileDrawer } from "../../../../components/CippComponents/CippAutopilotProfileDrawer"; const Page = () => { const pageTitle = "Autopilot Profiles"; diff --git a/src/pages/endpoint/autopilot/list-status-pages/index.js b/src/pages/endpoint/autopilot/list-status-pages/index.js index 6143b8eab136..4ec8a3b93313 100644 --- a/src/pages/endpoint/autopilot/list-status-pages/index.js +++ b/src/pages/endpoint/autopilot/list-status-pages/index.js @@ -1,6 +1,6 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { CippAutopilotStatusPageDrawer } from "/src/components/CippComponents/CippAutopilotStatusPageDrawer"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { CippAutopilotStatusPageDrawer } from "../../../../components/CippComponents/CippAutopilotStatusPageDrawer"; const Page = () => { const pageTitle = "Autopilot Status Pages"; diff --git a/src/pages/endpoint/reports/analyticsdevicescore/index.js b/src/pages/endpoint/reports/analyticsdevicescore/index.js index 5660e0fbf612..73051fe1318b 100644 --- a/src/pages/endpoint/reports/analyticsdevicescore/index.js +++ b/src/pages/endpoint/reports/analyticsdevicescore/index.js @@ -1,7 +1,7 @@ import { EyeIcon } from "@heroicons/react/24/outline"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { useSettings } from "/src/hooks/use-settings"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { useSettings } from "../../../../hooks/use-settings"; const Page = () => { const pageTitle = "Analytics Device Score Report"; diff --git a/src/pages/endpoint/reports/autopilot-deployment/index.js b/src/pages/endpoint/reports/autopilot-deployment/index.js index 94a8c0226c18..49f8b56e4d07 100644 --- a/src/pages/endpoint/reports/autopilot-deployment/index.js +++ b/src/pages/endpoint/reports/autopilot-deployment/index.js @@ -1,7 +1,7 @@ import { EyeIcon, DocumentTextIcon } from "@heroicons/react/24/outline"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { useSettings } from "/src/hooks/use-settings"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { useSettings } from "../../../../hooks/use-settings"; import { CheckCircle, Error, Warning, Refresh } from "@mui/icons-material"; const Page = () => { diff --git a/src/pages/endpoint/reports/detected-apps/index.js b/src/pages/endpoint/reports/detected-apps/index.js index 308d02cf3d79..5f692c12428d 100644 --- a/src/pages/endpoint/reports/detected-apps/index.js +++ b/src/pages/endpoint/reports/detected-apps/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; const Page = () => { const pageTitle = "Discovered Apps"; diff --git a/src/pages/endpoint/reports/workfromanywhere/index.js b/src/pages/endpoint/reports/workfromanywhere/index.js index cd963abfbe72..27fdfbe1e5a7 100644 --- a/src/pages/endpoint/reports/workfromanywhere/index.js +++ b/src/pages/endpoint/reports/workfromanywhere/index.js @@ -1,7 +1,7 @@ import { EyeIcon } from "@heroicons/react/24/outline"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { useSettings } from "/src/hooks/use-settings"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { useSettings } from "../../../../hooks/use-settings"; const Page = () => { const pageTitle = "Work from anywhere Report"; diff --git a/src/pages/identity/administration/deleted-items/index.js b/src/pages/identity/administration/deleted-items/index.js index 66d27c8bfd83..961298efb234 100644 --- a/src/pages/identity/administration/deleted-items/index.js +++ b/src/pages/identity/administration/deleted-items/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { RestoreFromTrash, Warning } from "@mui/icons-material"; const Page = () => { diff --git a/src/pages/identity/administration/deploy-group-template/index.js b/src/pages/identity/administration/deploy-group-template/index.js index d96412b632bf..04313ef9bf2d 100644 --- a/src/pages/identity/administration/deploy-group-template/index.js +++ b/src/pages/identity/administration/deploy-group-template/index.js @@ -1,4 +1,4 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; const Page = () => { const pageTitle = "Deploy Group Template"; diff --git a/src/pages/identity/administration/devices/index.js b/src/pages/identity/administration/devices/index.js index 9d0f56c18643..74791fb678c2 100644 --- a/src/pages/identity/administration/devices/index.js +++ b/src/pages/identity/administration/devices/index.js @@ -1,6 +1,6 @@ -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; // had to add an extra path here because I added an extra folder structure. We should switch to absolute pathing so we dont have to deal with relative. -import { useSettings } from "/src/hooks/use-settings"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; // had to add an extra path here because I added an extra folder structure. We should switch to absolute pathing so we dont have to deal with relative. +import { useSettings } from "../../../../hooks/use-settings"; import { Visibility, CheckCircleOutline, Block, VpnKey, DeleteForever } from "@mui/icons-material"; const Page = () => { diff --git a/src/pages/identity/administration/group-templates/add.jsx b/src/pages/identity/administration/group-templates/add.jsx index fe5b597fe926..4e79cc7900c5 100644 --- a/src/pages/identity/administration/group-templates/add.jsx +++ b/src/pages/identity/administration/group-templates/add.jsx @@ -1,6 +1,6 @@ import { Box } from "@mui/material"; import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm } from "react-hook-form"; import { useSettings } from "../../../../hooks/use-settings"; import CippAddGroupTemplateForm from "../../../../components/CippFormPages/CippAddGroupTemplateForm"; diff --git a/src/pages/identity/administration/group-templates/deploy.js b/src/pages/identity/administration/group-templates/deploy.js index 31791894f361..e27fa27a56ab 100644 --- a/src/pages/identity/administration/group-templates/deploy.js +++ b/src/pages/identity/administration/group-templates/deploy.js @@ -1,7 +1,7 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippWizardConfirmation } from "/src/components/CippWizard/CippWizardConfirmation"; -import CippWizardPage from "/src/components/CippWizard/CippWizardPage.jsx"; -import { CippTenantStep } from "/src/components/CippWizard/CippTenantStep.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippWizardConfirmation } from "../../../../components/CippWizard/CippWizardConfirmation"; +import CippWizardPage from "../../../../components/CippWizard/CippWizardPage.jsx"; +import { CippTenantStep } from "../../../../components/CippWizard/CippTenantStep.jsx"; import { CippWizardGroupTemplates } from "../../../../components/CippWizard/CippWizardGroupTemplates"; const Page = () => { diff --git a/src/pages/identity/administration/group-templates/edit.jsx b/src/pages/identity/administration/group-templates/edit.jsx index 6bcf87af7c0a..5fd7f3417805 100644 --- a/src/pages/identity/administration/group-templates/edit.jsx +++ b/src/pages/identity/administration/group-templates/edit.jsx @@ -1,6 +1,6 @@ import { Box, CircularProgress } from "@mui/material"; import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm } from "react-hook-form"; import { useSettings } from "../../../../hooks/use-settings"; import CippAddGroupTemplateForm from "../../../../components/CippFormPages/CippAddGroupTemplateForm"; diff --git a/src/pages/identity/administration/group-templates/index.js b/src/pages/identity/administration/group-templates/index.js index b7dc1b5aa2a3..3920e4a01d26 100644 --- a/src/pages/identity/administration/group-templates/index.js +++ b/src/pages/identity/administration/group-templates/index.js @@ -1,9 +1,9 @@ import { Button } from "@mui/material"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { AddBox, RocketLaunch, Delete, GitHub, Edit } from "@mui/icons-material"; import Link from "next/link"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../api/ApiCall"; import { CippPropertyListCard } from "../../../../components/CippCards/CippPropertyListCard"; import { getCippTranslation } from "../../../../utils/get-cipp-translation"; import { getCippFormatting } from "../../../../utils/get-cipp-formatting"; diff --git a/src/pages/identity/administration/groups/add.jsx b/src/pages/identity/administration/groups/add.jsx index 3eddb73d6ba0..1fa0ccfc83c4 100644 --- a/src/pages/identity/administration/groups/add.jsx +++ b/src/pages/identity/administration/groups/add.jsx @@ -1,6 +1,6 @@ import { Box } from "@mui/material"; import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm } from "react-hook-form"; import { useSettings } from "../../../../hooks/use-settings"; import { useEffect } from "react"; diff --git a/src/pages/identity/administration/groups/edit.jsx b/src/pages/identity/administration/groups/edit.jsx index bde4e212c864..4409353cb92b 100644 --- a/src/pages/identity/administration/groups/edit.jsx +++ b/src/pages/identity/administration/groups/edit.jsx @@ -2,10 +2,10 @@ import { useEffect, useState } from "react"; import { Box, Button, Divider, Typography, Alert } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormUserSelector } from "/src/components/CippComponents/CippFormUserSelector"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { CippFormUserSelector } from "../../../../components/CippComponents/CippFormUserSelector"; import { useRouter } from "next/router"; import { ApiGetCall } from "../../../../api/ApiCall"; import { useSettings } from "../../../../hooks/use-settings"; @@ -330,6 +330,7 @@ const EditGroup = () => { }, })) || [] } + sortOptions={true} /> @@ -353,6 +354,7 @@ const EditGroup = () => { }, })) || [] } + sortOptions={true} /> @@ -374,6 +376,7 @@ const EditGroup = () => { addedFields: { id: m.id }, })) || [] } + sortOptions={true} /> diff --git a/src/pages/identity/administration/groups/index.js b/src/pages/identity/administration/groups/index.js index af5182224a12..bb5de39a09a2 100644 --- a/src/pages/identity/administration/groups/index.js +++ b/src/pages/identity/administration/groups/index.js @@ -1,6 +1,6 @@ import { Button } from "@mui/material"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import Link from "next/link"; import { TrashIcon } from "@heroicons/react/24/outline"; import { @@ -287,6 +287,11 @@ const Page = () => { "mailEnabled", "securityEnabled", "visibility", + "assignedLicenses", + "licenseProcessingState.state", + "onPremisesSamAccountName", + "membershipRule", + "onPremisesSyncEnabled", ], actions: actions, }; @@ -320,11 +325,11 @@ const Page = () => { "mailNickname", "groupType", "assignedLicenses", + "licenseProcessingState.state", "visibility", "onPremisesSamAccountName", "membershipRule", "onPremisesSyncEnabled", - "userPrincipalName", ]} /> ); diff --git a/src/pages/identity/administration/index.js b/src/pages/identity/administration/index.js index ddd398e434f7..f2222b9e4fab 100644 --- a/src/pages/identity/administration/index.js +++ b/src/pages/identity/administration/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; const Page = () => { const pageTitle = "Administration"; diff --git a/src/pages/identity/administration/jit-admin-templates/add.jsx b/src/pages/identity/administration/jit-admin-templates/add.jsx new file mode 100644 index 000000000000..5b776241428d --- /dev/null +++ b/src/pages/identity/administration/jit-admin-templates/add.jsx @@ -0,0 +1,287 @@ +import { Box, Divider, Typography } from "@mui/material"; +import { Grid } from "@mui/system"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { useForm, useWatch } from "react-hook-form"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { CippFormCondition } from "../../../../components/CippComponents/CippFormCondition"; +import { CippFormDomainSelector } from "../../../../components/CippComponents/CippFormDomainSelector"; +import { CippFormUserSelector } from "../../../../components/CippComponents/CippFormUserSelector"; +import gdaproles from "../../../../data/GDAPRoles.json"; +import { useSettings } from "../../../../hooks/use-settings"; + +const Page = () => { + const userSettingsDefaults = useSettings(); + const formControl = useForm({ + mode: "onChange", + defaultValues: { + tenantFilter: userSettingsDefaults.currentTenant, + }, + }); + + const watchedTenant = useWatch({ control: formControl.control, name: "tenantFilter" }); + const isAllTenants = watchedTenant?.value === "AllTenants" || watchedTenant === "AllTenants"; + + return ( + <> + + + + + Template Information + + + + + + + + + + + Default JIT Admin Settings + + + + ({ label: role.Name, value: role.ObjectId }))} + formControl={formControl} + required={true} + validators={{ + required: "At least one default role is required", + validate: (options) => { + if (!options?.length) { + return "At least one default role is required"; + } + return true; + }, + }} + /> + + + + { + if (!value) return true; // Optional field + const durationValue = typeof value === "object" && value.value ? value.value : value; + const iso8601Regex = /^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/; + if (!iso8601Regex.test(durationValue)) { + return "Invalid format. Use PT1H, P1D, PT2H30M, etc."; + } + return true; + }, + }} + /> + + + + + + + + + + + + + + + + + + + + + User Creation Settings + + + + + {isAllTenants + ? "AllTenants templates can only use 'New User' option (no further options are configurable)" + : "Choose whether this template creates a new user or assigns to existing user"} + + + + + + + + {isAllTenants + ? "Pre-fill user details (optional, for AllTenants templates)" + : "Pre-fill user details (optional, only for specific tenant templates)"} + + + + + + + + + + + + {!isAllTenants && ( + + + + )} + + + + {!isAllTenants && ( + <> + + + Select default user (optional, only for specific tenant templates) + + + + + + + )} + + + + + + ); +}; + +Page.getLayout = (page) => {page}; + +export default Page; diff --git a/src/pages/identity/administration/jit-admin-templates/edit.jsx b/src/pages/identity/administration/jit-admin-templates/edit.jsx new file mode 100644 index 000000000000..468c457d4240 --- /dev/null +++ b/src/pages/identity/administration/jit-admin-templates/edit.jsx @@ -0,0 +1,313 @@ +import { Box, Divider, Typography } from "@mui/material"; +import { Grid } from "@mui/system"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { useForm, useWatch } from "react-hook-form"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { CippFormCondition } from "../../../../components/CippComponents/CippFormCondition"; +import { CippFormDomainSelector } from "../../../../components/CippComponents/CippFormDomainSelector"; +import { CippFormUserSelector } from "../../../../components/CippComponents/CippFormUserSelector"; +import gdaproles from "../../../../data/GDAPRoles.json"; +import { useSettings } from "../../../../hooks/use-settings"; +import { useRouter } from "next/router"; +import { ApiGetCall } from "../../../../api/ApiCall"; +import { useEffect } from "react"; + +const Page = () => { + const userSettingsDefaults = useSettings(); + const router = useRouter(); + const { id } = router.query; + + const formControl = useForm({ + mode: "onChange", + defaultValues: { + tenantFilter: userSettingsDefaults.currentTenant, + }, + }); + + const watchedTenant = useWatch({ control: formControl.control, name: "tenantFilter" }); + const isAllTenants = watchedTenant?.value === "AllTenants" || watchedTenant === "AllTenants"; + + // Get the template data + const template = ApiGetCall({ + url: `/api/ListJITAdminTemplates?GUID=${id}`, + queryKey: `JITAdminTemplate-${id}`, + waiting: !!id, + }); + + // Populate form when template data is loaded + useEffect(() => { + if (template.isSuccess && template.data?.[0]) { + const templateData = template.data[0]; + formControl.reset({ + ...templateData, + GUID: id, + }); + } + }, [template.isSuccess, template.data]); + + return ( + <> + + + + + Template Information + + + + + + + + + + + Default JIT Admin Settings + + + + ({ label: role.Name, value: role.ObjectId }))} + formControl={formControl} + required={true} + validators={{ + required: "At least one default role is required", + validate: (options) => { + if (!options?.length) { + return "At least one default role is required"; + } + return true; + }, + }} + /> + + + + { + if (!value) return true; // Optional field + const durationValue = typeof value === "object" && value.value ? value.value : value; + const iso8601Regex = /^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/; + if (!iso8601Regex.test(durationValue)) { + return "Invalid format. Use PT1H, P1D, PT2H30M, etc."; + } + return true; + }, + }} + /> + + + + + + + + + + + + + + + + + + + + + User Creation Settings + + + + + + + + + + {isAllTenants + ? "Pre-fill user details (optional, for AllTenants templates)" + : "Pre-fill user details (optional, only for specific tenant templates)"} + + + + + + + + + + + + {!isAllTenants && ( + + + + )} + + + + {!isAllTenants && ( + <> + + + Select default user (optional, only for specific tenant templates) + + + + + + + )} + + + + + + + + ); +}; + +Page.getLayout = (page) => {page}; + +export default Page; diff --git a/src/pages/identity/administration/jit-admin-templates/index.js b/src/pages/identity/administration/jit-admin-templates/index.js new file mode 100644 index 000000000000..8f3c4cd85a50 --- /dev/null +++ b/src/pages/identity/administration/jit-admin-templates/index.js @@ -0,0 +1,140 @@ +import { Button } from "@mui/material"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { AddBox, Delete, GitHub, Edit } from "@mui/icons-material"; +import Link from "next/link"; +import { ApiGetCall } from "../../../../api/ApiCall"; +import { CippPropertyListCard } from "../../../../components/CippCards/CippPropertyListCard"; +import { getCippTranslation } from "../../../../utils/get-cipp-translation"; +import { getCippFormatting } from "../../../../utils/get-cipp-formatting"; +import { useSettings } from "../../../../hooks/use-settings"; + +const Page = () => { + const pageTitle = "JIT Admin Templates"; + const tenantFilter = useSettings()?.currentTenant; + const integrations = ApiGetCall({ + url: "/api/ListExtensionsConfig", + queryKey: "Integrations", + refetchOnMount: false, + refetchOnReconnect: false, + }); + const actions = [ + { + label: "Edit Template", + icon: , + link: "/identity/administration/jit-admin-templates/edit?id=[GUID]", + }, + { + label: "Save to GitHub", + type: "POST", + url: "/api/ExecCommunityRepo", + icon: , + data: { + Action: "UploadTemplate", + GUID: "GUID", + }, + fields: [ + { + label: "Repository", + name: "FullName", + type: "select", + api: { + url: "/api/ListCommunityRepos", + data: { + WriteAccess: true, + }, + queryKey: "CommunityRepos-Write", + dataKey: "Results", + valueField: "FullName", + labelField: "FullName", + }, + multiple: false, + creatable: false, + required: true, + validators: { + required: { value: true, message: "This field is required" }, + }, + }, + { + label: "Commit Message", + placeholder: "Enter a commit message for adding this file to GitHub", + name: "Message", + type: "textField", + multiline: true, + required: true, + rows: 4, + }, + ], + confirmText: "Are you sure you want to save this template to the selected repository?", + condition: () => integrations.isSuccess && integrations?.data?.GitHub?.Enabled, + }, + { + label: "Delete Template", + type: "POST", + url: "/api/RemoveJITAdminTemplate", + icon: , + data: { + ID: "GUID", + }, + confirmText: "Do you want to delete the template?", + multiPost: false, + }, + ]; + + const offCanvas = { + children: (data) => { + const keys = Object.keys(data).filter( + (key) => !key.includes("@odata") && !key.includes("@data") + ); + const properties = []; + keys.forEach((key) => { + if (data[key] && data[key].length > 0) { + properties.push({ + label: getCippTranslation(key), + value: getCippFormatting(data[key], key), + }); + } + }); + return ( + + ); + }, + }; + + return ( + }> + Add JIT Admin Template + + } + offCanvas={offCanvas} + simpleColumns={[ + "templateName", + "defaultForTenant", + "tenantFilter", + "defaultDuration.label", + "defaultRoles", + "generateTAPByDefault", + "defaultExpireAction.label", + "defaultNotificationActions", + "reasonTemplate" + ]} + /> + ); +}; + +Page.getLayout = (page) => {page}; + +export default Page; diff --git a/src/pages/identity/administration/jit-admin/add.jsx b/src/pages/identity/administration/jit-admin/add.jsx index 48e9bf2255a0..9deef7e51b1b 100644 --- a/src/pages/identity/administration/jit-admin/add.jsx +++ b/src/pages/identity/administration/jit-admin/add.jsx @@ -1,16 +1,192 @@ import { Box, Divider } from "@mui/material"; import { Grid } from "@mui/system"; import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { CippFormTenantSelector } from "../../../../components/CippComponents/CippFormTenantSelector"; -import { useForm } from "react-hook-form"; +import { useForm, useWatch } from "react-hook-form"; import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; import { CippFormCondition } from "../../../../components/CippComponents/CippFormCondition"; -import gdaproles from "/src/data/GDAPRoles.json"; +import gdaproles from "../../../../data/GDAPRoles.json"; import { CippFormDomainSelector } from "../../../../components/CippComponents/CippFormDomainSelector"; import { CippFormUserSelector } from "../../../../components/CippComponents/CippFormUserSelector"; +import { ApiGetCall } from "../../../../api/ApiCall"; +import { useEffect, useState } from "react"; + const Page = () => { const formControl = useForm({ mode: "onChange" }); + const selectedTenant = useWatch({ control: formControl.control, name: "tenantFilter" }); + const [selectedTemplate, setSelectedTemplate] = useState(null); + + const jitAdminTemplates = ApiGetCall({ + url: selectedTenant + ? `/api/ListJITAdminTemplates?TenantFilter=${selectedTenant.value}` + : undefined, + queryKey: selectedTenant ? `JITAdminTemplates-${selectedTenant.value}` : "JITAdminTemplates", + refetchOnMount: false, + refetchOnReconnect: false, + waiting: !!selectedTenant, + }); + + const watcher = useWatch({ control: formControl.control }); + + // Simple duration parser for basic ISO 8601 durations + const parseDuration = (duration) => { + if (!duration) return null; + const matches = duration.match( + /P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?/ + ); + if (!matches) return null; + return { + years: parseInt(matches[1] || 0), + months: parseInt(matches[2] || 0), + weeks: parseInt(matches[3] || 0), + days: parseInt(matches[4] || 0), + hours: parseInt(matches[5] || 0), + minutes: parseInt(matches[6] || 0), + seconds: parseInt(matches[7] || 0), + }; + }; + + const addDurationToDate = (date, duration) => { + if (!date || !duration) return null; + const parsed = parseDuration(duration); + if (!parsed) return null; + + const result = new Date(date); + result.setFullYear(result.getFullYear() + parsed.years); + result.setMonth(result.getMonth() + parsed.months); + result.setDate(result.getDate() + parsed.weeks * 7); + result.setDate(result.getDate() + parsed.days); + result.setHours(result.getHours() + parsed.hours); + result.setMinutes(result.getMinutes() + parsed.minutes); + result.setSeconds(result.getSeconds() + parsed.seconds); + return result; + }; + + // Auto-select default template for tenant + // Priority: tenant-specific default > AllTenants default + useEffect(() => { + if (jitAdminTemplates.isSuccess && !watcher.jitAdminTemplate) { + const templates = jitAdminTemplates.data || []; + + // First, try to find a tenant-specific default template + let defaultTemplate = templates.find( + (template) => + template.defaultForTenant === true && + template.tenantFilter !== "AllTenants" && + template.tenantFilter === selectedTenant?.value + ); + + // If not found, fall back to AllTenants default template + if (!defaultTemplate) { + defaultTemplate = templates.find( + (template) => template.defaultForTenant === true && template.tenantFilter === "AllTenants" + ); + } + + if (defaultTemplate) { + formControl.setValue("jitAdminTemplate", { + label: defaultTemplate.templateName, + value: defaultTemplate.GUID, + addedFields: defaultTemplate, + }); + setSelectedTemplate(defaultTemplate); + } + } + }, [jitAdminTemplates.isSuccess, selectedTenant]); + + // Only set template-driven fields when the template actually changes + const [lastTemplate, setLastTemplate] = useState(null); + useEffect(() => { + const template = watcher.jitAdminTemplate?.addedFields; + if (!template || template.GUID === lastTemplate) return; + setSelectedTemplate(template); + setLastTemplate(template.GUID); + + // Helpers + const roundDown15 = (date) => { + const d = new Date(date); + d.setMilliseconds(0); + d.setSeconds(0); + d.setMinutes(Math.floor(d.getMinutes() / 15) * 15); + return d; + }; + const roundUp15 = (date) => { + const d = new Date(date); + d.setMilliseconds(0); + d.setSeconds(0); + let min = d.getMinutes(); + d.setMinutes(min % 15 === 0 ? min : Math.ceil(min / 15) * 15); + if (d.getMinutes() === 60) { + d.setHours(d.getHours() + 1); + d.setMinutes(0); + } + return d; + }; + + // Set all template-driven fields + formControl.setValue("adminRoles", template.defaultRoles || [], { shouldDirty: true }); + formControl.setValue("expireAction", template.defaultExpireAction || null, { + shouldDirty: true, + }); + formControl.setValue("postExecution", template.defaultNotificationActions || [], { + shouldDirty: true, + }); + formControl.setValue("UseTAP", template.generateTAPByDefault ?? false, { shouldDirty: true }); + formControl.setValue("reason", template.reasonTemplate || "", { shouldDirty: true }); + + // User action and user details + if (template.defaultUserAction) { + formControl.setValue("userAction", template.defaultUserAction, { shouldDirty: true }); + } + if (template.defaultFirstName) { + formControl.setValue("firstName", template.defaultFirstName, { shouldDirty: true }); + } + if (template.defaultLastName) { + formControl.setValue("lastName", template.defaultLastName, { shouldDirty: true }); + } + if (template.defaultUserName) { + formControl.setValue("userName", template.defaultUserName, { shouldDirty: true }); + } + if (template.defaultDomain) { + formControl.setValue("domain", template.defaultDomain, { shouldDirty: true }); + } + if (template.defaultExistingUser) { + formControl.setValue("existingUser", template.defaultExistingUser, { shouldDirty: true }); + } + + // Dates + if (template.defaultDuration) { + const duration = + typeof template.defaultDuration === "object" && template.defaultDuration !== null + ? template.defaultDuration.value + : template.defaultDuration; + const start = roundDown15(new Date()); + const unixStart = Math.floor(start.getTime() / 1000); + formControl.setValue("startDate", unixStart, { shouldDirty: true }); + const end = roundUp15(addDurationToDate(start, duration)); + const unixEnd = Math.floor(end.getTime() / 1000); + formControl.setValue("endDate", unixEnd, { shouldDirty: true }); + } + }, [watcher.jitAdminTemplate, lastTemplate]); + + // Recalculate end date when start date changes and template has default duration + useEffect(() => { + if (watcher.startDate && selectedTemplate?.defaultDuration) { + const durationValue = + typeof selectedTemplate.defaultDuration === "object" && + selectedTemplate.defaultDuration !== null + ? selectedTemplate.defaultDuration.value + : selectedTemplate.defaultDuration; + const startDateDate = new Date(watcher.startDate * 1000); + const endDateObj = addDurationToDate(startDateDate, durationValue); + if (endDateObj) { + const unixEnd = Math.floor(endDateObj.getTime() / 1000); + formControl.setValue("endDate", unixEnd); + } + } + }, [watcher.startDate]); + return ( <> { validators={{ required: "A tenant must be selected" }} /> + + ({ + label: template.templateName, + value: template.GUID, + addedFields: template, + })) + : [] + } + formControl={formControl} + /> + { label="Expiration Action" name="expireAction" multiple={false} + creatable={false} required={true} options={[ { label: "Delete User", value: "DeleteUser" }, @@ -216,6 +412,7 @@ const Page = () => { label="Notification Action" name="postExecution" multiple={true} + creatable={false} options={[ { label: "Webhook", value: "Webhook" }, { label: "Email", value: "email" }, diff --git a/src/pages/identity/administration/jit-admin/index.js b/src/pages/identity/administration/jit-admin/index.js index 46fd36efb4c9..b92faa05decb 100644 --- a/src/pages/identity/administration/jit-admin/index.js +++ b/src/pages/identity/administration/jit-admin/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippTablePage from "/src/components/CippComponents/CippTablePage"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippTablePage from "../../../../components/CippComponents/CippTablePage"; import { Button } from "@mui/material"; import { AdminPanelSettings } from "@mui/icons-material"; import Link from "next/link"; diff --git a/src/pages/identity/administration/offboarding-wizard/index.js b/src/pages/identity/administration/offboarding-wizard/index.js index bb99dc1e6ada..931cfded2eef 100644 --- a/src/pages/identity/administration/offboarding-wizard/index.js +++ b/src/pages/identity/administration/offboarding-wizard/index.js @@ -1,7 +1,7 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippWizardConfirmation } from "/src/components/CippWizard/CippWizardConfirmation"; -import CippWizardPage from "/src/components/CippWizard/CippWizardPage.jsx"; -import { CippTenantStep } from "/src/components/CippWizard/CippTenantStep.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippWizardConfirmation } from "../../../../components/CippWizard/CippWizardConfirmation"; +import CippWizardPage from "../../../../components/CippWizard/CippWizardPage.jsx"; +import { CippTenantStep } from "../../../../components/CippWizard/CippTenantStep.jsx"; import { CippWizardAutoComplete } from "../../../../components/CippWizard/CippWizardAutoComplete"; import { CippWizardOffboarding } from "../../../../components/CippWizard/CippWizardOffboarding"; import { useSettings } from "../../../../hooks/use-settings"; diff --git a/src/pages/identity/administration/risky-users/index.js b/src/pages/identity/administration/risky-users/index.js index 9fe9b7004690..d3ff4080d112 100644 --- a/src/pages/identity/administration/risky-users/index.js +++ b/src/pages/identity/administration/risky-users/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Clear, Search } from "@mui/icons-material"; const Page = () => { diff --git a/src/pages/identity/administration/roles/index.js b/src/pages/identity/administration/roles/index.js index 4229e12eb622..f09fb8a01388 100644 --- a/src/pages/identity/administration/roles/index.js +++ b/src/pages/identity/administration/roles/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; const Page = () => { const pageTitle = "Roles"; diff --git a/src/pages/identity/administration/users/add.jsx b/src/pages/identity/administration/users/add.jsx index ce2b545c7032..eabb890fa965 100644 --- a/src/pages/identity/administration/users/add.jsx +++ b/src/pages/identity/administration/users/add.jsx @@ -1,8 +1,8 @@ import { Box } from "@mui/material"; import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useWatch } from "react-hook-form"; -import { CippFormUserSelector } from "/src/components/CippComponents/CippFormUserSelector"; +import { CippFormUserSelector } from "../../../../components/CippComponents/CippFormUserSelector"; import { useSettings } from "../../../../hooks/use-settings"; import { useEffect } from "react"; diff --git a/src/pages/identity/administration/users/bulk-add.js b/src/pages/identity/administration/users/bulk-add.js index af435fb04982..215982897f48 100644 --- a/src/pages/identity/administration/users/bulk-add.js +++ b/src/pages/identity/administration/users/bulk-add.js @@ -1,7 +1,7 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippWizardConfirmation } from "/src/components/CippWizard/CippWizardConfirmation"; -import CippWizardPage from "/src/components/CippWizard/CippWizardPage.jsx"; -import { CippTenantStep } from "/src/components/CippWizard/CippTenantStep.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippWizardConfirmation } from "../../../../components/CippWizard/CippWizardConfirmation"; +import CippWizardPage from "../../../../components/CippWizard/CippWizardPage.jsx"; +import { CippTenantStep } from "../../../../components/CippWizard/CippTenantStep.jsx"; import { useSettings } from "../../../../hooks/use-settings"; import { CippWizardCSVImport } from "../../../../components/CippWizard/CippWizardCSVImport"; import { CippWizardBulkOptions } from "../../../../components/CippWizard/CippWizardBulkOptions"; diff --git a/src/pages/identity/administration/users/index.js b/src/pages/identity/administration/users/index.js index 27148701979b..41de8a086bd5 100644 --- a/src/pages/identity/administration/users/index.js +++ b/src/pages/identity/administration/users/index.js @@ -1,12 +1,12 @@ -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { useSettings } from "/src/hooks/use-settings.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { useSettings } from "../../../../hooks/use-settings.js"; import { PermissionButton } from "../../../../utils/permissions"; -import { useCippUserActions } from "/src/components/CippComponents/CippUserActions.jsx"; -import { CippInviteGuestDrawer } from "/src/components/CippComponents/CippInviteGuestDrawer.jsx"; -import { CippBulkUserDrawer } from "/src/components/CippComponents/CippBulkUserDrawer.jsx"; -import { CippAddUserDrawer } from "/src/components/CippComponents/CippAddUserDrawer.jsx"; -import { CippApiLogsDrawer } from "/src/components/CippComponents/CippApiLogsDrawer.jsx"; +import { useCippUserActions } from "../../../../components/CippComponents/CippUserActions.jsx"; +import { CippInviteGuestDrawer } from "../../../../components/CippComponents/CippInviteGuestDrawer.jsx"; +import { CippBulkUserDrawer } from "../../../../components/CippComponents/CippBulkUserDrawer.jsx"; +import { CippAddUserDrawer } from "../../../../components/CippComponents/CippAddUserDrawer.jsx"; +import { CippApiLogsDrawer } from "../../../../components/CippComponents/CippApiLogsDrawer.jsx"; import { Box } from "@mui/material"; const Page = () => { @@ -50,6 +50,7 @@ const Page = () => { "onPremisesLastSyncDateTime", // OnPrem Last Sync "onPremisesDistinguishedName", // OnPrem DN "otherMails", // Alternate Email Addresses + "licenseAssignmentStates", // License Assignment States ], actions: userActions, }; @@ -85,7 +86,7 @@ const Page = () => { Endpoint: "users", manualPagination: true, $select: - "id,accountEnabled,businessPhones,city,createdDateTime,companyName,country,department,displayName,faxNumber,givenName,isResourceAccount,jobTitle,mail,mailNickname,mobilePhone,officeLocation,otherMails,postalCode,preferredDataLocation,preferredLanguage,proxyAddresses,showInAddressList,state,streetAddress,surname,usageLocation,userPrincipalName,userType,assignedLicenses,onPremisesSyncEnabled,OnPremisesImmutableId,onPremisesLastSyncDateTime,onPremisesDistinguishedName", + "id,accountEnabled,businessPhones,city,createdDateTime,companyName,country,department,displayName,faxNumber,givenName,isResourceAccount,jobTitle,mail,mailNickname,mobilePhone,officeLocation,otherMails,postalCode,preferredDataLocation,preferredLanguage,proxyAddresses,showInAddressList,state,streetAddress,surname,usageLocation,userPrincipalName,userType,assignedLicenses,licenseAssignmentStates,onPremisesSyncEnabled,OnPremisesImmutableId,onPremisesLastSyncDateTime,onPremisesDistinguishedName", $count: true, $orderby: "displayName", $top: 999, @@ -101,6 +102,7 @@ const Page = () => { "businessPhones", "proxyAddresses", "assignedLicenses", + "licenseAssignmentStates", ]} filters={filters} /> diff --git a/src/pages/identity/administration/users/patch-wizard.jsx b/src/pages/identity/administration/users/patch-wizard.jsx index 1fece737f175..b990988398de 100644 --- a/src/pages/identity/administration/users/patch-wizard.jsx +++ b/src/pages/identity/administration/users/patch-wizard.jsx @@ -1,7 +1,7 @@ import { useState, useEffect, useMemo } from "react"; import { useRouter } from "next/router"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippWizardPage from "/src/components/CippWizard/CippWizardPage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippWizardPage from "../../../../components/CippWizard/CippWizardPage.jsx"; import { Stack, Typography, @@ -16,10 +16,10 @@ import { FormControlLabel, Autocomplete, } from "@mui/material"; -import { CippWizardStepButtons } from "/src/components/CippWizard/CippWizardStepButtons"; -import { ApiPostCall, ApiGetCall } from "/src/api/ApiCall"; -import { CippApiResults } from "/src/components/CippComponents/CippApiResults"; -import { CippDataTable } from "/src/components/CippTable/CippDataTable"; +import { CippWizardStepButtons } from "../../../../components/CippWizard/CippWizardStepButtons"; +import { ApiPostCall, ApiGetCall } from "../../../../api/ApiCall"; +import { CippApiResults } from "../../../../components/CippComponents/CippApiResults"; +import { CippDataTable } from "../../../../components/CippTable/CippDataTable"; import { Delete } from "@mui/icons-material"; // User properties that can be patched diff --git a/src/pages/identity/administration/users/user/bec.jsx b/src/pages/identity/administration/users/user/bec.jsx index 7ff997b562cd..6d305994513d 100644 --- a/src/pages/identity/administration/users/user/bec.jsx +++ b/src/pages/identity/administration/users/user/bec.jsx @@ -1,8 +1,8 @@ import { useEffect, useState } from "react"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { useSettings } from "/src/hooks/use-settings"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; +import { useSettings } from "../../../../../hooks/use-settings"; import { useRouter } from "next/router"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../../api/ApiCall"; import CalendarIcon from "@heroicons/react/24/outline/CalendarIcon"; import { CheckCircle, Download, Mail, Fingerprint, Launch } from "@mui/icons-material"; import { HeaderedTabbedLayout } from "../../../../../layouts/HeaderedTabbedLayout"; diff --git a/src/pages/identity/administration/users/user/conditional-access.jsx b/src/pages/identity/administration/users/user/conditional-access.jsx index a22232248742..ea58b93207dc 100644 --- a/src/pages/identity/administration/users/user/conditional-access.jsx +++ b/src/pages/identity/administration/users/user/conditional-access.jsx @@ -1,8 +1,8 @@ import { useState } from "react"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { useSettings } from "/src/hooks/use-settings"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; +import { useSettings } from "../../../../../hooks/use-settings"; import { useRouter } from "next/router"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; +import CippFormSkeleton from "../../../../../components/CippFormPages/CippFormSkeleton"; import CalendarIcon from "@heroicons/react/24/outline/CalendarIcon"; import { Mail, Fingerprint, Launch } from "@mui/icons-material"; import { HeaderedTabbedLayout } from "../../../../../layouts/HeaderedTabbedLayout"; @@ -11,9 +11,9 @@ import ReactTimeAgo from "react-time-ago"; import { CippCopyToClipBoard } from "../../../../../components/CippComponents/CippCopyToClipboard"; import { Box, Stack, Typography, Button } from "@mui/material"; import { Grid } from "@mui/system"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import countryList from "/src/data/countryList"; -import { CippDataTable } from "/src/components/CippTable/CippDataTable"; +import CippFormComponent from "../../../../../components/CippComponents/CippFormComponent"; +import countryList from "../../../../../data/countryList"; +import { CippDataTable } from "../../../../../components/CippTable/CippDataTable"; import { useForm } from "react-hook-form"; import CippButtonCard from "../../../../../components/CippCards/CippButtonCard"; import { ApiGetCall, ApiPostCall } from "../../../../../api/ApiCall"; diff --git a/src/pages/identity/administration/users/user/devices.jsx b/src/pages/identity/administration/users/user/devices.jsx index 2b0f4823415c..9fcb9086bdbc 100644 --- a/src/pages/identity/administration/users/user/devices.jsx +++ b/src/pages/identity/administration/users/user/devices.jsx @@ -1,8 +1,8 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { useSettings } from "/src/hooks/use-settings"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; +import { useSettings } from "../../../../../hooks/use-settings"; import { useRouter } from "next/router"; -import { ApiGetCall } from "/src/api/ApiCall"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; +import { ApiGetCall } from "../../../../../api/ApiCall"; +import CippFormSkeleton from "../../../../../components/CippFormPages/CippFormSkeleton"; import CalendarIcon from "@heroicons/react/24/outline/CalendarIcon"; import { Check, Mail, Fingerprint } from "@mui/icons-material"; import { HeaderedTabbedLayout } from "../../../../../layouts/HeaderedTabbedLayout"; diff --git a/src/pages/identity/administration/users/user/edit.jsx b/src/pages/identity/administration/users/user/edit.jsx index d7a559e57922..be61990e7e29 100644 --- a/src/pages/identity/administration/users/user/edit.jsx +++ b/src/pages/identity/administration/users/user/edit.jsx @@ -1,13 +1,13 @@ -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "../../../../../components/CippFormPages/CippFormPage"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; import { useForm } from "react-hook-form"; -import { useSettings } from "/src/hooks/use-settings"; -import CippAddEditUser from "/src/components/CippFormPages/CippAddEditUser"; +import { useSettings } from "../../../../../hooks/use-settings"; +import CippAddEditUser from "../../../../../components/CippFormPages/CippAddEditUser"; import { useRouter } from "next/router"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../../api/ApiCall"; import { useState, useEffect } from "react"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; -import { getCippLicenseTranslation } from "/src/utils/get-cipp-license-translation"; +import CippFormSkeleton from "../../../../../components/CippFormPages/CippFormSkeleton"; +import { getCippLicenseTranslation } from "../../../../../utils/get-cipp-license-translation"; import CalendarIcon from "@heroicons/react/24/outline/CalendarIcon"; import { Mail, Fingerprint, Launch } from "@mui/icons-material"; import { HeaderedTabbedLayout } from "../../../../../layouts/HeaderedTabbedLayout"; diff --git a/src/pages/identity/administration/users/user/exchange.jsx b/src/pages/identity/administration/users/user/exchange.jsx index 61013199368c..f91a6831426d 100644 --- a/src/pages/identity/administration/users/user/exchange.jsx +++ b/src/pages/identity/administration/users/user/exchange.jsx @@ -1,8 +1,8 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { useSettings } from "/src/hooks/use-settings"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; +import { useSettings } from "../../../../../hooks/use-settings"; import { useRouter } from "next/router"; -import { ApiGetCall } from "/src/api/ApiCall"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; +import { ApiGetCall } from "../../../../../api/ApiCall"; +import CippFormSkeleton from "../../../../../components/CippFormPages/CippFormSkeleton"; import CalendarIcon from "@heroicons/react/24/outline/CalendarIcon"; import { Check, @@ -107,6 +107,12 @@ const Page = () => { waiting: waiting, }); + const junkEmailConfigRequest = ApiGetCall({ + url: `/api/ListUserTrustedBlockedSenders?UserId=${userId}&userPrincipalName=${graphUserRequest.data?.[0]?.userPrincipalName}&tenantFilter=${userSettingsDefaults.currentTenant}`, + queryKey: `TrustedBlockedSenders-${userId}`, + waiting: waiting && !!graphUserRequest.data?.[0]?.userPrincipalName, + }); + const groupsList = ApiGetCall({ url: "/api/ListGraphRequest", data: { @@ -157,7 +163,9 @@ const Page = () => { // Exact match on display name (group.displayName && group.displayName === userIdentifier) || // Partial match - permission identifier starts with group display name (handles timestamps) - (group.displayName && userIdentifier.startsWith(group.displayName)) + (group.displayName && + typeof userIdentifier === "string" && + userIdentifier.startsWith(group.displayName)) ); }); @@ -307,11 +315,11 @@ const Page = () => { }); formControl.setValue( "ooo.StartTime", - new Date(oooRequest.data?.StartTime).getTime() / 1000 || null + new Date(oooRequest.data?.StartTime).getTime() / 1000 || null, ); formControl.setValue( "ooo.EndTime", - new Date(oooRequest.data?.EndTime).getTime() / 1000 || null + new Date(oooRequest.data?.EndTime).getTime() / 1000 || null, ); } }, [oooRequest.isSuccess, oooRequest.data]); @@ -1015,7 +1023,7 @@ const Page = () => { offCanvas: { children: (data) => { const keys = Object.keys(data).filter( - (key) => !key.includes("@odata") && !key.includes("@data") + (key) => !key.includes("@odata") && !key.includes("@data"), ); const properties = []; keys.forEach((key) => { @@ -1086,6 +1094,81 @@ const Page = () => { }, ]; + const junkEmailConfigActions = [ + { + label: "Remove Entry", + type: "POST", + icon: , + url: "/api/RemoveTrustedBlockedSender", + customDataformatter: (row, action, formData) => { + return { + userPrincipalName: row?.UserPrincipalName, + typeProperty: row?.TypeProperty, + value: row?.Value, + tenantFilter: userSettingsDefaults.currentTenant, + }; + }, + confirmText: + "Are you sure you want to remove [Value] from the [Type] list for [UserPrincipalName]?", + multiPost: false, + relatedQueryKeys: `JunkEmailConfig-${userId}`, + }, + ]; + + const junkEmailConfigCard = [ + { + id: 1, + cardLabelBox: { + cardLabelBoxHeader: junkEmailConfigRequest.isFetching ? ( + + ) : junkEmailConfigRequest.data?.length !== 0 ? ( + + ) : ( + + ), + }, + text: "Trusted and Blocked Senders/Domains", + subtext: junkEmailConfigRequest.data?.length + ? "Trusted/Blocked senders and domains are configured for this user" + : "No trusted or blocked senders/domains entries for this user", + statusColor: "green.main", + table: { + title: "Trusted and Blocked Senders/Domains", + hideTitle: true, + data: junkEmailConfigRequest.data || [], + refreshFunction: () => junkEmailConfigRequest.refetch(), + isFetching: junkEmailConfigRequest.isFetching, + simpleColumns: ["Type", "Value"], + actions: junkEmailConfigActions, + offCanvas: { + children: (data) => { + return ( + + ); + }, + }, + }, + }, + ]; + const proxyAddressActions = [ { label: "Make Primary", @@ -1154,7 +1237,7 @@ const Page = () => { data: graphUserRequest.data?.[0]?.proxyAddresses?.map((address) => ({ Address: address, - Type: address?.startsWith("SMTP:") ? "Primary" : "Alias", + Type: typeof address === "string" && address.startsWith("SMTP:") ? "Primary" : "Alias", })) || [], refreshFunction: () => graphUserRequest.refetch(), isFetching: graphUserRequest.isFetching, @@ -1210,7 +1293,7 @@ const Page = () => { {userRequest?.data?.[0]?.Mailbox?.[0]?.error.includes( - "Microsoft.Exchange.Configuration.Tasks.ManagementObjectNotFoundException" + "Microsoft.Exchange.Configuration.Tasks.ManagementObjectNotFoundException", ) ? "This user does not have a mailbox, make sure they are licensed for Exchange." : "An error occurred while fetching the mailbox details."} @@ -1230,7 +1313,7 @@ const Page = () => { )} {!userRequest?.data?.[0]?.Mailbox?.[0]?.error?.includes( - "Microsoft.Exchange.Configuration.Tasks.ManagementObjectNotFoundException" + "Microsoft.Exchange.Configuration.Tasks.ManagementObjectNotFoundException", ) && ( <> @@ -1268,6 +1351,11 @@ const Page = () => { items={mailboxRulesCard} isCollapsible={true} /> + import("/src/components/CippComponents/CippMap"), { ssr: false }); +const CippMap = dynamic(() => import("../../../../../components/CippComponents/CippMap"), { + ssr: false, +}); import { Button, Dialog, DialogTitle, DialogContent, IconButton } from "@mui/material"; import { Close } from "@mui/icons-material"; @@ -275,7 +277,7 @@ const Page = () => { ) { // Filter policies where result is "success" const appliedPolicies = signInData.appliedConditionalAccessPolicies.filter( - (policy) => policy.result === "success" + (policy) => policy.result === "success", ); if (appliedPolicies.length > 0) { @@ -420,7 +422,7 @@ const Page = () => { if (mfaDevices.length > 0) { // Exclude password authentication method const mfaDevicesFiltered = mfaDevices.filter( - (method) => method["@odata.type"] !== "#microsoft.graph.passwordAuthenticationMethod" + (method) => method["@odata.type"] !== "#microsoft.graph.passwordAuthenticationMethod", ); if (mfaDevicesFiltered.length > 0) { @@ -538,7 +540,7 @@ const Page = () => { }, ], data: userMemberOf?.filter( - (item) => item?.["@odata.type"] === "#microsoft.graph.group" + (item) => item?.["@odata.type"] === "#microsoft.graph.group", ), refreshFunction: refreshFunction, simpleColumns: ["displayName", "groupTypes", "securityEnabled", "mailEnabled"], @@ -558,7 +560,7 @@ const Page = () => { subtext: "List of roles the user is a member of", statusText: ` ${ userMemberOf?.filter( - (item) => item?.["@odata.type"] === "#microsoft.graph.directoryRole" + (item) => item?.["@odata.type"] === "#microsoft.graph.directoryRole", ).length } Role(s)`, statusColor: "info.main", @@ -566,7 +568,7 @@ const Page = () => { title: "Admin Roles", hideTitle: true, data: userMemberOf?.filter( - (item) => item?.["@odata.type"] === "#microsoft.graph.directoryRole" + (item) => item?.["@odata.type"] === "#microsoft.graph.directoryRole", ), simpleColumns: ["displayName", "description"], refreshFunction: refreshFunction, diff --git a/src/pages/identity/reports/azure-ad-connect-report/index.js b/src/pages/identity/reports/azure-ad-connect-report/index.js index 97594a13d6ac..5a75dd7a60d9 100644 --- a/src/pages/identity/reports/azure-ad-connect-report/index.js +++ b/src/pages/identity/reports/azure-ad-connect-report/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; const simpleColumns = [ "displayName", diff --git a/src/pages/identity/reports/inactive-users-report/index.js b/src/pages/identity/reports/inactive-users-report/index.js index b3bd18ea373a..9c6573ac1154 100644 --- a/src/pages/identity/reports/inactive-users-report/index.js +++ b/src/pages/identity/reports/inactive-users-report/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { EyeIcon, TrashIcon } from "@heroicons/react/24/outline"; import { Edit, Block } from "@mui/icons-material"; diff --git a/src/pages/identity/reports/index.js b/src/pages/identity/reports/index.js index 095383995762..cb5872a4486b 100644 --- a/src/pages/identity/reports/index.js +++ b/src/pages/identity/reports/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; const Page = () => { const pageTitle = "Reports"; diff --git a/src/pages/identity/reports/mfa-report/index.js b/src/pages/identity/reports/mfa-report/index.js index 815e2f5a0d3c..27e1dc025156 100644 --- a/src/pages/identity/reports/mfa-report/index.js +++ b/src/pages/identity/reports/mfa-report/index.js @@ -1,22 +1,54 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { LockPerson } from "@mui/icons-material"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { LockPerson, Sync, Info } from "@mui/icons-material"; +import { Button, Alert, SvgIcon, IconButton, Tooltip } from "@mui/material"; +import { useSettings } from "../../../../hooks/use-settings"; +import { Stack } from "@mui/system"; +import { useDialog } from "../../../../hooks/use-dialog"; +import { CippApiDialog } from "../../../../components/CippComponents/CippApiDialog"; +import { useRouter } from "next/router"; +import { useMemo } from "react"; const Page = () => { const pageTitle = "MFA Report"; const apiUrl = "/api/ListMFAUsers"; - const simpleColumns = [ - "UPN", - "AccountEnabled", - "isLicensed", - "MFARegistration", - "PerUser", - "CoveredBySD", - "CoveredByCA", - "MFAMethods", - "CAPolicies", - "IsAdmin", - ]; + const currentTenant = useSettings().currentTenant; + const syncDialog = useDialog(); + const router = useRouter(); + + const isAllTenants = currentTenant === "AllTenants"; + + const apiData = { + UseReportDB: true, + }; + const simpleColumns = isAllTenants + ? [ + "Tenant", + "UPN", + "AccountEnabled", + "isLicensed", + "MFARegistration", + "PerUser", + "CoveredBySD", + "CoveredByCA", + "MFAMethods", + "CAPolicies", + "IsAdmin", + "CacheTimestamp", + ] + : [ + "UPN", + "AccountEnabled", + "isLicensed", + "MFARegistration", + "PerUser", + "CoveredBySD", + "CoveredByCA", + "MFAMethods", + "CAPolicies", + "IsAdmin", + "CacheTimestamp", + ]; const filters = [ { filterName: "Enabled, licensed users", @@ -48,10 +80,23 @@ const Page = () => { { filterName: "Admin Users", value: [{ id: "IsAdmin", value: "Yes" }], - type: "column" - } + type: "column", + }, ]; + // Parse filters from URL query parameters + const urlFilters = useMemo(() => { + if (router.query.filters) { + try { + return JSON.parse(router.query.filters); + } catch (e) { + console.error("Failed to parse filters from URL:", e); + return null; + } + } + return null; + }, [router.query.filters]); + const actions = [ { label: "Set Per-User MFA", @@ -78,14 +123,54 @@ const Page = () => { }, ]; + const pageActions = [ + + + + + + + + , + ]; + return ( - + <> + + + ); }; diff --git a/src/pages/identity/reports/risk-detections/index.js b/src/pages/identity/reports/risk-detections/index.js index 3c54fc260e31..ef62600025d3 100644 --- a/src/pages/identity/reports/risk-detections/index.js +++ b/src/pages/identity/reports/risk-detections/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { MagnifyingGlassIcon } from "@heroicons/react/24/outline"; const Page = () => { diff --git a/src/pages/identity/reports/signin-report/index.js b/src/pages/identity/reports/signin-report/index.js index 38aada551af9..9f55cf2d2376 100644 --- a/src/pages/identity/reports/signin-report/index.js +++ b/src/pages/identity/reports/signin-report/index.js @@ -1,9 +1,9 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { useState } from "react"; import { Button, TextField, Switch, FormControlLabel } from "@mui/material"; import { Grid } from "@mui/system"; -import CippButtonCard from "/src/components/CippCards/CippButtonCard"; +import CippButtonCard from "../../../../components/CippCards/CippButtonCard"; const Page = () => { const pageTitle = "Sign Ins Report"; diff --git a/src/pages/index.js b/src/pages/index.js index 576bf8d504ff..fd02b3802bc9 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,470 +1,3 @@ -import Head from "next/head"; -import { useEffect, useState } from "react"; -import { Box, Container, Button, Card, CardContent } from "@mui/material"; -import { Grid } from "@mui/system"; -import { CippInfoBar } from "../components/CippCards/CippInfoBar"; -import { CippChartCard } from "../components/CippCards/CippChartCard"; -import { CippPropertyListCard } from "../components/CippCards/CippPropertyListCard"; -import { Layout as DashboardLayout } from "../layouts/index.js"; -import { useSettings } from "../hooks/use-settings"; -import { getCippFormatting } from "../utils/get-cipp-formatting.js"; -import Portals from "../data/portals"; -import { BulkActionsMenu } from "../components/bulk-actions-menu.js"; -import { CippUniversalSearch } from "../components/CippCards/CippUniversalSearch.jsx"; -import { ApiGetCall } from "../api/ApiCall.jsx"; -import { CippCopyToClipBoard } from "../components/CippComponents/CippCopyToClipboard.jsx"; -import { ExecutiveReportButton } from "../components/ExecutiveReportButton.js"; +import DashboardV2 from "./dashboardv2"; -const Page = () => { - const settings = useSettings(); - const { currentTenant } = settings; - const [domainVisible, setDomainVisible] = useState(false); - - const organization = ApiGetCall({ - url: "/api/ListOrg", - queryKey: `${currentTenant}-ListOrg`, - data: { tenantFilter: currentTenant }, - }); - - const dashboard = ApiGetCall({ - url: "/api/ListuserCounts", - data: { tenantFilter: currentTenant }, - queryKey: `${currentTenant}-ListuserCounts`, - }); - - const sharepoint = ApiGetCall({ - url: "/api/ListSharepointQuota", - queryKey: `${currentTenant}-ListSharepointQuota`, - data: { tenantFilter: currentTenant }, - }); - - const standards = ApiGetCall({ - url: "/api/ListStandardTemplates", - queryKey: `${currentTenant}-ListStandardTemplates`, - }); - - const driftApi = ApiGetCall({ - url: "/api/listTenantDrift", - data: { - TenantFilter: currentTenant, - }, - queryKey: `TenantDrift-${currentTenant}`, - }); - - const partners = ApiGetCall({ - url: "/api/ListGraphRequest", - queryKey: `${currentTenant}-ListPartners`, - data: { - Endpoint: "policies/crossTenantAccessPolicy/partners", - tenantFilter: currentTenant, - ReverseTenantLookup: true, - }, - }); - - const currentTenantInfo = ApiGetCall({ - url: "/api/ListTenants", - queryKey: `ListTenants`, - }); - - // Top bar data - const tenantInfo = [ - { name: "Tenant Name", data: organization.data?.displayName }, - { - name: "Tenant ID", - data: ( - <> - - - ), - }, - { - name: "Default Domain", - data: ( - <> - domain.isDefault === true)?.name - } - type="chip" - /> - - ), - }, - { - name: "AD Sync Enabled", - data: getCippFormatting(organization.data?.onPremisesSyncEnabled, "dirsync"), - }, - ]; - - // Process drift data for chart - filter by current tenant and aggregate - const processDriftDataForTenant = (driftData, currentTenant) => { - if (!driftData) { - return { - alignedCount: 0, - acceptedDeviationsCount: 0, - currentDeviationsCount: 0, - customerSpecificDeviations: 0, - hasData: false, - }; - } - - const rawDriftData = driftData || []; - const tenantDriftData = Array.isArray(rawDriftData) - ? rawDriftData.filter((item) => item.tenantFilter === currentTenant) - : []; - - const hasData = tenantDriftData.length > 0; - - // Aggregate data across all standards for this tenant - const aggregatedData = tenantDriftData.reduce( - (acc, item) => { - acc.acceptedDeviationsCount += item.acceptedDeviationsCount || 0; - acc.currentDeviationsCount += item.currentDeviationsCount || 0; - acc.alignedCount += item.alignedCount || 0; - acc.customerSpecificDeviations += item.customerSpecificDeviationsCount || 0; - return acc; - }, - { - acceptedDeviationsCount: 0, - currentDeviationsCount: 0, - alignedCount: 0, - customerSpecificDeviations: 0, - } - ); - - return { ...aggregatedData, hasData }; - }; - - function getActionCountsForTenant(standardsData, currentTenant) { - if (!standardsData) { - return { - remediateCount: 0, - alertCount: 0, - reportCount: 0, - total: 0, - }; - } - - const applicableTemplates = standardsData.filter((template) => { - const tenantFilterArr = Array.isArray(template?.tenantFilter) ? template.tenantFilter : []; - const excludedTenantsArr = Array.isArray(template?.excludedTenants) - ? template.excludedTenants - : []; - - const tenantInFilter = - tenantFilterArr.length > 0 && tenantFilterArr.some((tf) => tf.value === currentTenant); - - const allTenantsTemplate = - tenantFilterArr.some((tf) => tf.value === "AllTenants") && - (excludedTenantsArr.length === 0 || - !excludedTenantsArr.some((et) => et.value === currentTenant)); - - return tenantInFilter || allTenantsTemplate; - }); - - // Combine standards from all applicable templates: - let combinedStandards = {}; - for (const template of applicableTemplates) { - for (const [standardKey, standardValue] of Object.entries(template.standards)) { - combinedStandards[standardKey] = standardValue; - } - } - - // Count each action type: - let remediateCount = 0; - let alertCount = 0; - let reportCount = 0; - - for (const [, standard] of Object.entries(combinedStandards)) { - let actions = standard.action || []; - if (!Array.isArray(actions)) { - actions = [actions]; - } - actions.forEach((actionObj) => { - if (actionObj?.value === "Remediate") { - remediateCount++; - } else if (actionObj?.value === "Alert") { - alertCount++; - } else if (actionObj?.value === "Report") { - reportCount++; - } - }); - } - - const total = Object.keys(combinedStandards).length; - - return { remediateCount, alertCount, reportCount, total }; - } - - const driftData = processDriftDataForTenant(driftApi.data, currentTenant); - const { remediateCount, alertCount, reportCount, total } = getActionCountsForTenant( - standards.data, - currentTenant - ); - - const [PortalMenuItems, setPortalMenuItems] = useState([]); - const [partnersVisible, setPartnersVisible] = useState(false); - - const formatStorageSize = (sizeInMB) => { - if (sizeInMB >= 1024) { - return `${(sizeInMB / 1024).toFixed(2)}GB`; - } - return `${sizeInMB}MB`; - }; - - // Function to filter portals based on user preferences - const getFilteredPortals = () => { - const defaultLinks = { - M365_Portal: true, - Exchange_Portal: true, - Entra_Portal: true, - Teams_Portal: true, - Azure_Portal: true, - Intune_Portal: true, - SharePoint_Admin: true, - Security_Portal: true, - Compliance_Portal: true, - Power_Platform_Portal: true, - Power_BI_Portal: true, - }; - - let portalLinks; - if (settings.UserSpecificSettings?.portalLinks) { - portalLinks = { ...defaultLinks, ...settings.UserSpecificSettings.portalLinks }; - } else if (settings.portalLinks) { - portalLinks = { ...defaultLinks, ...settings.portalLinks }; - } else { - portalLinks = defaultLinks; - } - - // Filter the portals based on user settings - return Portals.filter((portal) => { - const settingKey = portal.name; - return settingKey ? portalLinks[settingKey] === true : true; - }); - }; - - useEffect(() => { - if (currentTenantInfo.isSuccess) { - const tenantLookup = currentTenantInfo.data?.find( - (tenant) => tenant.defaultDomainName === currentTenant - ); - - // Get filtered portals based on user preferences - const filteredPortals = getFilteredPortals(); - - const menuItems = filteredPortals.map((portal) => ({ - label: portal.label, - target: "_blank", - link: portal.url.replace(portal.variable, tenantLookup?.[portal.variable]), - icon: portal.icon, - })); - setPortalMenuItems(menuItems); - } - }, [ - currentTenantInfo.isSuccess, - currentTenant, - settings.portalLinks, - settings.UserSpecificSettings, - ]); - - return ( - <> - - Dashboard - - - - - - - - - - - {/* TODO: Remove Card from inside CippUniversalSearch to avoid double border */} - - - - - - - - - - - - - - - - - - - - - {/* Converted Domain Names to Property List */} - - ({ - label: "", - value: domain.name, - }))} - actionButton={ - organization.data?.verifiedDomains?.length > 3 && ( - - ) - } - /> - - - - ({ - label: partner.TenantInfo?.displayName, - value: partner.TenantInfo?.defaultDomainName, - }))} - actionButton={ - partners.data?.Results?.length > 3 && ( - - ) - } - /> - - - - - plan.capabilityStatus === "Enabled" && - ["exchange", "AADPremiumService", "WindowsDefenderATP"].includes( - plan.service - ) - ) - .reduce((uniqueServices, curr) => { - const serviceLabel = - curr.service === "exchange" - ? "Exchange" - : curr.service === "AADPremiumService" - ? "AAD Premium" - : curr.service === "Windows Defender" - ? "Windows Defender" - : curr.service; - - if (!uniqueServices.includes(serviceLabel)) { - uniqueServices.push(serviceLabel); - } - return uniqueServices; - }, []) - .join(", "), - }, - ]} - /> - - - - - - ); -}; - -Page.getLayout = (page) => {page}; - -export default Page; +export default DashboardV2; diff --git a/src/pages/license.js b/src/pages/license.js index 442aede25829..e95baaff74b7 100644 --- a/src/pages/license.js +++ b/src/pages/license.js @@ -1,5 +1,5 @@ import { Container } from "@mui/system"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../layouts/index.js"; import { Link } from "@mui/material"; const Page = () => { diff --git a/src/pages/logout/index.js b/src/pages/logout/index.js index dcc11b67276a..f0e0aef0ea09 100644 --- a/src/pages/logout/index.js +++ b/src/pages/logout/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../layouts/index.js"; const Page = () => { const pageTitle = "Log Out"; diff --git a/src/pages/security/defender/deployment/index.js b/src/pages/security/defender/deployment/index.js index a7eb6dbfa7db..65c1967f7d6a 100644 --- a/src/pages/security/defender/deployment/index.js +++ b/src/pages/security/defender/deployment/index.js @@ -1,12 +1,12 @@ import { Typography, Divider } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormTenantSelector } from "/src/components/CippComponents/CippFormTenantSelector"; -import { CippFormCondition } from "/src/components/CippComponents/CippFormCondition"; -import { CippFormInputArray } from "/src/components/CippComponents/CippFormInputArray"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { CippFormTenantSelector } from "../../../../components/CippComponents/CippFormTenantSelector"; +import { CippFormCondition } from "../../../../components/CippComponents/CippFormCondition"; +import { CippFormInputArray } from "../../../../components/CippComponents/CippFormInputArray"; const DeployDefenderForm = () => { const formControl = useForm({ diff --git a/src/pages/security/defender/list-defender-tvm/index.js b/src/pages/security/defender/list-defender-tvm/index.js index cc0f7a971bb7..0282426b7873 100644 --- a/src/pages/security/defender/list-defender-tvm/index.js +++ b/src/pages/security/defender/list-defender-tvm/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; const Page = () => { const pageTitle = "Software Vulnerabilities Status"; diff --git a/src/pages/security/defender/list-defender/index.js b/src/pages/security/defender/list-defender/index.js index 93834cfd42b2..b2aa76a3fe05 100644 --- a/src/pages/security/defender/list-defender/index.js +++ b/src/pages/security/defender/list-defender/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; const Page = () => { const pageTitle = "Defender Status"; diff --git a/src/pages/security/incidents/list-alerts/index.js b/src/pages/security/incidents/list-alerts/index.js index 9379da71347d..e3c4148a3e0d 100644 --- a/src/pages/security/incidents/list-alerts/index.js +++ b/src/pages/security/incidents/list-alerts/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Assignment, Done } from "@mui/icons-material"; const Page = () => { diff --git a/src/pages/security/incidents/list-check-alerts/index.js b/src/pages/security/incidents/list-check-alerts/index.js index 6d1401518d0f..db56faf0454e 100644 --- a/src/pages/security/incidents/list-check-alerts/index.js +++ b/src/pages/security/incidents/list-check-alerts/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Alert, Link } from "@mui/material"; const Page = () => { diff --git a/src/pages/security/incidents/list-incidents/index.js b/src/pages/security/incidents/list-incidents/index.js index 354a86355a8f..821fbd6e8c3c 100644 --- a/src/pages/security/incidents/list-incidents/index.js +++ b/src/pages/security/incidents/list-incidents/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { PersonAdd, PlayArrow, Assignment, Done } from "@mui/icons-material"; const Page = () => { diff --git a/src/pages/security/incidents/list-mdo-alerts/index.js b/src/pages/security/incidents/list-mdo-alerts/index.js index 2bd7601faefa..6ad4151c2ffa 100644 --- a/src/pages/security/incidents/list-mdo-alerts/index.js +++ b/src/pages/security/incidents/list-mdo-alerts/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { PersonAdd, PlayArrow, Assignment, Done } from "@mui/icons-material"; const Page = () => { diff --git a/src/pages/security/reports/list-device-compliance/index.js b/src/pages/security/reports/list-device-compliance/index.js index 6c4ab4343db3..27ce02ecbe70 100644 --- a/src/pages/security/reports/list-device-compliance/index.js +++ b/src/pages/security/reports/list-device-compliance/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; const Page = () => { const pageTitle = "Device Compliance"; diff --git a/src/pages/security/safelinks/safelinks-template/add.jsx b/src/pages/security/safelinks/safelinks-template/add.jsx index 3e9998b0a63e..3a427161d451 100644 --- a/src/pages/security/safelinks/safelinks-template/add.jsx +++ b/src/pages/security/safelinks/safelinks-template/add.jsx @@ -2,10 +2,10 @@ import { useEffect } from "react"; import { Divider } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm, useWatch } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormTenantSelector } from "/src/components/CippComponents/CippFormTenantSelector"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { CippFormTenantSelector } from "../../../../components/CippComponents/CippFormTenantSelector"; const DeploySafeLinksPolicyTemplate = () => { const formControl = useForm({ diff --git a/src/pages/security/safelinks/safelinks-template/create.jsx b/src/pages/security/safelinks/safelinks-template/create.jsx index e87962890042..4d373aa77995 100644 --- a/src/pages/security/safelinks/safelinks-template/create.jsx +++ b/src/pages/security/safelinks/safelinks-template/create.jsx @@ -1,9 +1,9 @@ import { Box } from "@mui/material"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useWatch } from "react-hook-form"; -import { useSettings } from "/src/hooks/use-settings"; -import { SafeLinksForm, safeLinksDataUtils } from "/src/components/CippFormPages/CippSafeLinksPolicyRuleForm"; +import { useSettings } from "../../../../hooks/use-settings"; +import { SafeLinksForm, safeLinksDataUtils } from "../../../../components/CippFormPages/CippSafeLinksPolicyRuleForm"; const Page = () => { const userSettingsDefaults = useSettings(); diff --git a/src/pages/security/safelinks/safelinks-template/edit.jsx b/src/pages/security/safelinks/safelinks-template/edit.jsx index 4945a158aa58..568b4163e0de 100644 --- a/src/pages/security/safelinks/safelinks-template/edit.jsx +++ b/src/pages/security/safelinks/safelinks-template/edit.jsx @@ -1,12 +1,12 @@ import { Box } from "@mui/material"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useWatch } from "react-hook-form"; -import { useSettings } from "/src/hooks/use-settings"; +import { useSettings } from "../../../../hooks/use-settings"; import { useEffect } from "react"; -import { SafeLinksForm, safeLinksDataUtils } from "/src/components/CippFormPages/CippSafeLinksPolicyRuleForm"; +import { SafeLinksForm, safeLinksDataUtils } from "../../../../components/CippFormPages/CippSafeLinksPolicyRuleForm"; import { useRouter } from "next/router"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../api/ApiCall"; const Page = () => { const router = useRouter(); diff --git a/src/pages/security/safelinks/safelinks-template/index.jsx b/src/pages/security/safelinks/safelinks-template/index.jsx index 87388958ff66..6fa5924ef055 100644 --- a/src/pages/security/safelinks/safelinks-template/index.jsx +++ b/src/pages/security/safelinks/safelinks-template/index.jsx @@ -1,10 +1,10 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { TrashIcon } from "@heroicons/react/24/outline"; import { Button } from "@mui/material"; import { RocketLaunch, GitHub, Edit, Add } from "@mui/icons-material"; import Link from "next/link"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../api/ApiCall"; const Page = () => { const pageTitle = "Safe Links Policy Templates"; diff --git a/src/pages/security/safelinks/safelinks/add.jsx b/src/pages/security/safelinks/safelinks/add.jsx index 0153bdb1dcac..ebce15a03ece 100644 --- a/src/pages/security/safelinks/safelinks/add.jsx +++ b/src/pages/security/safelinks/safelinks/add.jsx @@ -1,9 +1,9 @@ import { Box } from "@mui/material"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useWatch } from "react-hook-form"; -import { useSettings } from "/src/hooks/use-settings"; -import { SafeLinksForm, safeLinksDataUtils } from "/src/components/CippFormPages/CippSafeLinksPolicyRuleForm"; +import { useSettings } from "../../../../hooks/use-settings"; +import { SafeLinksForm, safeLinksDataUtils } from "../../../../components/CippFormPages/CippSafeLinksPolicyRuleForm"; const Page = () => { const userSettingsDefaults = useSettings(); diff --git a/src/pages/security/safelinks/safelinks/edit.jsx b/src/pages/security/safelinks/safelinks/edit.jsx index c0ed200b0b26..c49846119edb 100644 --- a/src/pages/security/safelinks/safelinks/edit.jsx +++ b/src/pages/security/safelinks/safelinks/edit.jsx @@ -1,12 +1,12 @@ import { Box } from "@mui/material"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useWatch } from "react-hook-form"; -import { useSettings } from "/src/hooks/use-settings"; +import { useSettings } from "../../../../hooks/use-settings"; import { useEffect } from "react"; -import { SafeLinksForm, safeLinksDataUtils } from "/src/components/CippFormPages/CippSafeLinksPolicyRuleForm"; +import { SafeLinksForm, safeLinksDataUtils } from "../../../../components/CippFormPages/CippSafeLinksPolicyRuleForm"; import { useRouter } from "next/router"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../api/ApiCall"; const Page = () => { const router = useRouter(); diff --git a/src/pages/security/safelinks/safelinks/index.jsx b/src/pages/security/safelinks/safelinks/index.jsx index 70ac3bcf51c0..02ccc9f872ea 100644 --- a/src/pages/security/safelinks/safelinks/index.jsx +++ b/src/pages/security/safelinks/safelinks/index.jsx @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Block, Check, LowPriority, Edit, DeleteForever, Policy, Book } from "@mui/icons-material"; import { Button } from "@mui/material"; import Link from "next/link"; diff --git a/src/pages/teams-share/onedrive/index.js b/src/pages/teams-share/onedrive/index.js index 9ec970fbda1e..8d279cffaf73 100644 --- a/src/pages/teams-share/onedrive/index.js +++ b/src/pages/teams-share/onedrive/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import { CippTablePage } from "../../../components/CippComponents/CippTablePage.jsx"; import { PersonAdd, PersonRemove } from "@mui/icons-material"; const Page = () => { diff --git a/src/pages/teams-share/sharepoint/add-site.js b/src/pages/teams-share/sharepoint/add-site.js index d901d6156c61..b6d2650d890b 100644 --- a/src/pages/teams-share/sharepoint/add-site.js +++ b/src/pages/teams-share/sharepoint/add-site.js @@ -1,10 +1,10 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import "@mui/material"; import { Grid } from "@mui/system"; import { useForm } from "react-hook-form"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import { useSettings } from "/src/hooks/use-settings"; +import CippFormComponent from "../../../components/CippComponents/CippFormComponent"; +import CippFormPage from "../../../components/CippFormPages/CippFormPage"; +import { useSettings } from "../../../hooks/use-settings"; const AddSiteForm = () => { const userSettingsDefaults = useSettings(); diff --git a/src/pages/teams-share/sharepoint/bulk-add-site.js b/src/pages/teams-share/sharepoint/bulk-add-site.js index 7c36905ad519..944d319ebe04 100644 --- a/src/pages/teams-share/sharepoint/bulk-add-site.js +++ b/src/pages/teams-share/sharepoint/bulk-add-site.js @@ -1,9 +1,9 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { useSettings } from "/src/hooks/use-settings"; -import { CippWizardConfirmation } from "/src/components/CippWizard/CippWizardConfirmation"; -import CippWizardPage from "/src/components/CippWizard/CippWizardPage.jsx"; -import { CippTenantStep } from "/src/components/CippWizard/CippTenantStep.jsx"; -import { CippWizardCSVImport } from "/src/components/CippWizard/CippWizardCSVImport"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import { useSettings } from "../../../hooks/use-settings"; +import { CippWizardConfirmation } from "../../../components/CippWizard/CippWizardConfirmation"; +import CippWizardPage from "../../../components/CippWizard/CippWizardPage.jsx"; +import { CippTenantStep } from "../../../components/CippWizard/CippTenantStep.jsx"; +import { CippWizardCSVImport } from "../../../components/CippWizard/CippWizardCSVImport"; const BulkAddSiteForm = () => { const tenantFilter = useSettings().currentTenant; diff --git a/src/pages/teams-share/sharepoint/index.js b/src/pages/teams-share/sharepoint/index.js index d11415f904a8..b92b04c5c5aa 100644 --- a/src/pages/teams-share/sharepoint/index.js +++ b/src/pages/teams-share/sharepoint/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import { CippTablePage } from "../../../components/CippComponents/CippTablePage.jsx"; import { Button } from "@mui/material"; import { Add, @@ -11,8 +11,8 @@ import { Delete, } from "@mui/icons-material"; import Link from "next/link"; -import { CippDataTable } from "/src/components/CippTable/CippDataTable"; -import { useSettings } from "/src/hooks/use-settings"; +import { CippDataTable } from "../../../components/CippTable/CippDataTable"; +import { useSettings } from "../../../hooks/use-settings"; const Page = () => { const pageTitle = "SharePoint Sites"; diff --git a/src/pages/teams-share/teams/business-voice/index.js b/src/pages/teams-share/teams/business-voice/index.js index 6cef4fd00045..a3aa56764153 100644 --- a/src/pages/teams-share/teams/business-voice/index.js +++ b/src/pages/teams-share/teams/business-voice/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { PersonAdd, PersonRemove, LocationOn } from "@mui/icons-material"; const Page = () => { diff --git a/src/pages/teams-share/teams/list-team/add.jsx b/src/pages/teams-share/teams/list-team/add.jsx index 23cdb4df916e..fe625e628eea 100644 --- a/src/pages/teams-share/teams/list-team/add.jsx +++ b/src/pages/teams-share/teams/list-team/add.jsx @@ -2,10 +2,10 @@ import React from "react"; import { Divider } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormUserSelector } from "/src/components/CippComponents/CippFormUserSelector"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { CippFormUserSelector } from "../../../../components/CippComponents/CippFormUserSelector"; import { useSettings } from "../../../../hooks/use-settings"; const TeamsAddTeamForm = () => { diff --git a/src/pages/teams-share/teams/list-team/index.js b/src/pages/teams-share/teams/list-team/index.js index 3ac53cdcfe3a..bf48cccb08a3 100644 --- a/src/pages/teams-share/teams/list-team/index.js +++ b/src/pages/teams-share/teams/list-team/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Button } from "@mui/material"; import { Delete, GroupAdd } from "@mui/icons-material"; import Link from "next/link"; diff --git a/src/pages/teams-share/teams/teams-activity/index.js b/src/pages/teams-share/teams/teams-activity/index.js index c2e8acb22944..2f2797a57cbb 100644 --- a/src/pages/teams-share/teams/teams-activity/index.js +++ b/src/pages/teams-share/teams/teams-activity/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; const Page = () => { const pageTitle = "Teams Activity List"; diff --git a/src/pages/tenant/administration/add-subscription/index.jsx b/src/pages/tenant/administration/add-subscription/index.jsx index 474313f5879b..457f350b3d3b 100644 --- a/src/pages/tenant/administration/add-subscription/index.jsx +++ b/src/pages/tenant/administration/add-subscription/index.jsx @@ -1,9 +1,9 @@ import { Box } from "@mui/material"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useWatch } from "react-hook-form"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { useSettings } from "/src/hooks/use-settings"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { useSettings } from "../../../../hooks/use-settings"; import { Grid, darken, lighten, styled } from "@mui/system"; import { CippPropertyListCard } from "../../../../components/CippCards/CippPropertyListCard"; import { getCippFormatting } from "../../../../utils/get-cipp-formatting"; diff --git a/src/pages/tenant/administration/alert-configuration/alert.jsx b/src/pages/tenant/administration/alert-configuration/alert.jsx index 91d671113f6b..577134d1fcc6 100644 --- a/src/pages/tenant/administration/alert-configuration/alert.jsx +++ b/src/pages/tenant/administration/alert-configuration/alert.jsx @@ -18,15 +18,15 @@ import { Grid } from "@mui/system"; import { ArrowLeftIcon } from "@mui/x-date-pickers"; import { useRouter } from "next/router"; import { useForm, useFormState, useWatch } from "react-hook-form"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormTenantSelector } from "/src/components/CippComponents/CippFormTenantSelector"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { CippFormTenantSelector } from "../../../../components/CippComponents/CippFormTenantSelector"; import CippButtonCard from "../../../../components/CippCards/CippButtonCard"; -import alertList from "/src/data/alerts.json"; -import auditLogTemplates from "/src/data/AuditLogTemplates"; -import auditLogSchema from "/src/data/AuditLogSchema.json"; +import alertList from "../../../../data/alerts.json"; +import auditLogTemplates from "../../../../data/AuditLogTemplates"; +import auditLogSchema from "../../../../data/AuditLogSchema.json"; import { Save, Delete } from "@mui/icons-material"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; // Dashboard layout +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; // Dashboard layout import { CippApiResults } from "../../../../components/CippComponents/CippApiResults"; import { ApiGetCall, ApiPostCall } from "../../../../api/ApiCall"; import { PlusIcon } from "@heroicons/react/24/outline"; @@ -116,7 +116,7 @@ const AlertWizard = () => { formControl.setValue( `conditions.${index}.Input`, { value: "" }, - { shouldValidate: false } + { shouldValidate: false }, ); } else { formControl.setValue(`conditions.${index}.Input`, "", { shouldValidate: false }); @@ -139,13 +139,13 @@ const AlertWizard = () => { ? alert.excludedTenants.map((tenant) => ({ value: tenant, label: tenant })) : []; const usedCommand = alertList?.find( - (cmd) => cmd.name === alert.RawAlert.Command.replace("Get-CIPPAlert", "") + (cmd) => cmd.name === alert.RawAlert.Command.replace("Get-CIPPAlert", ""), ); const recurrenceOption = recurrenceOptions?.find( - (opt) => opt.value === alert.RawAlert.Recurrence + (opt) => opt.value === alert.RawAlert.Recurrence, ); const postExecutionValue = postExecutionOptions.filter((opt) => - alert.RawAlert.PostExecution.split(",").includes(opt.value) + alert.RawAlert.PostExecution.split(",").includes(opt.value), ); let tenantFilterForForm; if (alert.RawAlert.TenantGroup) { @@ -216,7 +216,7 @@ const AlertWizard = () => { setAlertType("audit"); setIsLoadingExistingAlert(true); const foundLogbook = logbookOptions?.find( - (logbook) => logbook.value === alert.RawAlert.type + (logbook) => logbook.value === alert.RawAlert.type, ); const rawConditions = alert.RawAlert.Conditions || []; const formattedConditions = rawConditions.map((cond) => { @@ -264,7 +264,7 @@ const AlertWizard = () => { setTimeout(() => { // Seed previous operator values BEFORE setting conditions to prevent clearing prevOperatorValuesRef.current = formattedConditions.map((c) => - (c.Operator?.value || "").toLowerCase() + (c.Operator?.value || "").toLowerCase(), ); // Process each condition with proper normalization @@ -277,7 +277,7 @@ const AlertWizard = () => { // Normalize based on operator and property type if (Array.isArray(finalInput)) { finalInput = finalInput.map((item) => - typeof item === "string" ? { label: item, value: item } : item + typeof item === "string" ? { label: item, value: item } : item, ); // Further ensure label/value presence and rebuild from schema if possible const schemaOptions = auditLogSchema[cond.Property?.value] || []; @@ -362,7 +362,7 @@ const AlertWizard = () => { })); const recommendedOption = updatedRecurrenceOptions?.find( - (opt) => opt.value === commandValue.value.recommendedRunInterval + (opt) => opt.value === commandValue.value.recommendedRunInterval, ); if (recommendedOption) { @@ -381,7 +381,7 @@ const AlertWizard = () => { if (!selectedPreset) return; setIsLoadingPreset(true); const selectedTemplate = auditLogTemplates?.find( - (template) => template.value === selectedPreset.value + (template) => template.value === selectedPreset.value, ); if (!selectedTemplate) { setIsLoadingPreset(false); @@ -412,7 +412,7 @@ const AlertWizard = () => { } setAddedEvent(formattedConditions.map((_, i) => ({ id: i }))); prevOperatorValuesRef.current = formattedConditions.map((c) => - (c.Operator?.value || "").toLowerCase() + (c.Operator?.value || "").toLowerCase(), ); // Ensure React Hook Form registers nested fields before releasing the guard setTimeout(() => { @@ -448,7 +448,7 @@ const AlertWizard = () => { // Prevent form reload after successful save setHasLoadedExistingAlert(true); }, - } + }, ); }; @@ -493,7 +493,7 @@ const AlertWizard = () => { // Prevent form reload after successful save setHasLoadedExistingAlert(true); }, - } + }, ); }; @@ -753,7 +753,7 @@ const AlertWizard = () => { creatable={true} options={ propertyWatcher?.[event.id]?.Property?.value?.startsWith( - "List:" + "List:", ) ? auditLogSchema[ propertyWatcher?.[event.id]?.Property?.value @@ -958,6 +958,14 @@ 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 +982,14 @@ 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/administration/alert-configuration/index.js b/src/pages/tenant/administration/alert-configuration/index.js index 1cb731572572..da1d36a4fc6a 100644 --- a/src/pages/tenant/administration/alert-configuration/index.js +++ b/src/pages/tenant/administration/alert-configuration/index.js @@ -1,6 +1,6 @@ import { Button } from "@mui/material"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; // had to add an extra path here because I added an extra folder structure. We should switch to absolute pathing so we dont have to deal with relative. +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; // had to add an extra path here because I added an extra folder structure. We should switch to absolute pathing so we dont have to deal with relative. import Link from "next/link"; import { CopyAll, Delete, Edit, NotificationAdd, Visibility } from "@mui/icons-material"; diff --git a/src/pages/tenant/administration/app-consent-requests/index.js b/src/pages/tenant/administration/app-consent-requests/index.js index 5aafe8c75958..658f1996b523 100644 --- a/src/pages/tenant/administration/app-consent-requests/index.js +++ b/src/pages/tenant/administration/app-consent-requests/index.js @@ -1,6 +1,6 @@ import { useState } from "react"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Button, Accordion, @@ -14,8 +14,8 @@ import { Grid } from "@mui/system"; import { Visibility, CheckCircle, ExpandMore, Security } from "@mui/icons-material"; import { FunnelIcon, XMarkIcon } from "@heroicons/react/24/outline"; import { useForm } from "react-hook-form"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { useSettings } from "/src/hooks/use-settings"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { useSettings } from "../../../../hooks/use-settings"; const apiUrl = "/api/ListAppConsentRequests"; const pageTitle = "App Consent Requests"; @@ -86,6 +86,7 @@ const Page = () => { ]; const simpleColumns = [ + "requestDate", // Request Date "requestUser", // Requester "appDisplayName", // Application Name "appId", // Application ID @@ -116,6 +117,7 @@ const Page = () => { const offCanvas = { extendedInfoFields: [ + "requestDate", // Request Date "requestUser", // Requester "appDisplayName", // Application Name "appId", // Application ID diff --git a/src/pages/tenant/administration/applications/app-registrations.js b/src/pages/tenant/administration/applications/app-registrations.js index 05f1a54dee30..74dcf8b4e072 100644 --- a/src/pages/tenant/administration/applications/app-registrations.js +++ b/src/pages/tenant/administration/applications/app-registrations.js @@ -1,12 +1,12 @@ // this page is going to need some love for accounting for filters: https://github.com/KelvinTegelaar/CIPP/blob/main/src/views/tenant/administration/ListEnterpriseApps.jsx#L83 -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { CippFormComponent } from "/src/components/CippComponents/CippFormComponent.jsx"; -import { CertificateCredentialRemovalForm } from "/src/components/CippComponents/CertificateCredentialRemovalForm.jsx"; -import CippPermissionPreview from "/src/components/CippComponents/CippPermissionPreview.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { CippFormComponent } from "../../../../components/CippComponents/CippFormComponent.jsx"; +import { CertificateCredentialRemovalForm } from "../../../../components/CippComponents/CertificateCredentialRemovalForm.jsx"; +import CippPermissionPreview from "../../../../components/CippComponents/CippPermissionPreview.jsx"; import { Launch, Delete, Key, Security, ContentCopy } from "@mui/icons-material"; -import { usePermissions } from "/src/hooks/use-permissions.js"; +import { usePermissions } from "../../../../hooks/use-permissions.js"; import tabOptions from "./tabOptions"; const Page = () => { diff --git a/src/pages/tenant/administration/applications/enterprise-apps.js b/src/pages/tenant/administration/applications/enterprise-apps.js index 52efc533eef1..39aac9021b60 100644 --- a/src/pages/tenant/administration/applications/enterprise-apps.js +++ b/src/pages/tenant/administration/applications/enterprise-apps.js @@ -1,9 +1,9 @@ // this page is going to need some love for accounting for filters: https://github.com/KelvinTegelaar/CIPP/blob/main/src/views/tenant/administration/ListEnterpriseApps.jsx#L83 -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { CippFormComponent } from "/src/components/CippComponents/CippFormComponent.jsx"; -import { CertificateCredentialRemovalForm } from "/src/components/CippComponents/CertificateCredentialRemovalForm.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { CippFormComponent } from "../../../../components/CippComponents/CippFormComponent.jsx"; +import { CertificateCredentialRemovalForm } from "../../../../components/CippComponents/CertificateCredentialRemovalForm.jsx"; import { Launch, Delete, @@ -15,7 +15,7 @@ import { ContentCopy, RocketLaunch, } from "@mui/icons-material"; -import { usePermissions } from "/src/hooks/use-permissions.js"; +import { usePermissions } from "../../../../hooks/use-permissions.js"; import tabOptions from "./tabOptions"; import { Button } from "@mui/material"; import Link from "next/link"; diff --git a/src/pages/tenant/administration/applications/permission-sets/add.js b/src/pages/tenant/administration/applications/permission-sets/add.js index 6f27122c5562..aa189d7be95d 100644 --- a/src/pages/tenant/administration/applications/permission-sets/add.js +++ b/src/pages/tenant/administration/applications/permission-sets/add.js @@ -1,11 +1,11 @@ import { useRouter } from "next/router"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; import { useForm } from "react-hook-form"; import { ApiGetCall, ApiPostCall } from "../../../../../api/ApiCall"; -import CippAppPermissionBuilder from "/src/components/CippComponents/CippAppPermissionBuilder"; -import CippPageCard from "/src/components/CippCards/CippPageCard"; +import CippAppPermissionBuilder from "../../../../../components/CippComponents/CippAppPermissionBuilder"; +import CippPageCard from "../../../../../components/CippCards/CippPageCard"; import { Alert, CardContent, Stack, Typography, Button, Box } from "@mui/material"; -import { CippFormComponent } from "/src/components/CippComponents/CippFormComponent"; +import { CippFormComponent } from "../../../../../components/CippComponents/CippFormComponent"; import { useEffect, useState } from "react"; import { CopyAll } from "@mui/icons-material"; diff --git a/src/pages/tenant/administration/applications/permission-sets/edit.js b/src/pages/tenant/administration/applications/permission-sets/edit.js index e14272fb7b76..1ccb71f38380 100644 --- a/src/pages/tenant/administration/applications/permission-sets/edit.js +++ b/src/pages/tenant/administration/applications/permission-sets/edit.js @@ -1,14 +1,14 @@ import { useRouter } from "next/router"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; import { useForm } from "react-hook-form"; import { ApiGetCall, ApiPostCall } from "../../../../../api/ApiCall"; -import CippAppPermissionBuilder from "/src/components/CippComponents/CippAppPermissionBuilder"; -import CippPageCard from "/src/components/CippCards/CippPageCard"; +import CippAppPermissionBuilder from "../../../../../components/CippComponents/CippAppPermissionBuilder"; +import CippPageCard from "../../../../../components/CippCards/CippPageCard"; import { Alert, CardContent, Skeleton, Stack, Typography } from "@mui/material"; import { useEffect, useState } from "react"; import Link from "next/link"; import { Button } from "@mui/material"; -import { CippFormComponent } from "/src/components/CippComponents/CippFormComponent"; +import { CippFormComponent } from "../../../../../components/CippComponents/CippFormComponent"; const Page = () => { const router = useRouter(); diff --git a/src/pages/tenant/administration/applications/permission-sets/index.js b/src/pages/tenant/administration/applications/permission-sets/index.js index 79280e07e2a9..174a55ec6d39 100644 --- a/src/pages/tenant/administration/applications/permission-sets/index.js +++ b/src/pages/tenant/administration/applications/permission-sets/index.js @@ -1,11 +1,11 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; +import { TabbedLayout } from "../../../../../layouts/TabbedLayout"; +import { CippTablePage } from "../../../../../components/CippComponents/CippTablePage.jsx"; import { Edit, Delete, ContentCopy, Add } from "@mui/icons-material"; import tabOptions from "../tabOptions"; import { Button } from "@mui/material"; import Link from "next/link"; -import { CippPermissionSetDrawer } from "/src/components/CippComponents/CippPermissionSetDrawer"; +import { CippPermissionSetDrawer } from "../../../../../components/CippComponents/CippPermissionSetDrawer"; import { useRef } from "react"; const Page = () => { diff --git a/src/pages/tenant/administration/applications/templates/add.js b/src/pages/tenant/administration/applications/templates/add.js index e927db0acdcf..ef56de9b9fff 100644 --- a/src/pages/tenant/administration/applications/templates/add.js +++ b/src/pages/tenant/administration/applications/templates/add.js @@ -1,11 +1,11 @@ import { useRouter } from "next/router"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; import { useForm } from "react-hook-form"; import { ApiGetCall, ApiPostCall } from "../../../../../api/ApiCall"; -import CippPageCard from "/src/components/CippCards/CippPageCard"; +import CippPageCard from "../../../../../components/CippCards/CippPageCard"; import { CardContent } from "@mui/material"; import { useState } from "react"; -import AppApprovalTemplateForm from "/src/components/CippComponents/AppApprovalTemplateForm"; +import AppApprovalTemplateForm from "../../../../../components/CippComponents/AppApprovalTemplateForm"; const Page = () => { const router = useRouter(); diff --git a/src/pages/tenant/administration/applications/templates/edit.js b/src/pages/tenant/administration/applications/templates/edit.js index 807717c763cf..bef82c603d0b 100644 --- a/src/pages/tenant/administration/applications/templates/edit.js +++ b/src/pages/tenant/administration/applications/templates/edit.js @@ -1,12 +1,12 @@ import { useRouter } from "next/router"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; import { useForm } from "react-hook-form"; import { ApiGetCall, ApiPostCall } from "../../../../../api/ApiCall"; -import CippPageCard from "/src/components/CippCards/CippPageCard"; +import CippPageCard from "../../../../../components/CippCards/CippPageCard"; import { Alert, Button, CardContent } from "@mui/material"; import { useState } from "react"; import Link from "next/link"; -import AppApprovalTemplateForm from "/src/components/CippComponents/AppApprovalTemplateForm"; +import AppApprovalTemplateForm from "../../../../../components/CippComponents/AppApprovalTemplateForm"; const Page = () => { const router = useRouter(); diff --git a/src/pages/tenant/administration/applications/templates/index.js b/src/pages/tenant/administration/applications/templates/index.js index 37de08dd805b..7878f066ef7a 100644 --- a/src/pages/tenant/administration/applications/templates/index.js +++ b/src/pages/tenant/administration/applications/templates/index.js @@ -1,10 +1,10 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import CippPermissionPreview from "/src/components/CippComponents/CippPermissionPreview.jsx"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; +import { TabbedLayout } from "../../../../../layouts/TabbedLayout"; +import { CippTablePage } from "../../../../../components/CippComponents/CippTablePage.jsx"; +import CippPermissionPreview from "../../../../../components/CippComponents/CippPermissionPreview.jsx"; import { Edit, Delete, ContentCopy, Add, GitHub, RocketLaunch } from "@mui/icons-material"; import tabOptions from "../tabOptions"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../../api/ApiCall"; import { Button } from "@mui/material"; import { Stack } from "@mui/system"; import Link from "next/link"; diff --git a/src/pages/tenant/administration/audit-logs/directory-audits.js b/src/pages/tenant/administration/audit-logs/directory-audits.js index 4edda8fd0ec0..8683a1221c5f 100644 --- a/src/pages/tenant/administration/audit-logs/directory-audits.js +++ b/src/pages/tenant/administration/audit-logs/directory-audits.js @@ -1,7 +1,7 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import CippJsonView from "/src/components/CippFormPages/CippJSONView"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import CippJsonView from "../../../../components/CippFormPages/CippJSONView"; import tabOptions from "./tabOptions.json"; const DirectoryAuditsPage = () => { diff --git a/src/pages/tenant/administration/audit-logs/index.js b/src/pages/tenant/administration/audit-logs/index.js index 9a329abfb73a..c04388b8a3cc 100644 --- a/src/pages/tenant/administration/audit-logs/index.js +++ b/src/pages/tenant/administration/audit-logs/index.js @@ -1,8 +1,8 @@ import { useState } from "react"; import { useRouter } from "next/router"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Box, Button, @@ -13,7 +13,7 @@ import { } from "@mui/material"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import { useForm } from "react-hook-form"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; import { EyeIcon } from "@heroicons/react/24/outline"; import { Grid } from "@mui/system"; import tabOptions from "./tabOptions.json"; diff --git a/src/pages/tenant/administration/audit-logs/log.js b/src/pages/tenant/administration/audit-logs/log.js index d240889c26e7..ce8c8554ec5d 100644 --- a/src/pages/tenant/administration/audit-logs/log.js +++ b/src/pages/tenant/administration/audit-logs/log.js @@ -1,7 +1,7 @@ import { useRouter } from "next/router"; import { useEffect, useState } from "react"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { ApiGetCall } from "../../../../api/ApiCall"; import { Box, Typography, @@ -13,14 +13,14 @@ import { Divider, SvgIcon, } from "@mui/material"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; -import { CippPropertyListCard } from "/src/components/CippCards/CippPropertyListCard"; +import CippFormSkeleton from "../../../../components/CippFormPages/CippFormSkeleton"; +import { CippPropertyListCard } from "../../../../components/CippCards/CippPropertyListCard"; import { getCippFormatting } from "../../../../utils/get-cipp-formatting"; import { getCippTranslation } from "../../../../utils/get-cipp-translation"; import CippGeoLocation from "../../../../components/CippComponents/CippGeoLocation"; import { Grid } from "@mui/system"; import { OpenInNew } from "@mui/icons-material"; -import auditLogTranslation from "/src/data/audit-log-translations.json"; +import auditLogTranslation from "../../../../data/audit-log-translations.json"; import { ArrowLeftIcon } from "@mui/x-date-pickers"; const Page = () => { diff --git a/src/pages/tenant/administration/audit-logs/search-results.js b/src/pages/tenant/administration/audit-logs/search-results.js index 88ab5a7bf26b..93cedb6d263a 100644 --- a/src/pages/tenant/administration/audit-logs/search-results.js +++ b/src/pages/tenant/administration/audit-logs/search-results.js @@ -1,13 +1,13 @@ import { useRouter } from "next/router"; import { useState, useEffect } from "react"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { CippApiDialog } from "/src/components/CippComponents/CippApiDialog.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { CippApiDialog } from "../../../../components/CippComponents/CippApiDialog.jsx"; import { EyeIcon, ArrowLeftIcon } from "@heroicons/react/24/outline"; -import CippAuditLogDetails from "/src/components/CippComponents/CippAuditLogDetails.jsx"; +import CippAuditLogDetails from "../../../../components/CippComponents/CippAuditLogDetails.jsx"; import { Button, SvgIcon, Box } from "@mui/material"; import { ManageSearch } from "@mui/icons-material"; -import { useDialog } from "/src/hooks/use-dialog"; +import { useDialog } from "../../../../hooks/use-dialog"; const searchResultsColumns = [ "createdDateTime", diff --git a/src/pages/tenant/administration/audit-logs/searches.js b/src/pages/tenant/administration/audit-logs/searches.js index a16a4e3122e0..ffa70e4ab681 100644 --- a/src/pages/tenant/administration/audit-logs/searches.js +++ b/src/pages/tenant/administration/audit-logs/searches.js @@ -1,11 +1,11 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { CippAuditLogSearchDrawer } from "/src/components/CippComponents/CippAuditLogSearchDrawer.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { CippAuditLogSearchDrawer } from "../../../../components/CippComponents/CippAuditLogSearchDrawer.jsx"; import { EyeIcon } from "@heroicons/react/24/outline"; import { ManageSearch } from "@mui/icons-material"; import tabOptions from "./tabOptions.json"; -import { useSettings } from "/src/hooks/use-settings"; +import { useSettings } from "../../../../hooks/use-settings"; const simpleColumns = ["displayName", "status", "filterStartDateTime", "filterEndDateTime"]; diff --git a/src/pages/tenant/administration/authentication-methods/index.js b/src/pages/tenant/administration/authentication-methods/index.js index d86eccb0c3e6..104f44c93be8 100644 --- a/src/pages/tenant/administration/authentication-methods/index.js +++ b/src/pages/tenant/administration/authentication-methods/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Check, Block } from "@mui/icons-material"; const Page = () => { diff --git a/src/pages/tenant/administration/domains/index.js b/src/pages/tenant/administration/domains/index.js index 4d4548691292..52bb5ec87977 100644 --- a/src/pages/tenant/administration/domains/index.js +++ b/src/pages/tenant/administration/domains/index.js @@ -1,13 +1,13 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { CheckCircle, Star, Delete } from "@mui/icons-material"; -import { CippAddDomainDrawer } from "/src/components/CippComponents/CippAddDomainDrawer.jsx"; -import { CippDomainVerificationRecords } from "/src/components/CippComponents/CippDomainVerificationRecords.jsx"; -import { CippDomainServiceConfigurationRecords } from "/src/components/CippComponents/CippDomainServiceConfigurationRecords.jsx"; +import { CippAddDomainDrawer } from "../../../../components/CippComponents/CippAddDomainDrawer.jsx"; +import { CippDomainVerificationRecords } from "../../../../components/CippComponents/CippDomainVerificationRecords.jsx"; +import { CippDomainServiceConfigurationRecords } from "../../../../components/CippComponents/CippDomainServiceConfigurationRecords.jsx"; import { Box, Typography, Divider } from "@mui/material"; import { Stack } from "@mui/system"; -import { CippPropertyList } from "/src/components/CippComponents/CippPropertyList"; -import { getCippFormatting } from "/src/utils/get-cipp-formatting"; +import { CippPropertyList } from "../../../../components/CippComponents/CippPropertyList"; +import { getCippFormatting } from "../../../../utils/get-cipp-formatting"; const Page = () => { const pageTitle = "Domains"; diff --git a/src/pages/tenant/administration/index.js b/src/pages/tenant/administration/index.js index ddd398e434f7..f2222b9e4fab 100644 --- a/src/pages/tenant/administration/index.js +++ b/src/pages/tenant/administration/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; const Page = () => { const pageTitle = "Administration"; diff --git a/src/pages/tenant/administration/partner-relationships/index.js b/src/pages/tenant/administration/partner-relationships/index.js index 82d2307a0935..7784454e8d5b 100644 --- a/src/pages/tenant/administration/partner-relationships/index.js +++ b/src/pages/tenant/administration/partner-relationships/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; // Developer Note: The "Tenant" column should be omitted if tenant is not "AllTenants". // You may handle tenant filtering or visibility at a higher level if required. diff --git a/src/pages/tenant/administration/securescore/index.js b/src/pages/tenant/administration/securescore/index.js index 42002937e143..3c3a3aafb1bf 100644 --- a/src/pages/tenant/administration/securescore/index.js +++ b/src/pages/tenant/administration/securescore/index.js @@ -1,5 +1,5 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import tabOptions from "./tabOptions"; import { useSecureScore } from "../../../../hooks/use-securescore"; import { CippInfoBar } from "../../../../components/CippCards/CippInfoBar"; diff --git a/src/pages/tenant/administration/securescore/table.js b/src/pages/tenant/administration/securescore/table.js index e06dc5cc13f4..79afdef8d532 100644 --- a/src/pages/tenant/administration/securescore/table.js +++ b/src/pages/tenant/administration/securescore/table.js @@ -1,5 +1,5 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import tabOptions from "./tabOptions"; import CippTablePage from "../../../../components/CippComponents/CippTablePage"; import { useSecureScore } from "../../../../hooks/use-securescore"; diff --git a/src/pages/tenant/administration/tenantlookup/index.js b/src/pages/tenant/administration/tenantlookup/index.js index bb1c50698df7..9143eb017d07 100644 --- a/src/pages/tenant/administration/tenantlookup/index.js +++ b/src/pages/tenant/administration/tenantlookup/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; const Page = () => { const pageTitle = "Tenant Lookup"; diff --git a/src/pages/tenant/administration/tenants/edit.js b/src/pages/tenant/administration/tenants/edit.js index 44f643c5653d..960e536859b1 100644 --- a/src/pages/tenant/administration/tenants/edit.js +++ b/src/pages/tenant/administration/tenants/edit.js @@ -1,18 +1,18 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm } from "react-hook-form"; import { ApiGetCall } from "../../../../api/ApiCall"; import { useEffect, useState } from "react"; import { useRouter } from "next/router"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; import { Stack, Box, Tab, Tabs, Typography, Button } from "@mui/material"; import { Grid } from "@mui/system"; -import { CippCardTabPanel } from "/src/components/CippComponents/CippCardTabPanel"; -import CippFormSection from "/src/components/CippFormPages/CippFormSection"; +import { CippCardTabPanel } from "../../../../components/CippComponents/CippCardTabPanel"; +import CippFormSection from "../../../../components/CippFormPages/CippFormSection"; import CippPageCard from "../../../../components/CippCards/CippPageCard"; -import { CippPropertyListCard } from "/src/components/CippCards/CippPropertyListCard"; +import { CippPropertyListCard } from "../../../../components/CippCards/CippPropertyListCard"; import { getCippFormatting } from "../../../../utils/get-cipp-formatting"; -import CippCustomVariables from "/src/components/CippComponents/CippCustomVariables"; -import { CippOffboardingDefaultSettings } from "/src/components/CippComponents/CippOffboardingDefaultSettings"; +import CippCustomVariables from "../../../../components/CippComponents/CippCustomVariables"; +import { CippOffboardingDefaultSettings } from "../../../../components/CippComponents/CippOffboardingDefaultSettings"; function tabProps(index) { return { diff --git a/src/pages/tenant/administration/tenants/global-variables.js b/src/pages/tenant/administration/tenants/global-variables.js index c5ed3f7e60d0..9ebdb7b8b5ee 100644 --- a/src/pages/tenant/administration/tenants/global-variables.js +++ b/src/pages/tenant/administration/tenants/global-variables.js @@ -1,8 +1,8 @@ import tabOptions from "./tabOptions"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippCustomVariables from "/src/components/CippComponents/CippCustomVariables.jsx"; -import CippPageCard from "/src/components/CippCards/CippPageCard.jsx"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippCustomVariables from "../../../../components/CippComponents/CippCustomVariables.jsx"; +import CippPageCard from "../../../../components/CippCards/CippPageCard.jsx"; const Page = () => { return ( diff --git a/src/pages/tenant/administration/tenants/groups/add.js b/src/pages/tenant/administration/tenants/groups/add.js index 9b3c48e42494..229313fb75ad 100644 --- a/src/pages/tenant/administration/tenants/groups/add.js +++ b/src/pages/tenant/administration/tenants/groups/add.js @@ -1,11 +1,11 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; import { useForm } from "react-hook-form"; import { ApiPostCall } from "../../../../../api/ApiCall"; import { Box } from "@mui/material"; import { Grid } from "@mui/system"; import CippPageCard from "../../../../../components/CippCards/CippPageCard"; -import { CippApiResults } from "/src/components/CippComponents/CippApiResults"; -import CippAddEditTenantGroups from "/src/components/CippComponents/CippAddEditTenantGroups"; +import { CippApiResults } from "../../../../../components/CippComponents/CippApiResults"; +import CippAddEditTenantGroups from "../../../../../components/CippComponents/CippAddEditTenantGroups"; const Page = () => { const formControl = useForm({ diff --git a/src/pages/tenant/administration/tenants/groups/edit.js b/src/pages/tenant/administration/tenants/groups/edit.js index 33edecd5e1c6..f46b1d4d47ad 100644 --- a/src/pages/tenant/administration/tenants/groups/edit.js +++ b/src/pages/tenant/administration/tenants/groups/edit.js @@ -1,11 +1,11 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; import { useForm } from "react-hook-form"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../../api/ApiCall"; import { useEffect } from "react"; import { useRouter } from "next/router"; import { Box } from "@mui/material"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippAddEditTenantGroups from "/src/components/CippComponents/CippAddEditTenantGroups"; +import CippFormPage from "../../../../../components/CippFormPages/CippFormPage"; +import CippAddEditTenantGroups from "../../../../../components/CippComponents/CippAddEditTenantGroups"; const Page = () => { const router = useRouter(); diff --git a/src/pages/tenant/administration/tenants/groups/index.js b/src/pages/tenant/administration/tenants/groups/index.js index ea0416d936bf..ca49669462af 100644 --- a/src/pages/tenant/administration/tenants/groups/index.js +++ b/src/pages/tenant/administration/tenants/groups/index.js @@ -1,15 +1,15 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; +import { TabbedLayout } from "../../../../../layouts/TabbedLayout"; +import { CippTablePage } from "../../../../../components/CippComponents/CippTablePage.jsx"; import tabOptions from "../tabOptions"; import { Edit, PlayArrow, GroupAdd } from "@mui/icons-material"; import { TrashIcon } from "@heroicons/react/24/outline"; -import { CippAddTenantGroupDrawer } from "/src/components/CippComponents/CippAddTenantGroupDrawer"; -import { CippApiLogsDrawer } from "/src/components/CippComponents/CippApiLogsDrawer"; -import { CippTenantGroupOffCanvas } from "/src/components/CippComponents/CippTenantGroupOffCanvas"; -import { CippApiDialog } from "/src/components/CippComponents/CippApiDialog.jsx"; +import { CippAddTenantGroupDrawer } from "../../../../../components/CippComponents/CippAddTenantGroupDrawer"; +import { CippApiLogsDrawer } from "../../../../../components/CippComponents/CippApiLogsDrawer"; +import { CippTenantGroupOffCanvas } from "../../../../../components/CippComponents/CippTenantGroupOffCanvas"; +import { CippApiDialog } from "../../../../../components/CippComponents/CippApiDialog.jsx"; import { Box, Button } from "@mui/material"; -import { useDialog } from "/src/hooks/use-dialog.js"; +import { useDialog } from "../../../../../hooks/use-dialog.js"; const Page = () => { const pageTitle = "Tenant Groups"; diff --git a/src/pages/tenant/administration/tenants/index.js b/src/pages/tenant/administration/tenants/index.js index 129b26fb1db2..b6539a5ad323 100644 --- a/src/pages/tenant/administration/tenants/index.js +++ b/src/pages/tenant/administration/tenants/index.js @@ -1,6 +1,6 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { DeleteOutline, Edit } from "@mui/icons-material"; import tabOptions from "./tabOptions"; diff --git a/src/pages/tenant/backup/backup-wizard/add.jsx b/src/pages/tenant/backup/backup-wizard/add.jsx index cbceda81daf6..d4efe84bcbda 100644 --- a/src/pages/tenant/backup/backup-wizard/add.jsx +++ b/src/pages/tenant/backup/backup-wizard/add.jsx @@ -3,10 +3,10 @@ import { Typography } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm } from "react-hook-form"; import { omit } from "lodash"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { useSettings } from "/src/hooks/use-settings"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { useSettings } from "../../../../hooks/use-settings"; import { CippFormTenantSelector } from "../../../../components/CippComponents/CippFormTenantSelector"; const CreateBackup = () => { diff --git a/src/pages/tenant/backup/backup-wizard/index.js b/src/pages/tenant/backup/backup-wizard/index.js index f7b9e0b6b452..59b1f67b6c25 100644 --- a/src/pages/tenant/backup/backup-wizard/index.js +++ b/src/pages/tenant/backup/backup-wizard/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Button } from "@mui/material"; import { Restore, Backup } from "@mui/icons-material"; import Link from "next/link"; diff --git a/src/pages/tenant/backup/backup-wizard/restore.jsx b/src/pages/tenant/backup/backup-wizard/restore.jsx index 7f4a6414346a..12a628ddc16f 100644 --- a/src/pages/tenant/backup/backup-wizard/restore.jsx +++ b/src/pages/tenant/backup/backup-wizard/restore.jsx @@ -2,11 +2,11 @@ import { useState, useEffect } from "react"; import { Alert, Divider, Typography } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { useSettings } from "/src/hooks/use-settings"; -import { CippFormCondition } from "/src/components/CippComponents/CippFormCondition"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { useSettings } from "../../../../hooks/use-settings"; +import { CippFormCondition } from "../../../../components/CippComponents/CippFormCondition"; import { Chip, Stack } from "@mui/material"; const RestoreBackupForm = () => { diff --git a/src/pages/tenant/conditional/deploy-vacation/add.jsx b/src/pages/tenant/conditional/deploy-vacation/add.jsx index d9472444cc49..8ca5bbd67cc5 100644 --- a/src/pages/tenant/conditional/deploy-vacation/add.jsx +++ b/src/pages/tenant/conditional/deploy-vacation/add.jsx @@ -1,12 +1,12 @@ import React from "react"; import { Box, Divider, Stack, Typography } from "@mui/material"; import { Grid } from "@mui/system"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useWatch } from "react-hook-form"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormUserSelector } from "/src/components/CippComponents/CippFormUserSelector"; -import { CippFormTenantSelector } from "/src/components/CippComponents/CippFormTenantSelector"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { CippFormUserSelector } from "../../../../components/CippComponents/CippFormUserSelector"; +import { CippFormTenantSelector } from "../../../../components/CippComponents/CippFormTenantSelector"; const Page = () => { const formControl = useForm({ diff --git a/src/pages/tenant/conditional/deploy-vacation/index.js b/src/pages/tenant/conditional/deploy-vacation/index.js index bf1dfcafc145..14034536f6a5 100644 --- a/src/pages/tenant/conditional/deploy-vacation/index.js +++ b/src/pages/tenant/conditional/deploy-vacation/index.js @@ -1,8 +1,8 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippTablePage from "/src/components/CippComponents/CippTablePage"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippTablePage from "../../../../components/CippComponents/CippTablePage"; import { Delete } from "@mui/icons-material"; import { EyeIcon } from "@heroicons/react/24/outline"; -import { CippAddVacationModeDrawer } from "/src/components/CippComponents/CippAddVacationModeDrawer"; +import { CippAddVacationModeDrawer } from "../../../../components/CippComponents/CippAddVacationModeDrawer"; const Page = () => { const actions = [ diff --git a/src/pages/tenant/conditional/list-named-locations/add.jsx b/src/pages/tenant/conditional/list-named-locations/add.jsx index 906b869ed9d1..e7ba7ebe610e 100644 --- a/src/pages/tenant/conditional/list-named-locations/add.jsx +++ b/src/pages/tenant/conditional/list-named-locations/add.jsx @@ -2,12 +2,12 @@ import React from "react"; import { Typography } from "@mui/material"; import { Grid } from "@mui/system"; import { useForm } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { CippFormTenantSelector } from "/src/components/CippComponents/CippFormTenantSelector"; -import { CippFormCondition } from "/src/components/CippComponents/CippFormCondition"; -import countryList from "/src/data/countryList.json"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { CippFormTenantSelector } from "../../../../components/CippComponents/CippFormTenantSelector"; +import { CippFormCondition } from "../../../../components/CippComponents/CippFormCondition"; +import countryList from "../../../../data/countryList.json"; const DeployNamedLocationForm = () => { const formControl = useForm({ diff --git a/src/pages/tenant/conditional/list-named-locations/index.js b/src/pages/tenant/conditional/list-named-locations/index.js index 8b19e50281cc..20e6ca12d03e 100644 --- a/src/pages/tenant/conditional/list-named-locations/index.js +++ b/src/pages/tenant/conditional/list-named-locations/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Button } from "@mui/material"; import Link from "next/link"; import { @@ -11,7 +11,7 @@ import { TrashIcon, } from "@heroicons/react/24/outline"; import { LocationOn } from "@mui/icons-material"; -import countryList from "/src/data/countryList.json"; +import countryList from "../../../../data/countryList.json"; const Page = () => { const pageTitle = "Named Locations"; @@ -68,10 +68,18 @@ const Page = () => { type: "autoComplete", name: "input", label: "Country", - options: countryList.map(({ Code, Name }) => ({ - value: Code, - label: `${Name} (${Code})`, - })), + validators: { + required: { value: true, message: "Please select a country" }, + }, + options: (row) => { + const existingCountries = row?.countriesAndRegions || []; + return countryList + .filter(({ Code }) => !existingCountries.includes(Code)) + .map(({ Code, Name }) => ({ + value: Code, + label: `${Name} (${Code})`, + })); + }, }, ], confirmText: "Select a country to add to this named location.", @@ -91,14 +99,34 @@ const Page = () => { type: "autoComplete", name: "input", label: "Country", - options: countryList.map(({ Code, Name }) => ({ - value: Code, - label: `${Name} (${Code})`, - })), + multiple: true, + validators: { + required: { value: true, message: "Please select at least one country" }, + validate: (value, formValues, row) => { + const totalCountries = row?.countriesAndRegions?.length || 0; + const selectedCount = Array.isArray(value) ? value.length : value ? 1 : 0; + if (selectedCount >= totalCountries) { + return "You must leave at least one country in the named location"; + } + return true; + }, + }, + options: (row) => { + const currentCountries = row?.countriesAndRegions || []; + return currentCountries.map((code) => { + const country = countryList.find((c) => c.Code === code); + return { + value: code, + label: country ? `${country.Name} (${code})` : code, + }; + }); + }, }, ], - confirmText: "Select a country to remove from this named location.", - condition: (row) => row["@odata.type"] == "#microsoft.graph.countryNamedLocation", + confirmText: "Select countries to remove from this named location.", + condition: (row) => + row["@odata.type"] == "#microsoft.graph.countryNamedLocation" && + (row.countriesAndRegions?.length || 0) > 1, }, { label: "Add IP to named location", @@ -109,8 +137,47 @@ const Page = () => { namedLocationId: "id", change: "!addIp", }, - fields: [{ type: "textField", name: "input", label: "IP" }], - confirmText: "Enter an IP in CIDR format, e.g., 1.1.1.1/32.", + fields: [ + { + type: "textField", + name: "input", + label: "IP", + validators: { + required: { value: true, message: "IP address is required" }, + validate: (value) => { + if (!value) return true; + // IPv4 CIDR pattern + const ipv4Cidr = + /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/(\d{1,3})$/; + // IPv6 CIDR pattern (simplified - covers most common formats) + const ipv6Cidr = + /^(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}|:(?::[0-9a-fA-F]{1,4}){1,7}|::)\/(\d{1,3})$/; + + const ipv4Match = value.match(ipv4Cidr); + const ipv6Match = value.match(ipv6Cidr); + + if (ipv4Match) { + const prefix = parseInt(ipv4Match[1], 10); + if (prefix < 9 || prefix > 32) { + return "CIDR prefix must be between /9 and /32 for IPv4"; + } + return true; + } + + if (ipv6Match) { + const prefix = parseInt(ipv6Match[1], 10); + if (prefix < 9 || prefix > 128) { + return "CIDR prefix must be between /9 and /128 for IPv6"; + } + return true; + } + + return "Invalid CIDR format. Use IPv4 (e.g., 1.1.1.1/32) or IPv6 (e.g., 2001:db8::/32)"; + }, + }, + }, + ], + confirmText: "Enter an IP in CIDR format, e.g., 1.1.1.1/32 or 2001:db8::/32.", condition: (row) => row["@odata.type"] == "#microsoft.graph.ipNamedLocation", }, { @@ -122,9 +189,36 @@ const Page = () => { namedLocationId: "id", change: "!removeIp", }, - fields: [{ type: "textField", name: "input", label: "IP" }], - confirmText: "Enter an IP in CIDR format, e.g., 1.1.1.1/32.", - condition: (row) => row["@odata.type"] == "#microsoft.graph.ipNamedLocation", + fields: [ + { + type: "autoComplete", + name: "input", + label: "IP", + multiple: true, + validators: { + required: { value: true, message: "Please select at least one IP" }, + validate: (value, formValues, row) => { + const totalIps = row?.ipRanges?.length || 0; + const selectedCount = Array.isArray(value) ? value.length : value ? 1 : 0; + if (selectedCount >= totalIps) { + return "You must leave at least one IP in the named location"; + } + return true; + }, + }, + options: (row) => { + const ipRanges = row?.ipRanges || []; + return ipRanges.map((ip) => ({ + value: ip.cidrAddress, + label: ip.cidrAddress, + })); + }, + }, + ], + confirmText: "Select IPs to remove from this named location.", + condition: (row) => + row["@odata.type"] == "#microsoft.graph.ipNamedLocation" && + (row.ipRanges?.length || 0) > 1, }, { label: "Delete named location", diff --git a/src/pages/tenant/conditional/list-policies/index.js b/src/pages/tenant/conditional/list-policies/index.js index 22eed3c63e59..d7f48eac6694 100644 --- a/src/pages/tenant/conditional/list-policies/index.js +++ b/src/pages/tenant/conditional/list-policies/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Block, Check, @@ -14,7 +14,7 @@ import CippJsonView from "../../../../components/CippFormPages/CippJSONView"; import { CippCADeployDrawer } from "../../../../components/CippComponents/CippCADeployDrawer"; import { CippApiLogsDrawer } from "../../../../components/CippComponents/CippApiLogsDrawer"; import { PermissionButton } from "../../../../utils/permissions"; -import { useSettings } from "/src/hooks/use-settings.js"; +import { useSettings } from "../../../../hooks/use-settings.js"; // Page Component const Page = () => { diff --git a/src/pages/tenant/conditional/list-template/edit.jsx b/src/pages/tenant/conditional/list-template/edit.jsx index 0639fb829417..9521ddae99ed 100644 --- a/src/pages/tenant/conditional/list-template/edit.jsx +++ b/src/pages/tenant/conditional/list-template/edit.jsx @@ -2,11 +2,11 @@ import React, { useEffect, useState } from "react"; import { Alert, Box, Typography } from "@mui/material"; import { useForm } from "react-hook-form"; import { useRouter } from "next/router"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; -import { ApiGetCall } from "/src/api/ApiCall"; -import CippTemplateFieldRenderer from "/src/components/CippComponents/CippTemplateFieldRenderer"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormSkeleton from "../../../../components/CippFormPages/CippFormSkeleton"; +import { ApiGetCall } from "../../../../api/ApiCall"; +import CippTemplateFieldRenderer from "../../../../components/CippComponents/CippTemplateFieldRenderer"; const EditCATemplate = () => { const router = useRouter(); diff --git a/src/pages/tenant/conditional/list-template/index.js b/src/pages/tenant/conditional/list-template/index.js index a79bfeef2200..41221d021582 100644 --- a/src/pages/tenant/conditional/list-template/index.js +++ b/src/pages/tenant/conditional/list-template/index.js @@ -1,14 +1,14 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Button, Box } from "@mui/material"; import CippJsonView from "../../../../components/CippFormPages/CippJSONView"; import { Delete, GitHub, Edit, RocketLaunch } from "@mui/icons-material"; -import { ApiGetCall } from "/src/api/ApiCall"; -import { CippPolicyImportDrawer } from "/src/components/CippComponents/CippPolicyImportDrawer.jsx"; -import { CippCADeployDrawer } from "/src/components/CippComponents/CippCADeployDrawer.jsx"; +import { ApiGetCall } from "../../../../api/ApiCall"; +import { CippPolicyImportDrawer } from "../../../../components/CippComponents/CippPolicyImportDrawer.jsx"; +import { CippCADeployDrawer } from "../../../../components/CippComponents/CippCADeployDrawer.jsx"; import { CippApiLogsDrawer } from "../../../../components/CippComponents/CippApiLogsDrawer"; import { PermissionButton } from "../../../../utils/permissions"; -import { useSettings } from "/src/hooks/use-settings.js"; +import { useSettings } from "../../../../hooks/use-settings.js"; import { useState } from "react"; const Page = () => { diff --git a/src/pages/tenant/gdap-management/index.js b/src/pages/tenant/gdap-management/index.js index 5614dea97127..915caff047a0 100644 --- a/src/pages/tenant/gdap-management/index.js +++ b/src/pages/tenant/gdap-management/index.js @@ -1,5 +1,5 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import tabOptions from "./tabOptions"; import { Container } from "@mui/system"; import { Grid } from "@mui/system"; @@ -16,7 +16,7 @@ import CippPermissionCheck from "../../../components/CippSettings/CippPermission import { Button } from "@mui/material"; import { useEffect, useState } from "react"; import CippButtonCard from "../../../components/CippCards/CippButtonCard"; -import { WizardSteps } from "/src/components/CippWizard/wizard-steps"; +import { WizardSteps } from "../../../components/CippWizard/wizard-steps"; import Link from "next/link"; import { CippHead } from "../../../components/CippComponents/CippHead"; diff --git a/src/pages/tenant/gdap-management/invites/add.js b/src/pages/tenant/gdap-management/invites/add.js index ce6fe95f1226..736b207a8742 100644 --- a/src/pages/tenant/gdap-management/invites/add.js +++ b/src/pages/tenant/gdap-management/invites/add.js @@ -1,4 +1,4 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useWatch } from "react-hook-form"; import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; import { Grid } from "@mui/system"; diff --git a/src/pages/tenant/gdap-management/invites/index.js b/src/pages/tenant/gdap-management/invites/index.js index ce1384c92fb5..39729ae02fad 100644 --- a/src/pages/tenant/gdap-management/invites/index.js +++ b/src/pages/tenant/gdap-management/invites/index.js @@ -1,7 +1,7 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import tabOptions from "../tabOptions"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Button } from "@mui/material"; import { Add } from "@mui/icons-material"; import Link from "next/link"; diff --git a/src/pages/tenant/gdap-management/offboarding.js b/src/pages/tenant/gdap-management/offboarding.js index 07495a99119b..d9486f775c25 100644 --- a/src/pages/tenant/gdap-management/offboarding.js +++ b/src/pages/tenant/gdap-management/offboarding.js @@ -1,13 +1,13 @@ -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "../../../components/CippFormPages/CippFormPage"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import tabOptions from "./tabOptions"; import { useForm, useWatch } from "react-hook-form"; -import { CippFormComponent } from "/src/components/CippComponents/CippFormComponent"; -import vendorTenantList from "/src/data/vendorTenantList"; +import { CippFormComponent } from "../../../components/CippComponents/CippFormComponent"; +import vendorTenantList from "../../../data/vendorTenantList"; import { Box, Grid, Stack } from "@mui/system"; import { Alert, Divider, Typography } from "@mui/material"; -import { ApiGetCall, ApiGetCallWithPagination } from "/src/api/ApiCall"; +import { ApiGetCall, ApiGetCallWithPagination } from "../../../api/ApiCall"; import { CippInfoBar } from "../../../components/CippCards/CippInfoBar"; import { ShieldCheckIcon } from "@heroicons/react/24/outline"; import { Apps, Description, Widgets } from "@mui/icons-material"; diff --git a/src/pages/tenant/gdap-management/onboarding/index.js b/src/pages/tenant/gdap-management/onboarding/index.js index d7e0af05ef34..bd411615702f 100644 --- a/src/pages/tenant/gdap-management/onboarding/index.js +++ b/src/pages/tenant/gdap-management/onboarding/index.js @@ -1,7 +1,7 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import tabOptions from "../tabOptions"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Button } from "@mui/material"; import Link from "next/link"; import { Cancel, PlayArrow, Replay } from "@mui/icons-material"; diff --git a/src/pages/tenant/gdap-management/onboarding/start.js b/src/pages/tenant/gdap-management/onboarding/start.js index e6ae11357c1a..c2908d22a467 100644 --- a/src/pages/tenant/gdap-management/onboarding/start.js +++ b/src/pages/tenant/gdap-management/onboarding/start.js @@ -8,21 +8,21 @@ import { CardContent, Typography, } from "@mui/material"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useWatch } from "react-hook-form"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import GDAPRoles from "/src/data/GDAPRoles"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import GDAPRoles from "../../../../data/GDAPRoles"; import { Box, Stack } from "@mui/system"; import { Grid } from "@mui/system"; -import { CippPropertyList } from "/src/components/CippComponents/CippPropertyList"; -import { ApiGetCall, ApiGetCallWithPagination, ApiPostCall } from "/src/api/ApiCall"; +import { CippPropertyList } from "../../../../components/CippComponents/CippPropertyList"; +import { ApiGetCall, ApiGetCallWithPagination, ApiPostCall } from "../../../../api/ApiCall"; import { useEffect, useState } from "react"; -import { getCippFormatting } from "/src/utils/get-cipp-formatting"; +import { getCippFormatting } from "../../../../utils/get-cipp-formatting"; import { router } from "next/router"; -import cippDefaults from "/src/data/CIPPDefaultGDAPRoles"; -import { WizardSteps } from "/src/components/CippWizard/wizard-steps"; +import cippDefaults from "../../../../data/CIPPDefaultGDAPRoles"; +import { WizardSteps } from "../../../../components/CippWizard/wizard-steps"; import { ExpandMore, PlayArrow, Replay } from "@mui/icons-material"; -import CippPageCard from "/src/components/CippCards/CippPageCard"; +import CippPageCard from "../../../../components/CippCards/CippPageCard"; import { getCippTranslation } from "../../../../utils/get-cipp-translation"; import CippDataTableButton from "../../../../components/CippTable/CippDataTableButton"; @@ -507,7 +507,7 @@ const Page = () => { { label: "Invite URL", value: getCippFormatting( - "https://admin.microsoft.com/AdminPortal/Home#/partners/invitation/granularAdminRelationships/" + + "https://admin.cloud.microsoft/?#/partners/invitation/granularAdminRelationships/" + currentRelationship.value, "InviteUrl", "url" diff --git a/src/pages/tenant/gdap-management/relationships/index.js b/src/pages/tenant/gdap-management/relationships/index.js index 5a389831bade..8c3c3da01bc0 100644 --- a/src/pages/tenant/gdap-management/relationships/index.js +++ b/src/pages/tenant/gdap-management/relationships/index.js @@ -1,8 +1,8 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import tabOptions from "../tabOptions"; -import CippTablePage from "/src/components/CippComponents/CippTablePage"; -import CippGdapActions from "/src/components/CippComponents/CippGdapActions"; +import CippTablePage from "../../../../components/CippComponents/CippTablePage"; +import CippGdapActions from "../../../../components/CippComponents/CippGdapActions"; const pageTitle = "GDAP Relationships"; diff --git a/src/pages/tenant/gdap-management/relationships/relationship/index.js b/src/pages/tenant/gdap-management/relationships/relationship/index.js index c739ca3a0e21..9c9bfee83400 100644 --- a/src/pages/tenant/gdap-management/relationships/relationship/index.js +++ b/src/pages/tenant/gdap-management/relationships/relationship/index.js @@ -1,8 +1,8 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; import { useRouter } from "next/router"; -import { ApiGetCall } from "/src/api/ApiCall"; -import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; -import { HeaderedTabbedLayout } from "/src/layouts/HeaderedTabbedLayout"; +import { ApiGetCall } from "../../../../../api/ApiCall"; +import CippFormSkeleton from "../../../../../components/CippFormPages/CippFormSkeleton"; +import { HeaderedTabbedLayout } from "../../../../../layouts/HeaderedTabbedLayout"; import tabOptions from "./tabOptions.json"; import { Box, Grid, Stack } from "@mui/system"; import { CippTimeAgo } from "../../../../../components/CippComponents/CippTimeAgo"; @@ -11,7 +11,7 @@ import { CippPropertyListCard } from "../../../../../components/CippCards/CippPr import { getCippFormatting } from "../../../../../utils/get-cipp-formatting"; import { CippDataTable } from "../../../../../components/CippTable/CippDataTable"; import { Alert, Link } from "@mui/material"; -import CIPPDefaultGDAPRoles from "/src/data/CIPPDefaultGDAPRoles.json"; +import CIPPDefaultGDAPRoles from "../../../../../data/CIPPDefaultGDAPRoles.json"; import { CippCopyToClipBoard } from "../../../../../components/CippComponents/CippCopyToClipboard"; import { Schedule } from "@mui/icons-material"; import { useEffect, useState } from "react"; @@ -120,7 +120,7 @@ const Page = () => { properties.push({ label: "Invite URL", value: getCippFormatting( - "https://admin.microsoft.com/AdminPortal/Home#/partners/invitation/granularAdminRelationships/" + + "https://admin.cloud.microsoft/?#/partners/invitation/granularAdminRelationships/" + data?.id, "InviteUrl", "url" diff --git a/src/pages/tenant/gdap-management/relationships/relationship/mappings.js b/src/pages/tenant/gdap-management/relationships/relationship/mappings.js index 229637399653..3ec708b70c66 100644 --- a/src/pages/tenant/gdap-management/relationships/relationship/mappings.js +++ b/src/pages/tenant/gdap-management/relationships/relationship/mappings.js @@ -1,7 +1,7 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../../layouts/index.js"; import { useRouter } from "next/router"; -import { ApiGetCall } from "/src/api/ApiCall"; -import { HeaderedTabbedLayout } from "/src/layouts/HeaderedTabbedLayout"; +import { ApiGetCall } from "../../../../../api/ApiCall"; +import { HeaderedTabbedLayout } from "../../../../../layouts/HeaderedTabbedLayout"; import tabOptions from "./tabOptions.json"; import { CippTimeAgo } from "../../../../../components/CippComponents/CippTimeAgo"; import { CippDataTable } from "../../../../../components/CippTable/CippDataTable"; diff --git a/src/pages/tenant/gdap-management/role-templates/add.js b/src/pages/tenant/gdap-management/role-templates/add.js index 82b6e1cde666..3d3652e40d64 100644 --- a/src/pages/tenant/gdap-management/role-templates/add.js +++ b/src/pages/tenant/gdap-management/role-templates/add.js @@ -1,8 +1,8 @@ -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm } from "react-hook-form"; -import { CippAddEditGdapRoleTemplate } from "/src/components/CippFormPages/CippAddEditGdapRoleTemplate"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { CippAddEditGdapRoleTemplate } from "../../../../components/CippFormPages/CippAddEditGdapRoleTemplate"; +import { ApiGetCall } from "../../../../api/ApiCall"; const Page = () => { const formControl = useForm({ diff --git a/src/pages/tenant/gdap-management/role-templates/edit.js b/src/pages/tenant/gdap-management/role-templates/edit.js index 8a0fe7564634..24623ef563b8 100644 --- a/src/pages/tenant/gdap-management/role-templates/edit.js +++ b/src/pages/tenant/gdap-management/role-templates/edit.js @@ -1,8 +1,8 @@ -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm } from "react-hook-form"; -import { CippAddEditGdapRoleTemplate } from "/src/components/CippFormPages/CippAddEditGdapRoleTemplate"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { CippAddEditGdapRoleTemplate } from "../../../../components/CippFormPages/CippAddEditGdapRoleTemplate"; +import { ApiGetCall } from "../../../../api/ApiCall"; import { useEffect } from "react"; import { useRouter } from "next/router"; import { ApiGetCallWithPagination } from "../../../../api/ApiCall"; diff --git a/src/pages/tenant/gdap-management/role-templates/index.js b/src/pages/tenant/gdap-management/role-templates/index.js index c884bc8cc484..9364a937a96e 100644 --- a/src/pages/tenant/gdap-management/role-templates/index.js +++ b/src/pages/tenant/gdap-management/role-templates/index.js @@ -1,7 +1,7 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import tabOptions from "../tabOptions"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Alert, Button, SvgIcon } from "@mui/material"; import Link from "next/link"; import { ApiGetCallWithPagination, ApiPostCall } from "../../../../api/ApiCall"; diff --git a/src/pages/tenant/gdap-management/roles/add.js b/src/pages/tenant/gdap-management/roles/add.js index 06808b7e77df..31f4e86b9d09 100644 --- a/src/pages/tenant/gdap-management/roles/add.js +++ b/src/pages/tenant/gdap-management/roles/add.js @@ -1,18 +1,18 @@ import React, { useState } from "react"; import { Alert, Button, SvgIcon, Typography, Tooltip, Link } from "@mui/material"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useWatch } from "react-hook-form"; -import { CippFormComponent } from "/src/components/CippComponents/CippFormComponent"; -import { CippFormCondition } from "/src/components/CippComponents/CippFormCondition"; -import GDAPRoles from "/src/data/GDAPRoles"; +import { CippFormComponent } from "../../../../components/CippComponents/CippFormComponent"; +import { CippFormCondition } from "../../../../components/CippComponents/CippFormCondition"; +import GDAPRoles from "../../../../data/GDAPRoles"; import { Box, Stack, Grid } from "@mui/system"; import { ShieldCheckIcon, PlusSmallIcon } from "@heroicons/react/24/outline"; -import { CippPropertyList } from "/src/components/CippComponents/CippPropertyList"; -import cippDefaults from "/src/data/CIPPDefaultGDAPRoles"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { CippPropertyList } from "../../../../components/CippComponents/CippPropertyList"; +import cippDefaults from "../../../../data/CIPPDefaultGDAPRoles"; +import { ApiGetCall } from "../../../../api/ApiCall"; import { Settings, SyncAlt } from "@mui/icons-material"; -import { CippDataTable } from "/src/components/CippTable/CippDataTable"; +import { CippDataTable } from "../../../../components/CippTable/CippDataTable"; import { TrashIcon } from "@heroicons/react/24/outline"; const Page = () => { diff --git a/src/pages/tenant/gdap-management/roles/index.js b/src/pages/tenant/gdap-management/roles/index.js index 9a050a7d5150..09ce733370b7 100644 --- a/src/pages/tenant/gdap-management/roles/index.js +++ b/src/pages/tenant/gdap-management/roles/index.js @@ -1,7 +1,7 @@ -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import tabOptions from "../tabOptions"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { Button } from "@mui/material"; import { AdminPanelSettings, Add, Delete } from "@mui/icons-material"; import Link from "next/link"; diff --git a/src/pages/tenant/manage/applied-standards.js b/src/pages/tenant/manage/applied-standards.js index 71e4865d8771..fbd55c304e6c 100644 --- a/src/pages/tenant/manage/applied-standards.js +++ b/src/pages/tenant/manage/applied-standards.js @@ -18,8 +18,8 @@ import { Menu, MenuItem, } from "@mui/material"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { HeaderedTabbedLayout } from "/src/layouts/HeaderedTabbedLayout"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import { HeaderedTabbedLayout } from "../../../layouts/HeaderedTabbedLayout"; import { CheckCircle, Cancel, @@ -35,8 +35,9 @@ import { NotificationImportant, Construction, Schedule, + Check, } from "@mui/icons-material"; -import standards from "/src/data/standards.json"; +import standards from "../../../data/standards.json"; import { CippApiDialog } from "../../../components/CippComponents/CippApiDialog"; import { SvgIcon } from "@mui/material"; import { useForm } from "react-hook-form"; @@ -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) { @@ -145,133 +146,150 @@ const Page = () => { Object.entries(selectedTemplate.standards).forEach(([standardKey, standardConfig]) => { if (standardKey === "IntuneTemplate" && Array.isArray(standardConfig)) { standardConfig.forEach((templateItem, index) => { - console.log("Processing IntuneTemplate item:", templateItem); - if ( - templateItem["TemplateList-Tags"]?.value && - templateItem["TemplateList-Tags"]?.addedFields?.templates - ) { - console.log( - "Found TemplateList-Tags for IntuneTemplate:", - templateItem["TemplateList-Tags"] - ); - console.log( - "Templates to expand:", - templateItem["TemplateList-Tags"].addedFields.templates - ); - templateItem["TemplateList-Tags"].addedFields.templates.forEach( - (expandedTemplate) => { - console.log("Expanding IntuneTemplate:", expandedTemplate); - const itemTemplateId = expandedTemplate.GUID; - const standardId = `standards.IntuneTemplate.${itemTemplateId}`; - const standardInfo = standards.find( - (s) => s.name === `standards.IntuneTemplate` - ); - - // Find the tenant's value for this specific template - const currentTenantStandard = currentTenantData.find( - (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 - 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" - ) { - isCompliant = true; - } else if (currentTenantStandard) { - isCompliant = currentTenantStandard.value === true; - } + if (!templateItem) return; // Skip null items + + // Check for both addedFields.templates AND rawData.templates + const tagTemplates = + templateItem["TemplateList-Tags"]?.addedFields?.templates || + templateItem["TemplateList-Tags"]?.rawData?.templates; - // Create a standardValue object that contains the template settings - const templateSettings = { - templateId, - Template: - expandedTemplate.displayName || - expandedTemplate.name || - "Unknown Template", - "Assign to": templateItem.AssignTo || "On", - "Excluded Group": templateItem.excludeGroup || "", - "Included Group": templateItem.customGroup || "", - }; - - // Check if this standard is overridden by another template - const tenantTemplateId = standardObject?.TemplateId; - const isOverridden = tenantTemplateId && tenantTemplateId !== templateId; - const overridingTemplateName = isOverridden - ? getTemplateDisplayName(tenantTemplateId) - : null; - - allStandards.push({ - standardId, - standardName: `Intune Template: ${ - expandedTemplate.displayName || expandedTemplate.name || itemTemplateId - } (via ${templateItem["TemplateList-Tags"].value})`, - currentTenantValue: - standardObject !== undefined - ? { - Value: directStandardValue, - LastRefresh: standardObject?.LastRefresh, - TemplateId: tenantTemplateId, - } - : currentTenantStandard?.value, - standardValue: templateSettings, - complianceStatus: isOverridden - ? "Overridden" - : isCompliant + if (templateItem["TemplateList-Tags"]?.value && tagTemplates) { + tagTemplates.forEach((expandedTemplate) => { + const itemTemplateId = expandedTemplate.GUID; + const standardId = `standards.IntuneTemplate.${itemTemplateId}`; + const standardInfo = standards.find( + (s) => s.name === `standards.IntuneTemplate`, + ); + + // Find the tenant's value for this specific template + const currentTenantStandard = currentTenantData.find( + (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 - match main logic + let isCompliant = false; + + // 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; + } + // THIRD: Fall back to currentTenantStandard + else if (currentTenantStandard) { + isCompliant = currentTenantStandard.value === true; + } + + // Create a standardValue object that contains the template settings + const templateSettings = { + templateId, + Template: + expandedTemplate.displayName || expandedTemplate.name || "Unknown Template", + "Assign to": templateItem.AssignTo || "On", + "Excluded Group": templateItem.excludeGroup || "", + "Included Group": templateItem.customGroup || "", + }; + + // Check if this standard is overridden by another template + const tenantTemplateId = standardObject?.TemplateId; + const isOverridden = tenantTemplateId && tenantTemplateId !== templateId; + const overridingTemplateName = isOverridden + ? getTemplateDisplayName(tenantTemplateId) + : null; + + allStandards.push({ + standardId, + standardName: `Intune Template: ${ + expandedTemplate.displayName || expandedTemplate.name || itemTemplateId + } (via ${templateItem["TemplateList-Tags"]?.value})`, + currentTenantValue: + standardObject !== undefined + ? { + Value: directStandardValue, + LastRefresh: standardObject?.LastRefresh, + TemplateId: tenantTemplateId, + CurrentValue: standardObject?.CurrentValue, + ExpectedValue: standardObject?.ExpectedValue, + } + : currentTenantStandard?.value, + standardValue: templateSettings, + complianceStatus: isOverridden + ? "Overridden" + : isCompliant ? "Compliant" : "Non-Compliant", - isOverridden, - overridingTemplateId: isOverridden ? tenantTemplateId : null, - overridingTemplateName, - complianceDetails: - standardInfo?.docsDescription || standardInfo?.helpText || "", - standardDescription: standardInfo?.helpText || "", - standardImpact: standardInfo?.impact || "Medium Impact", - standardImpactColour: standardInfo?.impactColour || "warning", - templateName: selectedTemplate?.templateName || "Standard Template", - templateActions: (() => { - const actions = templateItem.action || []; - const hasRemediate = actions.some((a) => { - const label = typeof a === "object" ? a?.label || a?.value : a; - return label === "Remediate" || label === "remediate"; - }); - const hasReport = actions.some((a) => { - const label = typeof a === "object" ? a?.label || a?.value : a; - return label === "Report" || label === "report"; - }); - if (hasRemediate && !hasReport) { - return [...actions, "Report"]; - } - return actions; - })(), - autoRemediate: - templateItem.autoRemediate || - templateItem.TemplateList?.autoRemediate || - false, - }); - } - ); + isOverridden, + overridingTemplateId: isOverridden ? tenantTemplateId : null, + overridingTemplateName, + complianceDetails: + standardInfo?.docsDescription || standardInfo?.helpText || "", + standardDescription: standardInfo?.helpText || "", + standardImpact: standardInfo?.impact || "Medium Impact", + standardImpactColour: standardInfo?.impactColour || "warning", + templateName: selectedTemplate?.templateName || "Standard Template", + templateActions: (() => { + const actions = templateItem.action || []; + const hasRemediate = actions.some((a) => { + const label = typeof a === "object" ? a?.label || a?.value : a; + return label === "Remediate" || label === "remediate"; + }); + const hasReport = actions.some((a) => { + const label = typeof a === "object" ? a?.label || a?.value : a; + return label === "Report" || label === "report"; + }); + if (hasRemediate && !hasReport) { + return [...actions, "Report"]; + } + return actions; + })(), + autoRemediate: + templateItem.autoRemediate || + templateItem.TemplateList?.autoRemediate || + false, + }); + }); } else { // Regular TemplateList processing const itemTemplateId = templateItem.TemplateList?.value; 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,14 +338,16 @@ const Page = () => { Value: directStandardValue, LastRefresh: standardObject?.LastRefresh, TemplateId: tenantTemplateId, + CurrentValue: standardObject?.CurrentValue, + ExpectedValue: standardObject?.ExpectedValue, } : currentTenantStandard?.value, standardValue: templateSettings, // Use the template settings object instead of true complianceStatus: isOverridden ? "Overridden" : isCompliant - ? "Compliant" - : "Non-Compliant", + ? "Compliant" + : "Non-Compliant", isOverridden, overridingTemplateId: isOverridden ? tenantTemplateId : null, overridingTemplateName, @@ -367,120 +387,142 @@ const Page = () => { ) { // Process each ConditionalAccessTemplate item separately standardConfig.forEach((templateItem, index) => { + if (!templateItem) return; // Skip null items + + // Check for both addedFields.templates AND rawData.templates + const tagTemplates = + templateItem["TemplateList-Tags"]?.addedFields?.templates || + templateItem["TemplateList-Tags"]?.rawData?.templates; + // Check if this item has TemplateList-Tags and expand them - if ( - templateItem["TemplateList-Tags"]?.value && - templateItem["TemplateList-Tags"]?.addedFields?.templates - ) { - console.log( - "Found TemplateList-Tags for ConditionalAccessTemplate:", - templateItem["TemplateList-Tags"] - ); - console.log( - "Templates to expand:", - templateItem["TemplateList-Tags"].addedFields.templates - ); - // Expand TemplateList-Tags into multiple template items - templateItem["TemplateList-Tags"].addedFields.templates.forEach( - (expandedTemplate) => { - console.log("Expanding ConditionalAccessTemplate:", expandedTemplate); - const itemTemplateId = expandedTemplate.GUID; - const standardId = `standards.ConditionalAccessTemplate.${itemTemplateId}`; - const standardInfo = standards.find( - (s) => s.name === `standards.ConditionalAccessTemplate` - ); - - // Find the tenant's value for this specific template - const currentTenantStandard = currentTenantData.find( - (s) => s.standardId === standardId - ); - const standardObject = currentTenantObj?.[standardId]; - const directStandardValue = standardObject?.Value; - const tenantTemplateId = standardObject?.TemplateId; - const isOverridden = tenantTemplateId && tenantTemplateId !== templateId; - const overridingTemplateName = isOverridden - ? getTemplateDisplayName(tenantTemplateId) - : null; - let isCompliant = false; - - // For ConditionalAccessTemplate, the value is true if compliant, or an object with comparison data if not compliant - if (directStandardValue === true) { - isCompliant = true; - } else { - isCompliant = false; - } + if (templateItem["TemplateList-Tags"]?.value && tagTemplates) { + tagTemplates.forEach((expandedTemplate) => { + const itemTemplateId = expandedTemplate.GUID; + const standardId = `standards.ConditionalAccessTemplate.${itemTemplateId}`; + const standardInfo = standards.find( + (s) => s.name === `standards.ConditionalAccessTemplate`, + ); - // Create a standardValue object that contains the template settings - const templateSettings = { - templateId: itemTemplateId, - Template: - expandedTemplate.displayName || - expandedTemplate.name || - "Unknown Template", - }; - - allStandards.push({ - standardId, - standardName: `Conditional Access Template: ${ - expandedTemplate.displayName || expandedTemplate.name || itemTemplateId - } (via ${templateItem["TemplateList-Tags"].value})`, - currentTenantValue: - standardObject !== undefined - ? { - Value: directStandardValue, - LastRefresh: standardObject?.LastRefresh, - TemplateId: tenantTemplateId, - } - : currentTenantStandard?.value, - standardValue: templateSettings, - complianceStatus: isOverridden - ? "Overridden" - : isCompliant + // Find the tenant's value for this specific template + const currentTenantStandard = currentTenantData.find( + (s) => s.standardId === standardId, + ); + const standardObject = currentTenantObj?.[standardId]; + const directStandardValue = standardObject?.Value; + const tenantTemplateId = standardObject?.TemplateId; + const isOverridden = tenantTemplateId && tenantTemplateId !== templateId; + const overridingTemplateName = isOverridden + ? getTemplateDisplayName(tenantTemplateId) + : null; + let isCompliant = false; + + // 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; + } + // THIRD: Otherwise not compliant + else { + isCompliant = false; + } + + // Create a standardValue object that contains the template settings + const templateSettings = { + templateId: itemTemplateId, + Template: + expandedTemplate.displayName || expandedTemplate.name || "Unknown Template", + }; + + allStandards.push({ + standardId, + standardName: `Conditional Access Template: ${ + expandedTemplate.displayName || expandedTemplate.name || itemTemplateId + } (via ${templateItem["TemplateList-Tags"]?.value})`, + currentTenantValue: + standardObject !== undefined + ? { + Value: directStandardValue, + LastRefresh: standardObject?.LastRefresh, + TemplateId: tenantTemplateId, + CurrentValue: standardObject?.CurrentValue, + ExpectedValue: standardObject?.ExpectedValue, + } + : currentTenantStandard?.value, + standardValue: templateSettings, + complianceStatus: isOverridden + ? "Overridden" + : isCompliant ? "Compliant" : "Non-Compliant", - complianceDetails: - standardInfo?.docsDescription || standardInfo?.helpText || "", - standardDescription: standardInfo?.helpText || "", - standardImpact: standardInfo?.impact || "Medium Impact", - standardImpactColour: standardInfo?.impactColour || "warning", - templateName: selectedTemplate?.templateName || "Standard Template", - templateActions: (() => { - const actions = templateItem.action || []; - const hasRemediate = actions.some((a) => { - const label = typeof a === "object" ? a?.label || a?.value : a; - return label === "Remediate" || label === "remediate"; - }); - const hasReport = actions.some((a) => { - const label = typeof a === "object" ? a?.label || a?.value : a; - return label === "Report" || label === "report"; - }); - if (hasRemediate && !hasReport) { - return [...actions, "Report"]; - } - return actions; - })(), - autoRemediate: - templateItem.autoRemediate || - templateItem.TemplateList?.autoRemediate || - false, - isOverridden, - overridingTemplateId: isOverridden ? tenantTemplateId : null, - overridingTemplateName, - }); - } - ); + complianceDetails: + standardInfo?.docsDescription || standardInfo?.helpText || "", + standardDescription: standardInfo?.helpText || "", + standardImpact: standardInfo?.impact || "Medium Impact", + standardImpactColour: standardInfo?.impactColour || "warning", + templateName: selectedTemplate?.templateName || "Standard Template", + templateActions: (() => { + const actions = templateItem.action || []; + const hasRemediate = actions.some((a) => { + const label = typeof a === "object" ? a?.label || a?.value : a; + return label === "Remediate" || label === "remediate"; + }); + const hasReport = actions.some((a) => { + const label = typeof a === "object" ? a?.label || a?.value : a; + return label === "Report" || label === "report"; + }); + if (hasRemediate && !hasReport) { + return [...actions, "Report"]; + } + return actions; + })(), + autoRemediate: + templateItem.autoRemediate || + templateItem.TemplateList?.autoRemediate || + false, + isOverridden, + overridingTemplateId: isOverridden ? tenantTemplateId : null, + overridingTemplateName, + }); + }); } else { // Regular TemplateList processing const itemTemplateId = templateItem.TemplateList?.value; 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; @@ -515,14 +557,16 @@ const Page = () => { Value: directStandardValue, LastRefresh: standardObject?.LastRefresh, TemplateId: tenantTemplateId, + CurrentValue: standardObject?.CurrentValue, + ExpectedValue: standardObject?.ExpectedValue, } : currentTenantStandard?.value, standardValue: templateSettings, // Use the template settings object instead of true complianceStatus: isOverridden ? "Overridden" : isCompliant - ? "Compliant" - : "Non-Compliant", + ? "Compliant" + : "Non-Compliant", complianceDetails: standardInfo?.docsDescription || standardInfo?.helpText || "", standardDescription: standardInfo?.helpText || "", @@ -565,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; @@ -637,14 +681,16 @@ const Page = () => { Value: directStandardValue, LastRefresh: standardObject?.LastRefresh, TemplateId: tenantTemplateId, + CurrentValue: standardObject?.CurrentValue, + ExpectedValue: standardObject?.ExpectedValue, } : currentTenantStandard?.value, standardValue: templateSettings, complianceStatus: isOverridden ? "Overridden" : isCompliant - ? "Compliant" - : "Non-Compliant", + ? "Compliant" + : "Non-Compliant", complianceDetails: standardInfo?.docsDescription || standardInfo?.helpText || "", standardDescription: standardInfo?.helpText || "", standardImpact: standardInfo?.impact || "Medium Impact", @@ -684,35 +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, ); - // Determine compliance status - let isCompliant = false; - let reportingDisabled = !reportingEnabled; - // 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; - // Special case for boolean standards that are true in the tenant - if (directStandardValue === true) { - // If the standard is directly in the tenant and is true, it's compliant + // Determine compliance - prioritize Value field, then CurrentValue/ExpectedValue comparison + let isCompliant = false; + let reportingDisabled = !reportingEnabled; + + // 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 (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 { @@ -726,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; @@ -746,6 +876,8 @@ const Page = () => { Value: directStandardValue, LastRefresh: standardObject?.LastRefresh, TemplateId: tenantTemplateId, + CurrentValue: standardObject?.CurrentValue, + ExpectedValue: standardObject?.ExpectedValue, } : currentTenantStandard?.value, standardValue: standardSettings, @@ -912,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; @@ -962,51 +1094,7 @@ const Page = () => { // Prepare title and subtitle for HeaderedTabbedLayout const title = selectedTemplate?.templateName || selectedTemplate?.displayName || "Tenant Report"; - const subtitle = [ - { - icon: , - text: ( - - { - const query = { ...router.query }; - if (selectedTemplate && selectedTemplate.value) { - query.templateId = selectedTemplate.value; - } else { - delete query.templateId; - } - router.replace( - { - pathname: router.pathname, - query: query, - }, - undefined, - { shallow: true } - ); - }} - sx={{ minWidth: 300 }} - placeholder="Select a template..." - /> - {templateId && ( - - )} - - ), - }, - ]; + const subtitle = []; // Actions for the header const actions = [ @@ -1123,7 +1211,34 @@ const Page = () => { mt: 2, }} > - + + { + const query = { ...router.query }; + if (selectedTemplate && selectedTemplate.value) { + query.templateId = selectedTemplate.value; + } else { + delete query.templateId; + } + router.replace( + { + pathname: router.pathname, + query: query, + }, + undefined, + { shallow: true }, + ); + }} + sx={{ width: 300 }} + placeholder="Select template..." + /> { }, }} /> + {templateId && ( + + )} - handleMenuClose(item.id)} - > - handleAction("accept-customer-specific", item.id)}> - - Accept Deviation - Customer Specific - - handleAction("accept", item.id)}> - - Accept Deviation - - {supportsDelete && ( - handleAction("deny-delete", item.id)}> - - Deny Deviation - Delete Policy - - )} - handleAction("deny-remediate", item.id)}> - - Deny Deviation - Remediate to align with template - - - + cardLabelBoxActions: ( + ), }; }); // Add action buttons to accepted deviation items const acceptedDeviationItemsWithActions = acceptedDeviationItems.map((item) => { - // Check if this is a template that supports delete action - const supportsDelete = - (item.standardName?.includes("ConditionalAccessTemplate") || - item.standardName?.includes("IntuneTemplate")) && - item.expectedValue === "This policy only exists in the tenant, not in the template."; - return { ...item, - actionButton: ( - <> - - handleMenuClose(`accepted-${item.id}`)} - > - {supportsDelete && ( - handleDeviationAction("deny-delete", item)}> - - Deny - Delete Policy - - )} - handleDeviationAction("deny-remediate", item)}> - - Deny - Remediate to align with template - - handleDeviationAction("accept-customer-specific", item)}> - - Accept - Customer Specific - - - + cardLabelBoxActions: ( + ), }; }); // Add action buttons to customer specific deviation items const customerSpecificDeviationItemsWithActions = customerSpecificDeviationItems.map((item) => { - // Check if this is a template that supports delete action - const supportsDelete = - (item.standardName?.includes("ConditionalAccessTemplate") || - item.standardName?.includes("IntuneTemplate")) && - item.expectedValue === "This policy only exists in the tenant, not in the template."; - return { ...item, - actionButton: ( - <> - - handleMenuClose(`customer-${item.id}`)} - > - {supportsDelete && ( - handleDeviationAction("deny-delete", item)}> - - Deny - Delete - - )} - handleDeviationAction("deny-remediate", item)}> - - Deny - Remediate to align with template - - handleDeviationAction("accept", item)}> - - Accept - - - - ), - }; - }); - - // Add action buttons to denied deviation items - const deniedDeviationItemsWithActions = deniedDeviationItems.map((item) => ({ - ...item, - actionButton: ( - <> + cardLabelBoxActions: ( - handleMenuClose(`denied-${item.id}`)} - > - handleDeviationAction("accept", item)}> - - Accept - - handleDeviationAction("accept-customer-specific", item)}> - - Accept - Customer Specific - - - + ), + }; + }); + + // Add action buttons to denied deviation items + const deniedDeviationItemsWithActions = deniedDeviationItems.map((item) => ({ + ...item, + cardLabelBoxActions: ( + ), })); // Calculate compliance metrics for badges + // Accepted and Customer Specific deviations count as compliant since they are user-approved + // Denied deviations are included in total but not in compliant count (they haven't been fixed yet) const totalPolicies = processedDriftData.alignedCount + processedDriftData.currentDeviationsCount + processedDriftData.acceptedDeviationsCount + + processedDriftData.customerSpecificDeviations + + processedDriftData.deniedDeviationsCount; + + const compliantCount = + processedDriftData.alignedCount + + processedDriftData.acceptedDeviationsCount + processedDriftData.customerSpecificDeviations; + // Alignment Score: Only actual compliance (excluding license-missing items) const compliancePercentage = - totalPolicies > 0 ? Math.round((processedDriftData.alignedCount / totalPolicies) * 100) : 0; + totalPolicies > 0 ? Math.round((compliantCount / totalPolicies) * 100) : 0; - const missingLicensePercentage = 0; // This would need to be calculated from actual license data + // Calculate missing license percentage + const missingLicensePercentage = + totalPolicies > 0 ? Math.round((licenseSkippedItems.length / totalPolicies) * 100) : 0; + + // Total Score: Alignment + License Missing (represents addressable compliance) const combinedScore = compliancePercentage + missingLicensePercentage; + // Helper function to get category from standardName + const getCategory = (standardName) => { + if (!standardName) return "Other Standards"; + if (standardName.includes("ConditionalAccessTemplate")) return "Conditional Access Policies"; + if (standardName.includes("IntuneTemplate")) return "Intune Policies"; + + // For other standards, look up category in standards.json + const standard = standardsData.find((s) => s.name === standardName); + if (standard && standard.cat) { + return standard.cat; + } + + return "Other Standards"; + }; + + // Apply search and sort filters + const applyFilters = (items) => { + let filtered = [...items]; + + if (searchQuery) { + filtered = filtered.filter( + (item) => + item.text?.toLowerCase().includes(searchQuery.toLowerCase()) || + item.subtext?.toLowerCase().includes(searchQuery.toLowerCase()) || + item.standardName?.toLowerCase().includes(searchQuery.toLowerCase()), + ); + } + + if (sortBy === "name") { + filtered.sort((a, b) => (a.text || "").localeCompare(b.text || "")); + } else if (sortBy === "status") { + filtered.sort((a, b) => (a.statusText || "").localeCompare(b.statusText || "")); + } else if (sortBy === "category") { + // Sort by category, then by name within each category + filtered.sort((a, b) => { + const catA = getCategory(a.standardName); + const catB = getCategory(b.standardName); + if (catA !== catB) { + return catA.localeCompare(catB); + } + return (a.text || "").localeCompare(b.text || ""); + }); + } + + return filtered; + }; + + const filteredDeviationItems = applyFilters(deviationItemsWithActions); + const filteredAcceptedItems = applyFilters(acceptedDeviationItemsWithActions); + const filteredCustomerSpecificItems = applyFilters(customerSpecificDeviationItemsWithActions); + const filteredDeniedItems = applyFilters(deniedDeviationItemsWithActions); + const filteredAlignedItems = applyFilters(allAlignedItems); + const filteredLicenseSkippedItems = applyFilters(licenseSkippedItems); + + // Helper function to render items grouped by category when category sort is active + const renderItemsByCategory = (items) => { + if (sortBy !== "category" || items.length === 0) { + return ( + + ); + } + + // Group items by category and collect unique categories + const groupedItems = {}; + items.forEach((item) => { + const category = getCategory(item.standardName); + if (!groupedItems[category]) { + groupedItems[category] = []; + } + groupedItems[category].push(item); + }); + + // Sort categories alphabetically + const categories = Object.keys(groupedItems).sort(); + + return ( + + {categories.map((category) => { + if (groupedItems[category].length === 0) return null; + return ( + + + {category} + + + + ); + })} + + ); + }; + // Simple filter for drift templates const driftTemplateOptions = standardsApi.data ? standardsApi.data @@ -768,76 +1545,7 @@ const ManageDriftPage = () => { ? driftTemplateOptions.find((option) => option.value === templateId) || null : null; const title = "Manage Drift"; - const subtitle = [ - { - icon: , - text: ( - { - const query = { ...router.query }; - if (selectedTemplate && selectedTemplate.value) { - query.templateId = selectedTemplate.value; - } else { - delete query.templateId; - } - router.replace( - { - pathname: router.pathname, - query: query, - }, - undefined, - { shallow: true } - ); - }} - sx={{ minWidth: 300 }} - placeholder="Select a drift template..." - /> - ), - }, - // Add compliance badges when data is available - ...(totalPolicies > 0 - ? [ - { - component: ( - - - - - } - label={`${compliancePercentage}% Compliant`} - variant="outlined" - size="small" - color={ - compliancePercentage === 100 - ? "success" - : compliancePercentage >= 50 - ? "warning" - : "error" - } - /> - = 80 ? "success" : combinedScore >= 60 ? "warning" : "error" - } - /> - - ), - }, - ] - : []), - ]; + const subtitle = []; return ( { ) : ( - {/* Left side - Chart */} + {/* Left side - Alignment Score & Filters */} - + + {/* Stats Card */} + + + + + Aligned + + + + + + Current + + + + + + Accepted + + + + + + Customer Specific + + + + + + Denied + + + + + + Skipped (No License) + + + + + + + Total + + + + + + Alignment Score + + + + + + Total Score + + = 80 + ? "warning" + : combinedScore >= 30 + ? "warning" + : "error" + } + variant="outlined" + /> + + + + + {/* Filters Card */} + + + { + const query = { ...router.query }; + if (selectedTemplate && selectedTemplate.value) { + query.templateId = selectedTemplate.value; + } else { + delete query.templateId; + } + router.replace( + { + pathname: router.pathname, + query: query, + }, + undefined, + { shallow: true }, + ); + }} + placeholder="Select a drift template..." + /> + + setSearchQuery(e.target.value)} + /> + + + + setSortBy(newValue?.value || "name")} + multiple={false} + /> + + + {/* Right side - Deviation Management */} - + {/* Current Deviations Section */} - - {/* Header with bulk actions */} - - Current Deviations - - {/* Bulk Actions Dropdown */} - - setBulkActionsAnchorEl(null)} - > - handleBulkAction("accept-all-customer-specific")}> - - Accept All Deviations - Customer Specific - - handleBulkAction("accept-all")}> - - Accept All Deviations - - {/* Only show delete option if there are template deviations that support deletion */} - {processedDriftData.currentDeviations.some( - (deviation) => - (deviation.standardName?.includes("ConditionalAccessTemplate") || - deviation.standardName?.includes("IntuneTemplate")) && - deviation.expectedValue === - "This policy only exists in the tenant, not in the template." - ) && ( - handleBulkAction("deny-all-delete")}> - - Deny All Deviations - Delete - - )} - handleBulkAction("deny-all-remediate")}> - - Deny All Deviations - Remediate to align with template - - - - Remove Drift Customization - - + {(!filterStatus || + filterStatus.length === 0 || + filterStatus.some((f) => f.value === "all" || f.value === "current")) && ( + + {/* Header with bulk actions */} + + New Deviations + {selectedItems.length > 0 && ( + + {/* Bulk Actions Dropdown */} + + setBulkActionsAnchorEl(null)} + > + handleBulkAction("accept-all-customer-specific")} + > + + Accept All Deviations - Customer Specific + + handleBulkAction("accept-all")}> + + Accept All Deviations + + {/* Only show delete option if there are template deviations that support deletion */} + {processedDriftData.currentDeviations.some( + (deviation) => + (deviation.standardName?.includes("ConditionalAccessTemplate") || + deviation.standardName?.includes("IntuneTemplate")) && + deviation.expectedValue === + "This policy only exists in the tenant, not in the template.", + ) && ( + handleBulkAction("deny-all-delete")}> + + Deny All Deviations - Delete + + )} + handleBulkAction("deny-all-remediate")}> + + Deny All Deviations - Remediate to align with template + + + + Remove Drift Customization + + + + )} + {renderItemsByCategory(filteredDeviationItems)} - - + )} {/* Accepted Deviations Section */} - {acceptedDeviationItemsWithActions.length > 0 && ( - - - Accepted Deviations - - - - )} + {(!filterStatus || + filterStatus.length === 0 || + filterStatus.some((f) => f.value === "all" || f.value === "accepted")) && + filteredAcceptedItems.length > 0 && ( + + + Accepted Deviations + + {renderItemsByCategory(filteredAcceptedItems)} + + )} {/* Customer Specific Deviations Section */} - {customerSpecificDeviationItemsWithActions.length > 0 && ( - - - Accepted Deviations - Customer Specific - - - - )} + {(!filterStatus || + filterStatus.length === 0 || + filterStatus.some((f) => f.value === "all" || f.value === "customerspecific")) && + filteredCustomerSpecificItems.length > 0 && ( + + + Accepted Deviations - Customer Specific + + {renderItemsByCategory(filteredCustomerSpecificItems)} + + )} {/* Denied Deviations Section */} - {deniedDeviationItemsWithActions.length > 0 && ( + {(!filterStatus || + filterStatus.length === 0 || + filterStatus.some((f) => f.value === "all" || f.value === "denied")) && + filteredDeniedItems.length > 0 && ( + + + Denied Deviations + + {renderItemsByCategory(filteredDeniedItems)} + + )} + + {/* Compliant Standards Section - Only shown when filtered by All or Compliant */} + {(!filterStatus || + filterStatus.length === 0 || + filterStatus.some((f) => f.value === "all" || f.value === "compliant")) && + filteredAlignedItems.length > 0 && ( + + + Compliant Standards + + + + )} + + {/* License Skipped Section - Always at the end */} + {filteredLicenseSkippedItems.length > 0 && ( - Denied Deviations + Skipped - No License Available + + + These standards were skipped because the required licenses are not available + for this tenant. { api={{ url: "/api/ExecUpdateDriftDeviation", type: "POST", - data: { - deviations: "deviations", - }, + postEntireRow: true, confirmText: `Are you sure you'd like to ${actionData.action?.text || "update"} ${ actionData.action?.type === "single" ? "this deviation" : actionData.action?.type === "bulk" - ? `these ${actionData.action?.count || 0} deviations` - : actionData.action?.type === "reset" - ? "for this tenant" - : "this deviation" + ? `these ${actionData.action?.count || 0} deviations` + : actionData.action?.type === "reset" + ? "for this tenant" + : "this deviation" }?`, }} row={actionData.data} @@ -1038,6 +1955,179 @@ const ManageDriftPage = () => { /> )} + {/* Render all Menu components outside of card structure */} + {deviationItemsWithActions.map((item) => { + const supportsDelete = + (item.standardName?.includes("ConditionalAccessTemplate") || + item.standardName?.includes("IntuneTemplate")) && + item.expectedValue === "This policy only exists in the tenant, not in the template."; + return ( + handleMenuClose(item.id)} + > + { + handleDeviationAction("accept-customer-specific", item); + handleMenuClose(item.id); + }} + > + + Accept Deviation - Customer Specific + + { + handleDeviationAction("accept", item); + handleMenuClose(item.id); + }} + > + + Accept Deviation + + {supportsDelete && ( + { + handleDeviationAction("deny-delete", item); + handleMenuClose(item.id); + }} + > + + Deny Deviation - Delete Policy + + )} + { + handleDeviationAction("deny-remediate", item); + handleMenuClose(item.id); + }} + > + + Deny Deviation - Remediate to align with template + + + ); + })} + + {acceptedDeviationItemsWithActions.map((item) => { + const supportsDelete = + (item.standardName?.includes("ConditionalAccessTemplate") || + item.standardName?.includes("IntuneTemplate")) && + item.expectedValue === "This policy only exists in the tenant, not in the template."; + return ( + handleMenuClose(`accepted-${item.id}`)} + > + {supportsDelete && ( + { + handleDeviationAction("deny-delete", item); + handleMenuClose(`accepted-${item.id}`); + }} + > + + Deny - Delete Policy + + )} + { + handleDeviationAction("deny-remediate", item); + handleMenuClose(`accepted-${item.id}`); + }} + > + + Deny - Remediate to align with template + + { + handleDeviationAction("accept-customer-specific", item); + handleMenuClose(`accepted-${item.id}`); + }} + > + + Accept - Customer Specific + + + ); + })} + + {customerSpecificDeviationItemsWithActions.map((item) => { + const supportsDelete = + (item.standardName?.includes("ConditionalAccessTemplate") || + item.standardName?.includes("IntuneTemplate")) && + item.expectedValue === "This policy only exists in the tenant, not in the template."; + return ( + handleMenuClose(`customer-${item.id}`)} + > + {supportsDelete && ( + { + handleDeviationAction("deny-delete", item); + handleMenuClose(`customer-${item.id}`); + }} + > + + Deny - Delete + + )} + { + handleDeviationAction("deny-remediate", item); + handleMenuClose(`customer-${item.id}`); + }} + > + + Deny - Remediate to align with template + + { + handleDeviationAction("accept", item); + handleMenuClose(`customer-${item.id}`); + }} + > + + Accept + + + ); + })} + + {deniedDeviationItemsWithActions.map((item) => ( + handleMenuClose(`denied-${item.id}`)} + > + { + handleDeviationAction("accept", item); + handleMenuClose(`denied-${item.id}`); + }} + > + + Accept + + { + handleDeviationAction("accept-customer-specific", item); + handleMenuClose(`denied-${item.id}`); + }} + > + + Accept - Customer Specific + + + ))} + {/* Hidden ExecutiveReportButton that gets triggered programmatically */} { const router = useRouter(); diff --git a/src/pages/tenant/manage/history.js b/src/pages/tenant/manage/history.js index d0e37ec3a077..129fcad963b4 100644 --- a/src/pages/tenant/manage/history.js +++ b/src/pages/tenant/manage/history.js @@ -20,9 +20,9 @@ import { TimelineDot, TimelineOppositeContent, } from "@mui/lab"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { HeaderedTabbedLayout } from "/src/layouts/HeaderedTabbedLayout"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import { HeaderedTabbedLayout } from "../../../layouts/HeaderedTabbedLayout"; +import { ApiGetCall } from "../../../api/ApiCall"; import { useRouter } from "next/router"; import { Policy, diff --git a/src/pages/tenant/manage/policies-deployed.js b/src/pages/tenant/manage/policies-deployed.js index b6cca34dd67f..d38f68d03698 100644 --- a/src/pages/tenant/manage/policies-deployed.js +++ b/src/pages/tenant/manage/policies-deployed.js @@ -1,4 +1,4 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import { useRouter } from "next/router"; import { Policy, Security, AdminPanelSettings, Devices, ExpandMore } from "@mui/icons-material"; import { @@ -10,12 +10,12 @@ import { AccordionDetails, Chip, } from "@mui/material"; -import { HeaderedTabbedLayout } from "/src/layouts/HeaderedTabbedLayout"; +import { HeaderedTabbedLayout } from "../../../layouts/HeaderedTabbedLayout"; import tabOptions from "./tabOptions.json"; -import { CippDataTable } from "/src/components/CippTable/CippDataTable"; -import { CippHead } from "/src/components/CippComponents/CippHead"; -import { ApiGetCall } from "/src/api/ApiCall"; -import standardsData from "/src/data/standards.json"; +import { CippDataTable } from "../../../components/CippTable/CippDataTable"; +import { CippHead } from "../../../components/CippComponents/CippHead"; +import { ApiGetCall } from "../../../api/ApiCall"; +import standardsData from "../../../data/standards.json"; import { createDriftManagementActions } from "./driftManagementActions"; import { useSettings } from "../../../hooks/use-settings"; import { CippAutoComplete } from "../../../components/CippComponents/CippAutocomplete"; @@ -57,6 +57,12 @@ const PoliciesDeployedPage = () => { enabled: !!templateId && !!tenantFilter, }); + // API call to get all Intune templates for displayName lookup + const intuneTemplatesApi = ApiGetCall({ + url: "/api/ListIntuneTemplates", + queryKey: "ListIntuneTemplates", + }); + // Find the current template from standards data const currentTemplate = (standardsApi.data || []).find( (template) => template.GUID === templateId @@ -67,42 +73,67 @@ const PoliciesDeployedPage = () => { // Helper function to get status from comparison data with deviation status const getStatus = (standardKey, templateValue = null, templateType = null) => { const comparisonKey = `standards.${standardKey}`; - const value = comparisonData[comparisonKey]?.Value; + const comparisonItem = comparisonData[comparisonKey]; + const value = comparisonItem?.Value; + // If value is true, it's deployed and compliant if (value === true) { return "Deployed"; - } else { - // Check if there's drift data for this standard to get the deviation status - const driftData = Array.isArray(driftApi.data) ? driftApi.data : []; - - // For templates, we need to match against the full template path - let searchKeys = [standardKey, `standards.${standardKey}`]; - - // Add template-specific search keys - if (templateValue && templateType) { - searchKeys.push( - `standards.${templateType}.${templateValue}`, - `${templateType}.${templateValue}`, - templateValue - ); + } + + // Check if ExpectedValue and CurrentValue match (like drift.js does) + if (comparisonItem?.ExpectedValue && comparisonItem?.CurrentValue) { + try { + const expectedStr = JSON.stringify(comparisonItem.ExpectedValue); + const currentStr = JSON.stringify(comparisonItem.CurrentValue); + if (expectedStr === currentStr) { + return "Deployed"; + } + } catch (e) { + console.error("Error comparing values:", e); } + } + + // If value is explicitly false, it means not deployed (not a deviation) + if (value === false) { + return "Not Deployed"; + } - const deviation = driftData.find((item) => - searchKeys.some( - (key) => - item.standardName === key || - item.policyName === key || - item.standardName?.includes(key) || - item.policyName?.includes(key) - ) + // If value is null/undefined, check drift data for deviation status + const driftData = Array.isArray(driftApi.data) ? driftApi.data : []; + + // For templates, we need to match against the full template path + let searchKeys = [standardKey, `standards.${standardKey}`]; + + // Add template-specific search keys + if (templateValue && templateType) { + searchKeys.push( + `standards.${templateType}.${templateValue}`, + `${templateType}.${templateValue}`, + templateValue ); + } - if (deviation && deviation.Status) { - return `Deviation - ${deviation.Status}`; - } + const deviation = driftData.find((item) => + searchKeys.some( + (key) => + item.standardName === key || + item.policyName === key || + item.standardName?.includes(key) || + item.policyName?.includes(key) + ) + ); + if (deviation && deviation.Status) { + return `Deviation - ${deviation.Status}`; + } + + // Only return "Deviation - New" if we have comparison data but value is null + if (comparisonItem) { return "Deviation - New"; } + + return "Not Configured"; }; // Helper function to get display name from drift data @@ -131,7 +162,20 @@ const PoliciesDeployedPage = () => { ) ); - return deviation?.standardDisplayName || null; + // If found in drift data, return the display name + if (deviation?.standardDisplayName) { + return deviation.standardDisplayName; + } + + // If not found in drift data and this is an Intune template, look it up in the Intune templates API + if (templateType === "IntuneTemplate" && templateValue && intuneTemplatesApi.data) { + const template = intuneTemplatesApi.data.find((t) => t.GUID === templateValue); + if (template?.Displayname) { + return template.Displayname; + } + } + + return null; }; // Helper function to get last refresh date @@ -195,111 +239,145 @@ const PoliciesDeployedPage = () => { (templateStandards.IntuneTemplate || []).forEach((template, index) => { console.log("Processing IntuneTemplate in policies-deployed:", template); + // Check if this template has TemplateList-Tags (try both property formats) + const templateListTags = template["TemplateList-Tags"] || template.TemplateListTags; + // Check if this template has TemplateList-Tags and expand them - if ( - template["TemplateList-Tags"]?.value && - template["TemplateList-Tags"]?.addedFields?.templates - ) { + if (templateListTags?.value && templateListTags?.addedFields?.templates) { console.log( "Found TemplateList-Tags for IntuneTemplate in policies-deployed:", - template["TemplateList-Tags"] + templateListTags ); - console.log("Templates to expand:", template["TemplateList-Tags"].addedFields.templates); + console.log("Templates to expand:", templateListTags.addedFields.templates); // Expand TemplateList-Tags into multiple template items - template["TemplateList-Tags"].addedFields.templates.forEach( - (expandedTemplate, expandedIndex) => { - console.log("Expanding IntuneTemplate in policies-deployed:", expandedTemplate); - const standardKey = `IntuneTemplate.${expandedTemplate.GUID}`; - const driftDisplayName = getDisplayNameFromDrift( - standardKey, - expandedTemplate.GUID, - "IntuneTemplate" - ); - const packageTagName = template["TemplateList-Tags"].value; - const templateName = - expandedTemplate.displayName || expandedTemplate.name || "Unknown Template"; - - intunePolices.push({ - id: intunePolices.length + 1, - name: `${driftDisplayName || templateName} (via ${packageTagName})`, - category: "Intune Template", - platform: "Multi-Platform", - status: getStatus(standardKey, expandedTemplate.GUID, "IntuneTemplate"), - lastModified: getLastRefresh(standardKey), - assignedGroups: template.AssignTo || "N/A", - templateValue: expandedTemplate.GUID, - }); - } - ); + templateListTags.addedFields.templates.forEach((expandedTemplate, expandedIndex) => { + console.log("Expanding IntuneTemplate in policies-deployed:", expandedTemplate); + const standardKey = `IntuneTemplate.${expandedTemplate.GUID}`; + const driftDisplayName = getDisplayNameFromDrift( + standardKey, + expandedTemplate.GUID, + "IntuneTemplate" + ); + const packageTagName = templateListTags.value; + const templateName = + expandedTemplate.displayName || expandedTemplate.name || "Unknown Template"; + + intunePolices.push({ + id: intunePolices.length + 1, + name: `${driftDisplayName || templateName} (via ${packageTagName})`, + category: "Intune Template", + platform: "Multi-Platform", + status: getStatus(standardKey, expandedTemplate.GUID, "IntuneTemplate"), + lastModified: getLastRefresh(standardKey), + assignedGroups: template.AssignTo || "N/A", + templateValue: expandedTemplate.GUID, + }); + }); } else { // Regular TemplateList processing - const standardKey = `IntuneTemplate.${template.TemplateList?.value}`; - const driftDisplayName = getDisplayNameFromDrift( - standardKey, - template.TemplateList?.value, - "IntuneTemplate" - ); - const templateLabel = getTemplateLabel(template.TemplateList?.value, "IntuneTemplate"); + const templateGuid = template.TemplateList?.value; + const standardKey = `IntuneTemplate.${templateGuid}`; + const driftDisplayName = getDisplayNameFromDrift(standardKey, templateGuid, "IntuneTemplate"); + + // Try multiple fallbacks for the name + let templateName = driftDisplayName; + if (!templateName) { + const templateLabel = getTemplateLabel(templateGuid, "IntuneTemplate"); + if (templateLabel !== "Unknown Template") { + templateName = `Intune - ${templateLabel}`; + } + } + // If still no name, try looking up directly in intuneTemplatesApi by GUID + if (!templateName && templateGuid && intuneTemplatesApi.data) { + const intuneTemplate = intuneTemplatesApi.data.find((t) => t.GUID === templateGuid); + if (intuneTemplate?.Displayname) { + templateName = intuneTemplate.Displayname; + } + } + // Final fallback + if (!templateName) { + templateName = `Intune - ${templateGuid || "Unknown Template"}`; + } intunePolices.push({ id: intunePolices.length + 1, - name: driftDisplayName || `Intune - ${templateLabel}`, + name: templateName, category: "Intune Template", platform: "Multi-Platform", - status: getStatus(standardKey, template.TemplateList?.value, "IntuneTemplate"), + status: getStatus(standardKey, templateGuid, "IntuneTemplate"), lastModified: getLastRefresh(standardKey), assignedGroups: template.AssignTo || "N/A", - templateValue: template.TemplateList?.value, + templateValue: templateGuid, }); } }); + // Add any templates from comparison data that weren't in template standards (e.g., from tags) + // Check for IntuneTemplate entries in comparison data + Object.keys(comparisonData).forEach((key) => { + if (key.startsWith("standards.IntuneTemplate.")) { + const guid = key.replace("standards.IntuneTemplate.", ""); + // Check if this GUID is already in our list + const alreadyExists = intunePolices.some((p) => p.templateValue === guid); + if (!alreadyExists && comparisonData[key]?.Value === true) { + const standardKey = `IntuneTemplate.${guid}`; + const driftDisplayName = getDisplayNameFromDrift(standardKey, guid, "IntuneTemplate"); + + intunePolices.push({ + id: intunePolices.length + 1, + name: driftDisplayName || `Intune - ${guid}`, + category: "Intune Template", + platform: "Multi-Platform", + status: getStatus(standardKey, guid, "IntuneTemplate"), + lastModified: getLastRefresh(standardKey), + assignedGroups: "N/A", + templateValue: guid, + }); + } + } + }); + // Process Conditional Access Templates const conditionalAccessPolicies = []; (templateStandards.ConditionalAccessTemplate || []).forEach((template, index) => { console.log("Processing ConditionalAccessTemplate in policies-deployed:", template); + // Check if this template has TemplateList-Tags (try both property formats) + const templateListTags = template["TemplateList-Tags"] || template.TemplateListTags; + // Check if this template has TemplateList-Tags and expand them - if ( - template["TemplateList-Tags"]?.value && - template["TemplateList-Tags"]?.addedFields?.templates - ) { + if (templateListTags?.value && templateListTags?.addedFields?.templates) { console.log( "Found TemplateList-Tags for ConditionalAccessTemplate in policies-deployed:", - template["TemplateList-Tags"] + templateListTags ); - console.log("Templates to expand:", template["TemplateList-Tags"].addedFields.templates); + console.log("Templates to expand:", templateListTags.addedFields.templates); // Expand TemplateList-Tags into multiple template items - template["TemplateList-Tags"].addedFields.templates.forEach( - (expandedTemplate, expandedIndex) => { - console.log( - "Expanding ConditionalAccessTemplate in policies-deployed:", - expandedTemplate - ); - const standardKey = `ConditionalAccessTemplate.${expandedTemplate.GUID}`; - const driftDisplayName = getDisplayNameFromDrift( - standardKey, - expandedTemplate.GUID, - "ConditionalAccessTemplate" - ); - const packageTagName = template["TemplateList-Tags"].value; - const templateName = - expandedTemplate.displayName || expandedTemplate.name || "Unknown Template"; - - conditionalAccessPolicies.push({ - id: conditionalAccessPolicies.length + 1, - name: `${driftDisplayName || templateName} (via ${packageTagName})`, - state: template.state || "Unknown", - conditions: "Conditional Access Policy", - controls: "Access Control", - lastModified: getLastRefresh(standardKey), - status: getStatus(standardKey, expandedTemplate.GUID, "ConditionalAccessTemplate"), - templateValue: expandedTemplate.GUID, - }); - } - ); + templateListTags.addedFields.templates.forEach((expandedTemplate, expandedIndex) => { + console.log("Expanding ConditionalAccessTemplate in policies-deployed:", expandedTemplate); + const standardKey = `ConditionalAccessTemplate.${expandedTemplate.GUID}`; + const driftDisplayName = getDisplayNameFromDrift( + standardKey, + expandedTemplate.GUID, + "ConditionalAccessTemplate" + ); + const packageTagName = templateListTags.value; + const templateName = + expandedTemplate.displayName || expandedTemplate.name || "Unknown Template"; + + conditionalAccessPolicies.push({ + id: conditionalAccessPolicies.length + 1, + name: `${driftDisplayName || templateName} (via ${packageTagName})`, + state: template.state || "Unknown", + conditions: "Conditional Access Policy", + controls: "Access Control", + lastModified: getLastRefresh(standardKey), + status: getStatus(standardKey, expandedTemplate.GUID, "ConditionalAccessTemplate"), + templateValue: expandedTemplate.GUID, + }); + }); } else { // Regular TemplateList processing const standardKey = `ConditionalAccessTemplate.${template.TemplateList?.value}`; @@ -325,6 +403,35 @@ const PoliciesDeployedPage = () => { }); } }); + + // Add any CA templates from comparison data that weren't in template standards + Object.keys(comparisonData).forEach((key) => { + if (key.startsWith("standards.ConditionalAccessTemplate.")) { + const guid = key.replace("standards.ConditionalAccessTemplate.", ""); + // Check if this GUID is already in our list + const alreadyExists = conditionalAccessPolicies.some((p) => p.templateValue === guid); + if (!alreadyExists && comparisonData[key]?.Value === true) { + const standardKey = `ConditionalAccessTemplate.${guid}`; + const driftDisplayName = getDisplayNameFromDrift( + standardKey, + guid, + "ConditionalAccessTemplate" + ); + + conditionalAccessPolicies.push({ + id: conditionalAccessPolicies.length + 1, + name: driftDisplayName || `Conditional Access - ${guid}`, + state: "Unknown", + conditions: "Conditional Access Policy", + controls: "Access Control", + lastModified: getLastRefresh(standardKey), + status: getStatus(standardKey, guid, "ConditionalAccessTemplate"), + templateValue: guid, + }); + } + } + }); + // Simple filter for all templates (no type filtering) const templateOptions = standardsApi.data ? standardsApi.data.map((template) => ({ @@ -363,40 +470,7 @@ const PoliciesDeployedPage = () => { currentTenant, }); const title = "View Deployed Policies"; - const subtitle = [ - { - icon: , - text: ( - { - const query = { ...router.query }; - if (selectedTemplate && selectedTemplate.value) { - query.templateId = selectedTemplate.value; - } else { - delete query.templateId; - } - router.replace( - { - pathname: router.pathname, - query: query, - }, - undefined, - { shallow: true } - ); - }} - sx={{ minWidth: 300 }} - placeholder="Select a template..." - /> - ), - }, - ]; + const subtitle = []; return ( { > - + {/* Filters Section */} + + { + const query = { ...router.query }; + if (selectedTemplate && selectedTemplate.value) { + query.templateId = selectedTemplate.value; + } else { + delete query.templateId; + } + router.replace( + { + pathname: router.pathname, + query: query, + }, + undefined, + { shallow: true } + ); + }} + sx={{ width: 300 }} + placeholder="Select template..." + /> + + + {/* Standards Section */} }> diff --git a/src/pages/tenant/manage/recover-policies.js b/src/pages/tenant/manage/recover-policies.js index 98d10ec5208a..16a68a469186 100644 --- a/src/pages/tenant/manage/recover-policies.js +++ b/src/pages/tenant/manage/recover-policies.js @@ -1,4 +1,4 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import { useRouter } from "next/router"; import { Policy, Restore, ExpandMore } from "@mui/icons-material"; import { @@ -14,15 +14,15 @@ import { import { Grid } from "@mui/system"; import { useState } from "react"; import { useForm } from "react-hook-form"; -import { HeaderedTabbedLayout } from "/src/layouts/HeaderedTabbedLayout"; +import { HeaderedTabbedLayout } from "../../../layouts/HeaderedTabbedLayout"; import tabOptions from "./tabOptions.json"; -import { CippDataTable } from "/src/components/CippTable/CippDataTable"; -import { CippHead } from "/src/components/CippComponents/CippHead"; -import { CippFormComponent } from "/src/components/CippComponents/CippFormComponent"; -import { ApiPostCall } from "/src/api/ApiCall"; -import { CippApiResults } from "/src/components/CippComponents/CippApiResults"; +import { CippDataTable } from "../../../components/CippTable/CippDataTable"; +import { CippHead } from "../../../components/CippComponents/CippHead"; +import { CippFormComponent } from "../../../components/CippComponents/CippFormComponent"; +import { ApiPostCall } from "../../../api/ApiCall"; +import { CippApiResults } from "../../../components/CippComponents/CippApiResults"; import { createDriftManagementActions } from "./driftManagementActions"; -import { useSettings } from "/src/hooks/use-settings"; +import { useSettings } from "../../../hooks/use-settings"; const RecoverPoliciesPage = () => { const router = useRouter(); diff --git a/src/pages/tenant/manage/user-defaults.js b/src/pages/tenant/manage/user-defaults.js index 85cccf57c0e3..96b9810a4ad9 100644 --- a/src/pages/tenant/manage/user-defaults.js +++ b/src/pages/tenant/manage/user-defaults.js @@ -1,11 +1,11 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import { TabbedLayout } from "../../../layouts/TabbedLayout"; +import { CippTablePage } from "../../../components/CippComponents/CippTablePage.jsx"; import { Button } from "@mui/material"; import { Delete, Add } from "@mui/icons-material"; import { useDialog } from "../../../hooks/use-dialog"; import { CippApiDialog } from "../../../components/CippComponents/CippApiDialog"; -import countryList from "/src/data/countryList.json"; +import countryList from "../../../data/countryList.json"; import tabOptions from "./tabOptions.json"; import { useSettings } from "../../../hooks/use-settings"; diff --git a/src/pages/tenant/reports/application-consent/index.js b/src/pages/tenant/reports/application-consent/index.js index c6524f8345e6..3270740aa766 100644 --- a/src/pages/tenant/reports/application-consent/index.js +++ b/src/pages/tenant/reports/application-consent/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; const simpleColumns = ["Tenant", "Name", "ApplicationID", "ObjectID", "Scope", "StartTime"]; diff --git a/src/pages/tenant/reports/index.js b/src/pages/tenant/reports/index.js index 095383995762..cb5872a4486b 100644 --- a/src/pages/tenant/reports/index.js +++ b/src/pages/tenant/reports/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; const Page = () => { const pageTitle = "Reports"; diff --git a/src/pages/tenant/reports/list-csp-licenses/index.jsx b/src/pages/tenant/reports/list-csp-licenses/index.jsx index e23c22114264..6cf2cacd9f44 100644 --- a/src/pages/tenant/reports/list-csp-licenses/index.jsx +++ b/src/pages/tenant/reports/list-csp-licenses/index.jsx @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import { MinusIcon, PlusIcon } from "@heroicons/react/24/outline"; import { DeleteForever, ShoppingCart } from "@mui/icons-material"; import { Button } from "@mui/material"; diff --git a/src/pages/tenant/reports/list-licenses/index.js b/src/pages/tenant/reports/list-licenses/index.js index c4de2000b4f8..35d61ea16158 100644 --- a/src/pages/tenant/reports/list-licenses/index.js +++ b/src/pages/tenant/reports/list-licenses/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; const Page = () => { const pageTitle = "Licences Report"; diff --git a/src/pages/tenant/standards/alignment/index.js b/src/pages/tenant/standards/alignment/index.js index 6ddd7a186223..1e7470d2dd44 100644 --- a/src/pages/tenant/standards/alignment/index.js +++ b/src/pages/tenant/standards/alignment/index.js @@ -1,6 +1,6 @@ -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { TabbedLayout } from "/src/layouts/TabbedLayout"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { TabbedLayout } from "../../../../layouts/TabbedLayout"; import { Delete, Add } from "@mui/icons-material"; import { EyeIcon } from "@heroicons/react/24/outline"; import tabOptions from "../tabOptions.json"; diff --git a/src/pages/tenant/standards/bpa-report/builder.js b/src/pages/tenant/standards/bpa-report/builder.js index 5fe95c9b0691..3e7cf6f56475 100644 --- a/src/pages/tenant/standards/bpa-report/builder.js +++ b/src/pages/tenant/standards/bpa-report/builder.js @@ -1,4 +1,4 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { Box, Container, Typography, Button, IconButton, Stack, SvgIcon } from "@mui/material"; import { Grid } from "@mui/system"; import { useEffect, useState } from "react"; diff --git a/src/pages/tenant/standards/bpa-report/index.js b/src/pages/tenant/standards/bpa-report/index.js index 1c98aad23b69..4f88b19b630c 100644 --- a/src/pages/tenant/standards/bpa-report/index.js +++ b/src/pages/tenant/standards/bpa-report/index.js @@ -1,13 +1,13 @@ import { Button } from "@mui/material"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; // had to add an extra path here because I added an extra folder structure. We should switch to absolute pathing so we dont have to deal with relative. +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; // had to add an extra path here because I added an extra folder structure. We should switch to absolute pathing so we dont have to deal with relative. import Link from "next/link"; import { EyeIcon } from "@heroicons/react/24/outline"; import { CopyAll, Delete, Edit, AddBox, GitHub, Sync } from "@mui/icons-material"; -import { ApiGetCall } from "/src/api/ApiCall"; +import { ApiGetCall } from "../../../../api/ApiCall"; import { Stack } from "@mui/system"; -import { BPASyncDialog } from "/src/components/CippComponents/BPASyncDialog"; -import { useDialog } from "/src/hooks/use-dialog"; +import { BPASyncDialog } from "../../../../components/CippComponents/BPASyncDialog"; +import { useDialog } from "../../../../hooks/use-dialog"; const Page = () => { const pageTitle = "Best Practice Reports"; diff --git a/src/pages/tenant/standards/bpa-report/view.js b/src/pages/tenant/standards/bpa-report/view.js index 0af8c51a7011..f85fb633a3a3 100644 --- a/src/pages/tenant/standards/bpa-report/view.js +++ b/src/pages/tenant/standards/bpa-report/view.js @@ -1,4 +1,4 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { Box, Container, Typography, Button, Stack, SvgIcon, Skeleton, Chip, Alert } from "@mui/material"; import { Grid } from "@mui/system"; import Head from "next/head"; diff --git a/src/pages/tenant/standards/domains-analyser/index.js b/src/pages/tenant/standards/domains-analyser/index.js index 5f5d15186ae1..58ee11a13fb4 100644 --- a/src/pages/tenant/standards/domains-analyser/index.js +++ b/src/pages/tenant/standards/domains-analyser/index.js @@ -1,6 +1,6 @@ import { Button } from "@mui/material"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; // had to add an extra path here because I added an extra folder structure. We should switch to absolute pathing so we dont have to deal with relative. +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; // had to add an extra path here because I added an extra folder structure. We should switch to absolute pathing so we dont have to deal with relative. import Link from "next/link"; import { ApiGetCall } from "../../../../api/ApiCall"; import { useSettings } from "../../../../hooks/use-settings"; diff --git a/src/pages/tenant/standards/templates/index.js b/src/pages/tenant/standards/templates/index.js index 6b3c7c483f87..b23752c50d9b 100644 --- a/src/pages/tenant/standards/templates/index.js +++ b/src/pages/tenant/standards/templates/index.js @@ -1,7 +1,7 @@ import { Alert, Button } from "@mui/material"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; // had to add an extra path here because I added an extra folder structure. We should switch to absolute pathing so we dont have to deal with relative. -import { TabbedLayout } from "/src/layouts/TabbedLayout"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; // had to add an extra path here because I added an extra folder structure. We should switch to absolute pathing so we dont have to deal with relative. +import { TabbedLayout } from "../../../../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"; @@ -9,9 +9,9 @@ import { Grid } from "@mui/system"; 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 { useSettings } from "../../../../hooks/use-settings.js"; import { CippPolicyImportDrawer } from "../../../../components/CippComponents/CippPolicyImportDrawer.jsx"; -import { PermissionButton } from "/src/utils/permissions.js"; +import { PermissionButton } from "../../../../utils/permissions.js"; const Page = () => { const oldStandards = ApiGetCall({ url: "/api/ListStandards", queryKey: "ListStandards-legacy" }); diff --git a/src/pages/tenant/standards/templates/template.jsx b/src/pages/tenant/standards/templates/template.jsx index 3263cf6a6720..f7dd53861b02 100644 --- a/src/pages/tenant/standards/templates/template.jsx +++ b/src/pages/tenant/standards/templates/template.jsx @@ -1,11 +1,11 @@ import { Box, Button, Container, Stack, Typography, SvgIcon, Skeleton } from "@mui/material"; import { Grid } from "@mui/system"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useWatch } from "react-hook-form"; import { useRouter } from "next/router"; import { Add, SaveRounded } from "@mui/icons-material"; import { useEffect, useState, useCallback, useMemo, useRef, lazy, Suspense } from "react"; -import standards from "/src/data/standards"; +import standards from "../../../../data/standards"; import CippStandardAccordion from "../../../../components/CippStandards/CippStandardAccordion"; // Lazy load the dialog to improve initial page load performance const CippStandardDialog = lazy(() => @@ -17,8 +17,8 @@ import { useDialog } from "../../../../hooks/use-dialog"; import { ApiGetCall } from "../../../../api/ApiCall"; import _ from "lodash"; import { createDriftManagementActions } from "../../manage/driftManagementActions"; -import { ActionsMenu } from "/src/components/actions-menu"; -import { useSettings } from "/src/hooks/use-settings"; +import { ActionsMenu } from "../../../../components/actions-menu"; +import { useSettings } from "../../../../hooks/use-settings"; import { CippHead } from "../../../../components/CippComponents/CippHead"; const Page = () => { diff --git a/src/pages/tenant/tools/appapproval/index.js b/src/pages/tenant/tools/appapproval/index.js index a18484cc85ce..b05b6938415d 100644 --- a/src/pages/tenant/tools/appapproval/index.js +++ b/src/pages/tenant/tools/appapproval/index.js @@ -1,7 +1,7 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippWizardConfirmation } from "/src/components/CippWizard/CippWizardConfirmation"; -import CippWizardPage from "/src/components/CippWizard/CippWizardPage.jsx"; -import { CippTenantStep } from "/src/components/CippWizard/CippTenantStep.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippWizardConfirmation } from "../../../../components/CippWizard/CippWizardConfirmation"; +import CippWizardPage from "../../../../components/CippWizard/CippWizardPage.jsx"; +import { CippTenantStep } from "../../../../components/CippWizard/CippTenantStep.jsx"; import { CippWizardAppApproval } from "../../../../components/CippWizard/CippWizardAppApproval"; import { Alert } from "@mui/material"; diff --git a/src/pages/tenant/tools/bpa-report-builder/index.js b/src/pages/tenant/tools/bpa-report-builder/index.js index 082890d4c7f8..337f594354ee 100644 --- a/src/pages/tenant/tools/bpa-report-builder/index.js +++ b/src/pages/tenant/tools/bpa-report-builder/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; const Page = () => { const pageTitle = "BPA Report Builder"; diff --git a/src/pages/tenant/tools/geoiplookup/index.js b/src/pages/tenant/tools/geoiplookup/index.js index 28f93d377573..162e93a3b5a1 100644 --- a/src/pages/tenant/tools/geoiplookup/index.js +++ b/src/pages/tenant/tools/geoiplookup/index.js @@ -1,6 +1,6 @@ import { Box, Button, Container } from "@mui/material"; import { Grid, Stack } from "@mui/system"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useWatch } from "react-hook-form"; import CippButtonCard from "../../../../components/CippCards/CippButtonCard"; import { Add, Delete, Search } from "@mui/icons-material"; diff --git a/src/pages/tenant/tools/graph-explorer/index.js b/src/pages/tenant/tools/graph-explorer/index.js index 0ef2f470f585..2ba20baa9332 100644 --- a/src/pages/tenant/tools/graph-explorer/index.js +++ b/src/pages/tenant/tools/graph-explorer/index.js @@ -1,9 +1,9 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; +import { CippTablePage } from "../../../../components/CippComponents/CippTablePage.jsx"; import CippGraphExplorerFilter from "../../../../components/CippTable/CippGraphExplorerFilter"; import { useState } from "react"; import { Grid } from "@mui/system"; -import { useSettings } from "/src/hooks/use-settings"; +import { useSettings } from "../../../../hooks/use-settings"; const Page = () => { const [apiFilter, setApiFilter] = useState([]); diff --git a/src/pages/tenant/tools/individual-domains/index.js b/src/pages/tenant/tools/individual-domains/index.js index d6de4f16eeef..99f7a4157dce 100644 --- a/src/pages/tenant/tools/individual-domains/index.js +++ b/src/pages/tenant/tools/individual-domains/index.js @@ -1,5 +1,5 @@ import { Box, Container } from "@mui/material"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { CippDomainCards } from "../../../../components/CippCards/CippDomainCards"; const Page = () => { diff --git a/src/pages/tenant/tools/tenantlookup/index.js b/src/pages/tenant/tools/tenantlookup/index.js index 1678f428f818..aea206a02e98 100644 --- a/src/pages/tenant/tools/tenantlookup/index.js +++ b/src/pages/tenant/tools/tenantlookup/index.js @@ -1,6 +1,6 @@ import { Box, Button, Container, Typography, Skeleton, Link } from "@mui/material"; import { Grid } from "@mui/system"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../../layouts/index.js"; import { useForm, useWatch } from "react-hook-form"; import CippButtonCard from "../../../../components/CippCards/CippButtonCard"; import { Search } from "@mui/icons-material"; @@ -84,7 +84,11 @@ const Page = () => { Tenant Brand Name :{" "} {getTenant.data?.GraphRequest?.federationBrandName ? getTenant.data?.GraphRequest?.federationBrandName - : "N/A"} + : "Not Specified"} + + + Tenant Region:{" "} + {getTenant.data?.OpenIdConfig?.tenant_region_scope} diff --git a/src/pages/tools/breachlookup/index.js b/src/pages/tools/breachlookup/index.js index 53d9e62c071f..04aed11e72ed 100644 --- a/src/pages/tools/breachlookup/index.js +++ b/src/pages/tools/breachlookup/index.js @@ -10,17 +10,17 @@ import { Alert, } from "@mui/material"; import { Grid } from "@mui/system"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; import { useForm, useWatch } from "react-hook-form"; -import CippButtonCard from "/src/components/CippCards/CippButtonCard"; +import CippButtonCard from "../../../components/CippCards/CippButtonCard"; import { Search } from "@mui/icons-material"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { ApiGetCall } from "/src/api/ApiCall"; +import CippFormComponent from "../../../components/CippComponents/CippFormComponent"; +import { ApiGetCall } from "../../../api/ApiCall"; import DOMPurify from "dompurify"; -import { getCippTranslation } from "/src/utils/get-cipp-translation"; +import { getCippTranslation } from "../../../utils/get-cipp-translation"; import { useRouter } from "next/router"; import { useEffect } from "react"; -import CippCsvExportButton from "/src/components/CippComponents/CippCsvExportButton"; +import CippCsvExportButton from "../../../components/CippComponents/CippCsvExportButton"; import { CippCopyToClipBoard } from "../../../components/CippComponents/CippCopyToClipboard"; const Page = () => { diff --git a/src/pages/tools/community-repos/index.js b/src/pages/tools/community-repos/index.js index beadf2237bf1..1d885b8647da 100644 --- a/src/pages/tools/community-repos/index.js +++ b/src/pages/tools/community-repos/index.js @@ -1,6 +1,6 @@ import { useState } from "react"; -import { Layout as DashboardLayout } from "/src/layouts"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../layouts"; +import { CippTablePage } from "../../../components/CippComponents/CippTablePage.jsx"; import { Button, Dialog, @@ -21,14 +21,14 @@ import { SvgIcon, } from "@mui/material"; import { MagnifyingGlassIcon, TrashIcon } from "@heroicons/react/24/outline"; -import { ApiPostCall } from "/src/api/ApiCall"; +import { ApiPostCall } from "../../../api/ApiCall"; import { useForm, FormProvider } from "react-hook-form"; import { Radio, RadioGroup, FormControlLabel } from "@mui/material"; -import { CippFormCondition } from "/src/components/CippComponents/CippFormCondition"; +import { CippFormCondition } from "../../../components/CippComponents/CippFormCondition"; import AddIcon from "@mui/icons-material/Add"; import { Box } from "@mui/system"; import { Add, AddBox, Close, ForkLeft, OpenInNew } from "@mui/icons-material"; -import { CippApiResults } from "/src/components/CippComponents/CippApiResults"; +import { CippApiResults } from "../../../components/CippComponents/CippApiResults"; import CippFormComponent from "../../../components/CippComponents/CippFormComponent"; import { ApiGetCall } from "../../../api/ApiCall"; diff --git a/src/pages/tools/community-repos/repo.js b/src/pages/tools/community-repos/repo.js index 4fdb8ccd2849..96635f694393 100644 --- a/src/pages/tools/community-repos/repo.js +++ b/src/pages/tools/community-repos/repo.js @@ -1,8 +1,8 @@ import { useRouter } from "next/router"; -import { Layout as DashboardLayout } from "/src/layouts"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../layouts"; +import { CippTablePage } from "../../../components/CippComponents/CippTablePage.jsx"; import { useState, useEffect } from "react"; -import { ApiPostCall, ApiGetCall } from "/src/api/ApiCall"; +import { ApiPostCall, ApiGetCall } from "../../../api/ApiCall"; import { Button, Dialog, @@ -13,9 +13,9 @@ import { Skeleton, } from "@mui/material"; import { Grid } from "@mui/system"; -import CippJSONView from "/src/components/CippFormPages/CippJSONView"; +import CippJSONView from "../../../components/CippFormPages/CippJSONView"; import { EyeIcon } from "@heroicons/react/24/outline"; -import { CippAutoComplete } from "/src/components/CippComponents/CippAutocomplete"; +import { CippAutoComplete } from "../../../components/CippComponents/CippAutocomplete"; import React from "react"; import { CloudDownload } from "@mui/icons-material"; diff --git a/src/pages/tools/templatelib/index.jsx b/src/pages/tools/templatelib/index.jsx index 9b2fabb67bba..2822287dfa28 100644 --- a/src/pages/tools/templatelib/index.jsx +++ b/src/pages/tools/templatelib/index.jsx @@ -1,10 +1,10 @@ import { useEffect } from "react"; import { Divider, Typography, Alert, Chip } from "@mui/material"; import { useForm, useWatch } from "react-hook-form"; -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import CippFormPage from "/src/components/CippFormPages/CippFormPage"; -import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; -import { useSettings } from "/src/hooks/use-settings"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import CippFormPage from "../../../components/CippFormPages/CippFormPage"; +import CippFormComponent from "../../../components/CippComponents/CippFormComponent"; +import { useSettings } from "../../../hooks/use-settings"; import { CippFormTenantSelector } from "../../../components/CippComponents/CippFormTenantSelector"; import { Grid } from "@mui/system"; import { CippFormCondition } from "../../../components/CippComponents/CippFormCondition"; diff --git a/src/pages/tools/tenantbreachlookup/index.js b/src/pages/tools/tenantbreachlookup/index.js index 50b351e0e091..f1b59d8b663c 100644 --- a/src/pages/tools/tenantbreachlookup/index.js +++ b/src/pages/tools/tenantbreachlookup/index.js @@ -1,5 +1,5 @@ -import { Layout as DashboardLayout } from "/src/layouts/index.js"; -import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import { CippTablePage } from "../../../components/CippComponents/CippTablePage.jsx"; import { EyeIcon } from "@heroicons/react/24/outline"; import { Button } from "@mui/material"; import { Search } from "@mui/icons-material"; diff --git a/src/pages/unauthenticated.js b/src/pages/unauthenticated.js index 5a1d385a4c4c..a544ff4c5aa4 100644 --- a/src/pages/unauthenticated.js +++ b/src/pages/unauthenticated.js @@ -22,9 +22,7 @@ const Page = () => { // Use useMemo to derive userRoles directly const userRoles = useMemo(() => { if (orgData.isSuccess && orgData.data?.clientPrincipal?.userRoles) { - return orgData.data.clientPrincipal.userRoles.filter( - (role) => !blockedRoles.includes(role) - ); + return orgData.data.clientPrincipal.userRoles.filter((role) => !blockedRoles.includes(role)); } return []; }, [orgData.isSuccess, orgData.data?.clientPrincipal?.userRoles]); @@ -54,7 +52,10 @@ const Page = () => { 0 diff --git a/src/utils/get-cipp-formatting.js b/src/utils/get-cipp-formatting.js index 36eb740bbe15..b9a321e1cc91 100644 --- a/src/utils/get-cipp-formatting.js +++ b/src/utils/get-cipp-formatting.js @@ -181,9 +181,11 @@ export const getCippFormatting = (data, cellName, type, canReceive, flatten = tr "NotBefore", "NotAfter", "latestDataCollection", + "requestDate", // App Consent Requests + "reviewedDate", // App Consent Requests ]; - const matchDateTime = /([dD]ate[tT]ime|[Ee]xpiration)/; + const matchDateTime = /([dD]ate[tT]ime|[Ee]xpiration|[Tt]imestamp)/; if (timeAgoArray.includes(cellName) || matchDateTime.test(cellName)) { return isText && canReceive === false ? ( new Date(data).toLocaleString() // This runs if canReceive is false and isText is true @@ -247,14 +249,14 @@ export const getCippFormatting = (data, cellName, type, canReceive, flatten = tr unit === "d" ? "day" : unit === "h" - ? "hour" - : unit === "w" - ? "week" - : unit === "m" - ? "minutes" - : unit === "y" - ? "year" - : unit; + ? "hour" + : unit === "w" + ? "week" + : unit === "m" + ? "minutes" + : unit === "y" + ? "year" + : unit; return isText ? `Every ${value} ${unitText}` : `Every ${value} ${unitText}`; } } @@ -350,7 +352,7 @@ export const getCippFormatting = (data, cellName, type, canReceive, flatten = tr icon: icon, key: key, }; - }) + }), ); } else { // Handle null/undefined single element @@ -457,7 +459,7 @@ export const getCippFormatting = (data, cellName, type, canReceive, flatten = tr : renderChipList( data .filter((item) => item) - .map((item) => (typeof item === "object" && item?.label ? item.label : item)) + .map((item) => (typeof item === "object" && item?.label ? item.label : item)), ); } } @@ -498,12 +500,12 @@ export const getCippFormatting = (data, cellName, type, canReceive, flatten = tr normalized === "enabled" ? "Enabled" : normalized === "disabled" - ? "Disabled" - : normalized === "enabledforreportingbutnotenforced" || - normalized === "report-only" || - normalized === "reportonly" - ? "Report Only" - : data.charAt(0).toUpperCase() + data.slice(1); + ? "Disabled" + : normalized === "enabledforreportingbutnotenforced" || + normalized === "report-only" || + normalized === "reportonly" + ? "Report Only" + : data.charAt(0).toUpperCase() + data.slice(1); if (isText) { return label; @@ -559,8 +561,8 @@ export const getCippFormatting = (data, cellName, type, canReceive, flatten = tr const accessRights = Array.isArray(data) ? data.flatMap((item) => (typeof item === "string" ? item.split(", ") : [])) : typeof data === "string" - ? data.split(", ") - : []; + ? data.split(", ") + : []; return isText ? accessRights.join(", ") : renderChipList(accessRights); } @@ -633,6 +635,34 @@ export const getCippFormatting = (data, cellName, type, canReceive, flatten = tr ); } + //handle assignedUsers + if (cellName === "AssignedUsers" || cellName === "assignedUsers") { + //show the display name in text. otherwise, just return the obj. + return isText ? ( + Array.isArray(data) ? ( + data.map((user) => user.displayName).join(",") + ) : ( + data.displayName + ) + ) : ( + + ); + } + + // handle assignedGroups + if (cellName === "AssignedGroups" || cellName === "assignedGroups") { + //show the display name in text. otherwise, just return the obj. + return isText ? ( + Array.isArray(data) ? ( + data.map((group) => group.displayName).join(",") + ) : ( + data.displayName + ) + ) : ( + + ); + } + // Handle assigned licenses if (cellName === "assignedLicenses") { var translatedLicenses = getCippLicenseTranslation(data); @@ -641,8 +671,37 @@ export const getCippFormatting = (data, cellName, type, canReceive, flatten = tr ? translatedLicenses.join(", ") : translatedLicenses : Array.isArray(translatedLicenses) - ? renderChipList(translatedLicenses) - : translatedLicenses; + ? renderChipList(translatedLicenses) + : translatedLicenses; + } + + // Handle license assignment states + if (cellName === "licenseAssignmentStates") { + if (!Array.isArray(data) || data.length === 0) { + return []; + } + + // Transform the array to replace skuId with translated name and remove disabledPlans + const transformedData = data.map((license) => { + const translatedLicense = getCippLicenseTranslation([license]); + const licenseName = Array.isArray(translatedLicense) + ? translatedLicense[0] + : translatedLicense; + + // Return new object with skuId replaced by License and without disabledPlans + const { skuId, disabledPlans, ...rest } = license; + return { + License: licenseName, + ...rest, + }; + }); + + // Render as a table + return isText ? ( + JSON.stringify(transformedData) + ) : ( + + ); } if (cellName === "unifiedRoles") { @@ -796,6 +855,38 @@ export const getCippFormatting = (data, cellName, type, canReceive, flatten = tr /> ); } + if (cellName === "Status" || cellName === "Risk" || cellName === "UserImpact") { + let color = "default"; + let label = data; + + switch (data.toLowerCase()) { + case "success": + color = "success"; + break; + case "passed": + color = "success"; + break; + case "failed": + case "high": + color = "error"; + break; + case "in progress": + color = "info"; + break; + case "not started": + color = "default"; + break; + case "investigate": + case "medium": + case "warning": + case "skipped": + color = "warning"; + break; + default: + color = "default"; + } + return isText ? label : ; + } // ISO 8601 Duration Formatting // Add property names here to automatically format ISO 8601 duration strings (e.g., "PT1H23M30S") @@ -815,7 +906,7 @@ export const getCippFormatting = (data, cellName, type, canReceive, flatten = tr }, { fallbackLocale: "en", - } + }, ); const duration = isoDuration(data); return duration.humanize("en"); @@ -870,7 +961,7 @@ export const getCippFormatting = (data, cellName, type, canReceive, flatten = tr return { label: item.label, }; - }) + }), ); } diff --git a/src/utils/get-cipp-signin-errorcode-translation.js b/src/utils/get-cipp-signin-errorcode-translation.js index 00f0074e05f5..b73e73aeac52 100644 --- a/src/utils/get-cipp-signin-errorcode-translation.js +++ b/src/utils/get-cipp-signin-errorcode-translation.js @@ -1,4 +1,4 @@ -import SignInErrorCodes from "/src/data/signinErrorCodes"; +import SignInErrorCodes from "../data/signinErrorCodes"; export const getSignInErrorCodeTranslation = (errorCode) => { if (SignInErrorCodes.hasOwnProperty(String(errorCode))) { diff --git a/src/utils/permissions.js b/src/utils/permissions.js index df66872101d4..46e6a539864a 100644 --- a/src/utils/permissions.js +++ b/src/utils/permissions.js @@ -1,5 +1,5 @@ import Button from "@mui/material/Button"; -import { usePermissions } from "/src/hooks/use-permissions.js"; +import { usePermissions } from "../hooks/use-permissions.js"; /** * Permission Helper Utilities * diff --git a/yarn.lock b/yarn.lock index e1d176d44d7a..6f8601e5e565 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1325,10 +1325,10 @@ dependencies: "@monaco-editor/loader" "^1.5.0" -"@mui/core-downloads-tracker@^7.3.2": - version "7.3.6" - resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.6.tgz#e7e3a4dc161a377be8224aa988410e89571ab40a" - integrity sha512-QaYtTHlr8kDFN5mE1wbvVARRKH7Fdw1ZuOjBJcFdVpfNfRYKF3QLT4rt+WaB6CKJvpqxRsmEo0kpYinhH5GeHg== +"@mui/core-downloads-tracker@^7.3.7": + version "7.3.7" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.7.tgz#99d9c60be3ce5632ec915b2c287682020ce19a99" + integrity sha512-8jWwS6FweMkpyRkrJooamUGe1CQfO1yJ+lM43IyUJbrhHW/ObES+6ry4vfGi8EKaldHL3t3BG1bcLcERuJPcjg== "@mui/icons-material@7.3.2": version "7.3.2" @@ -1349,25 +1349,25 @@ clsx "^2.1.1" prop-types "^15.8.1" -"@mui/material@7.3.2": - version "7.3.2" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-7.3.2.tgz#21ad66bba695e2cd36e4a93e2e4ff5e04d8636a1" - integrity sha512-qXvbnawQhqUVfH1LMgMaiytP+ZpGoYhnGl7yYq2x57GYzcFL/iPzSZ3L30tlbwEjSVKNYcbiKO8tANR1tadjUg== +"@mui/material@7.3.7": + version "7.3.7" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-7.3.7.tgz#50fc9b9f8645a4d26a48d7c5f7fa0c9876a8c679" + integrity sha512-6bdIxqzeOtBAj2wAsfhWCYyMKPLkRO9u/2o5yexcL0C3APqyy91iGSWgT3H7hg+zR2XgE61+WAu12wXPON8b6A== dependencies: - "@babel/runtime" "^7.28.3" - "@mui/core-downloads-tracker" "^7.3.2" - "@mui/system" "^7.3.2" - "@mui/types" "^7.4.6" - "@mui/utils" "^7.3.2" + "@babel/runtime" "^7.28.4" + "@mui/core-downloads-tracker" "^7.3.7" + "@mui/system" "^7.3.7" + "@mui/types" "^7.4.10" + "@mui/utils" "^7.3.7" "@popperjs/core" "^2.11.8" "@types/react-transition-group" "^4.4.12" clsx "^2.1.1" - csstype "^3.1.3" + csstype "^3.2.3" prop-types "^15.8.1" - react-is "^19.1.1" + react-is "^19.2.3" react-transition-group "^4.4.5" -"@mui/private-theming@^7.3.2", "@mui/private-theming@^7.3.6": +"@mui/private-theming@^7.3.2": version "7.3.6" resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-7.3.6.tgz#1ca65a08e8f7f538d9a10ba974f1f4db5231a969" integrity sha512-Ws9wZpqM+FlnbZXaY/7yvyvWQo1+02Tbx50mVdNmzWEi51C51y56KAbaDCYyulOOBL6BJxuaqG8rNNuj7ivVyw== @@ -1376,7 +1376,16 @@ "@mui/utils" "^7.3.6" prop-types "^15.8.1" -"@mui/styled-engine@^7.3.2", "@mui/styled-engine@^7.3.6": +"@mui/private-theming@^7.3.7": + version "7.3.7" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-7.3.7.tgz#f5b41d573df3824fbfd10a7e6ac8de94bbcf15c5" + integrity sha512-w7r1+CYhG0syCAQUWAuV5zSaU2/67WA9JXUderdb7DzCIJdp/5RmJv6L85wRjgKCMsxFF0Kfn0kPgPbPgw/jdw== + dependencies: + "@babel/runtime" "^7.28.4" + "@mui/utils" "^7.3.7" + prop-types "^15.8.1" + +"@mui/styled-engine@^7.3.2": version "7.3.6" resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-7.3.6.tgz#dde8e6ae32c9b5b400dcd37afd9514a5344f7d91" integrity sha512-+wiYbtvj+zyUkmDB+ysH6zRjuQIJ+CM56w0fEXV+VDNdvOuSywG+/8kpjddvvlfMLsaWdQe5oTuYGBcodmqGzQ== @@ -1388,6 +1397,18 @@ csstype "^3.1.3" prop-types "^15.8.1" +"@mui/styled-engine@^7.3.7": + version "7.3.7" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-7.3.7.tgz#cde5a8381e14310f293a53dd59d27ae737a305fc" + integrity sha512-y/QkNXv6cF6dZ5APztd/dFWfQ6LHKPx3skyYO38YhQD4+Cxd6sFAL3Z38WMSSC8LQz145Mpp3CcLrSCLKPwYAg== + dependencies: + "@babel/runtime" "^7.28.4" + "@emotion/cache" "^11.14.0" + "@emotion/serialize" "^1.3.3" + "@emotion/sheet" "^1.4.0" + csstype "^3.2.3" + prop-types "^15.8.1" + "@mui/system@7.3.2": version "7.3.2" resolved "https://registry.yarnpkg.com/@mui/system/-/system-7.3.2.tgz#e838097fc6cb0a2e4c1822478950db89affb116a" @@ -1402,56 +1423,56 @@ csstype "^3.1.3" prop-types "^15.8.1" -"@mui/system@^7.3.2": - version "7.3.6" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-7.3.6.tgz#460f82fc6fe1b79b8c04dc97694f6b162ffc3d25" - integrity sha512-8fehAazkHNP1imMrdD2m2hbA9sl7Ur6jfuNweh5o4l9YPty4iaZzRXqYvBCWQNwFaSHmMEj2KPbyXGp7Bt73Rg== +"@mui/system@^7.3.2", "@mui/system@^7.3.7": + version "7.3.7" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-7.3.7.tgz#530932e078ba58031cd9bcc71494a544fa635a27" + integrity sha512-DovL3k+FBRKnhmatzUMyO5bKkhMLlQ9L7Qw5qHrre3m8zCZmE+31NDVBFfqrbrA7sq681qaEIHdkWD5nmiAjyQ== dependencies: "@babel/runtime" "^7.28.4" - "@mui/private-theming" "^7.3.6" - "@mui/styled-engine" "^7.3.6" - "@mui/types" "^7.4.9" - "@mui/utils" "^7.3.6" + "@mui/private-theming" "^7.3.7" + "@mui/styled-engine" "^7.3.7" + "@mui/types" "^7.4.10" + "@mui/utils" "^7.3.7" clsx "^2.1.1" - csstype "^3.1.3" + csstype "^3.2.3" prop-types "^15.8.1" -"@mui/types@^7.4.6", "@mui/types@^7.4.9": - version "7.4.9" - resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.4.9.tgz#99accc87920b4c8c4ce33c5076a58f7f81b528fa" - integrity sha512-dNO8Z9T2cujkSIaCnWwprfeKmTWh97cnjkgmpFJ2sbfXLx8SMZijCYHOtP/y5nnUb/Rm2omxbDMmtUoSaUtKaw== +"@mui/types@^7.4.10", "@mui/types@^7.4.6": + version "7.4.10" + resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.4.10.tgz#c80ed5850a1da7802a01c1d0153d8603ce41be10" + integrity sha512-0+4mSjknSu218GW3isRqoxKRTOrTLd/vHi/7UC4+wZcUrOAqD9kRk7UQRL1mcrzqRoe7s3UT6rsRpbLkW5mHpQ== dependencies: "@babel/runtime" "^7.28.4" -"@mui/utils@^7.3.2", "@mui/utils@^7.3.5", "@mui/utils@^7.3.6": - version "7.3.6" - resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-7.3.6.tgz#508fbe864832f99b215d134eb89e1198cdc66b34" - integrity sha512-jn+Ba02O6PiFs7nKva8R2aJJ9kJC+3kQ2R0BbKNY3KQQ36Qng98GnPRFTlbwYTdMD6hLEBKaMLUktyg/rTfd2w== +"@mui/utils@^7.3.2", "@mui/utils@^7.3.5", "@mui/utils@^7.3.6", "@mui/utils@^7.3.7": + version "7.3.7" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-7.3.7.tgz#71443559a7fbd993b5b90fcb843fa26a60046f99" + integrity sha512-+YjnjMRnyeTkWnspzoxRdiSOgkrcpTikhNPoxOZW0APXx+urHtUoXJ9lbtCZRCA5a4dg5gSbd19alL1DvRs5fg== dependencies: "@babel/runtime" "^7.28.4" - "@mui/types" "^7.4.9" + "@mui/types" "^7.4.10" "@types/prop-types" "^15.7.15" clsx "^2.1.1" prop-types "^15.8.1" - react-is "^19.2.0" + react-is "^19.2.3" -"@mui/x-date-pickers@^8.11.1": - version "8.22.0" - resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-8.22.0.tgz#60bc176a308078002e93cfbf3ee55cae234280d5" - integrity sha512-iF4H8EJHcltiwfytTe6M1/iCwBUCB97oVh63nAsL4tre2Ew2jm8bKR4Dw2pbgvSUonB3okpZVuweFBQdnECJCg== +"@mui/x-date-pickers@^8.25.0": + version "8.25.0" + resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-8.25.0.tgz#6e35a740969eeacf3f532e24ffd8960c761bdd10" + integrity sha512-XmLQwlo9C9gPWY9OeFbQka4TGi3MXrW/jJ+E4LV1wdfg/ebOklq6KKKTbvRgTVWlMcIoQwqPbalFxcwQSUUbDw== dependencies: "@babel/runtime" "^7.28.4" "@mui/utils" "^7.3.5" - "@mui/x-internals" "8.22.0" + "@mui/x-internals" "8.25.0" "@types/react-transition-group" "^4.4.12" clsx "^2.1.1" prop-types "^15.8.1" react-transition-group "^4.4.5" -"@mui/x-internals@8.22.0": - version "8.22.0" - resolved "https://registry.yarnpkg.com/@mui/x-internals/-/x-internals-8.22.0.tgz#adf47979b3ef1e7c078965756916f6bd57c3c655" - integrity sha512-PA7jCKRLbS6aYvTSbGr3Id4CPUdTrUejHm31l8Vje7dw138gBBHrHeGsqWJh/S5foorpK8loiRejKrLlTZokyQ== +"@mui/x-internals@8.25.0": + version "8.25.0" + resolved "https://registry.yarnpkg.com/@mui/x-internals/-/x-internals-8.25.0.tgz#93849275bfb3a4e5e0130ae91449bb4f8517a1d7" + integrity sha512-RKexkVaK3xvAeLBNeLAw6oJCsQrXkx7TYSRoSUmmJveydqOqoBbimv+nbc8PmL4UL0ShVNkaFL1YWY7kYCCXUA== dependencies: "@babel/runtime" "^7.28.4" "@mui/utils" "^7.3.5" @@ -1472,10 +1493,10 @@ "@emnapi/runtime" "^1.4.3" "@tybys/wasm-util" "^0.10.0" -"@next/env@15.5.9": - version "15.5.9" - resolved "https://registry.yarnpkg.com/@next/env/-/env-15.5.9.tgz#53c2c34dc17cd87b61f70c6cc211e303123b2ab8" - integrity sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg== +"@next/env@16.1.2": + version "16.1.2" + resolved "https://registry.yarnpkg.com/@next/env/-/env-16.1.2.tgz#449128f465309fee4999cb9cc346a0bf33de6aad" + integrity sha512-r6TpLovDTvWtzw11UubUQxEK6IduT8rSAHbGX68yeFpA/1Oq9R4ovi5nqMUMgPN0jr2SpfeyFRbTZg3Inuuv3g== "@next/eslint-plugin-next@15.5.2": version "15.5.2" @@ -1484,45 +1505,45 @@ dependencies: fast-glob "3.3.1" -"@next/swc-darwin-arm64@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz#f0c9ccfec2cd87cbd4b241ce4c779a7017aed958" - integrity sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw== - -"@next/swc-darwin-x64@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.7.tgz#18009e9fcffc5c0687cc9db24182ddeac56280d9" - integrity sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg== - -"@next/swc-linux-arm64-gnu@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.7.tgz#fe7c7e08264cf522d4e524299f6d3e63d68d579a" - integrity sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA== - -"@next/swc-linux-arm64-musl@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.7.tgz#94228fe293475ec34a5a54284e1056876f43a3cf" - integrity sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw== - -"@next/swc-linux-x64-gnu@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz#078c71201dfe7fcfb8fa6dc92aae6c94bc011cdc" - integrity sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw== - -"@next/swc-linux-x64-musl@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz#72947f5357f9226292353e0bb775643da3c7a182" - integrity sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA== - -"@next/swc-win32-arm64-msvc@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.7.tgz#397b912cd51c6a80e32b9c0507ecd82514353941" - integrity sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ== - -"@next/swc-win32-x64-msvc@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.7.tgz#e02b543d9dc6c1631d4ac239cb1177245dfedfe4" - integrity sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw== +"@next/swc-darwin-arm64@16.1.2": + version "16.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.2.tgz#74f81d4d4d1d6d0b7cda28fdf86c60c0a9ffa00d" + integrity sha512-0N2baysDpTXASTVxTV+DkBnD97bo9PatUj8sHlKA+oR9CyvReaPQchQyhCbH0Jm0mC/Oka5F52intN+lNOhSlA== + +"@next/swc-darwin-x64@16.1.2": + version "16.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.2.tgz#4ab440f5dbce4147e0044d2439a4cbf5264044e0" + integrity sha512-Q0wnSK0lmeC9ps+/w/bDsMSF3iWS45WEwF1bg8dvMH3CmKB2BV4346tVrjWxAkrZq20Ro6Of3R19IgrEJkXKyw== + +"@next/swc-linux-arm64-gnu@16.1.2": + version "16.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.2.tgz#62007c34504caa6d65d5174df6d575bd406a40a5" + integrity sha512-4twW+h7ZatGKWq+2pUQ9SDiin6kfZE/mY+D8jOhSZ0NDzKhQfAPReXqwTDWVrNjvLzHzOcDL5kYjADHfXL/b/Q== + +"@next/swc-linux-arm64-musl@16.1.2": + version "16.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.2.tgz#7dc9db5aa7d4dc705fc5831a0bf2210bd5dc8175" + integrity sha512-Sn6LxPIZcADe5AnqqMCfwBv6vRtDikhtrjwhu+19WM6jHZe31JDRcGuPZAlJrDk6aEbNBPUUAKmySJELkBOesg== + +"@next/swc-linux-x64-gnu@16.1.2": + version "16.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.2.tgz#588913369dac3191bb2833bd1739acc1d5c2f642" + integrity sha512-nwzesEQBfQIOOnQ7JArzB08w9qwvBQ7nC1i8gb0tiEFH94apzQM3IRpY19MlE8RBHxc9ArG26t1DEg2aaLaqVQ== + +"@next/swc-linux-x64-musl@16.1.2": + version "16.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.2.tgz#61c1466fe6e7a9176942ef9d276e9b5838d51c5f" + integrity sha512-s60bLf16BDoICQHeKEm0lDgUNMsL1UpQCkRNZk08ZNnRpK0QUV+6TvVHuBcIA7oItzU0m7kVmXe8QjXngYxJVA== + +"@next/swc-win32-arm64-msvc@16.1.2": + version "16.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.2.tgz#6854380d1c8456c955b5352c18ddf44e2373345d" + integrity sha512-Sq8k4SZd8Y8EokKdz304TvMO9HoiwGzo0CTacaiN1bBtbJSQ1BIwKzNFeFdxOe93SHn1YGnKXG6Mq3N+tVooyQ== + +"@next/swc-win32-x64-msvc@16.1.2": + version "16.1.2" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.2.tgz#5b884d3b722ccd751b1482b575daa74ce5434dd0" + integrity sha512-KQDBwspSaNX5/wwt6p7ed5oINJWIxcgpuqJdDNubAyq7dD+ZM76NuEjg8yUxNOl5R4NNgbMfqE/RyNrsbYmOKg== "@nivo/colors@0.99.0": version "0.99.0" @@ -2466,13 +2487,6 @@ "@types/linkify-it" "^5" "@types/mdurl" "^2" -"@types/mdast@^3.0.0": - version "3.0.15" - resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.15.tgz#49c524a263f30ffa28b71ae282f813ed000ab9f5" - integrity sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ== - dependencies: - "@types/unist" "^2" - "@types/mdast@^4.0.0": version "4.0.4" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" @@ -3047,10 +3061,10 @@ base64-js@^1.1.2, base64-js@^1.3.0: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -baseline-browser-mapping@^2.9.0: - version "2.9.8" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.8.tgz#04fb5c10ff9c7a1b04ac08cfdfc3b10942a8ac72" - integrity sha512-Y1fOuNDowLfgKOypdc9SPABfoWXuZHBOyCS4cD52IeZBhr4Md6CLLs6atcxVrzRmQ06E7hSlm5bHHApPKR/byA== +baseline-browser-mapping@^2.8.3, baseline-browser-mapping@^2.9.0: + version "2.9.15" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.15.tgz#6baaa0069883f50a99cdb31b56646491f47c05d7" + integrity sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg== bidi-js@^1.0.2: version "1.0.3" @@ -3413,7 +3427,7 @@ csso@^5.0.5: dependencies: css-tree "~2.2.0" -csstype@^3.0.2, csstype@^3.1.3, csstype@^3.2.2: +csstype@^3.0.2, csstype@^3.1.3, csstype@^3.2.2, csstype@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== @@ -3678,11 +3692,6 @@ dfa@^1.2.0: resolved "https://registry.yarnpkg.com/dfa/-/dfa-1.2.0.tgz#96ac3204e2d29c49ea5b57af8d92c2ae12790657" integrity sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q== -diff@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" - integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== - doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -4929,11 +4938,6 @@ is-boolean-object@^1.2.1: call-bound "^1.0.3" has-tostringtag "^1.0.2" -is-buffer@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" - integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== - is-bun-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-bun-module/-/is-bun-module-2.0.0.tgz#4d7859a87c0fcac950c95e666730e745eae8bddd" @@ -5273,11 +5277,6 @@ kind-of@^6.0.0, kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -kleur@^4.0.3: - version "4.1.5" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" - integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== - language-subtag-registry@^0.3.20: version "0.3.23" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" @@ -5436,33 +5435,15 @@ math-intrinsics@^1.1.0: resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== -mdast-util-find-and-replace@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz#cc2b774f7f3630da4bd592f61966fecade8b99b1" - integrity sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw== +mdast-util-find-and-replace@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz#70a3174c894e14df722abf43bc250cbae44b11df" + integrity sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg== dependencies: - "@types/mdast" "^3.0.0" + "@types/mdast" "^4.0.0" escape-string-regexp "^5.0.0" - unist-util-is "^5.0.0" - unist-util-visit-parents "^5.0.0" - -mdast-util-from-markdown@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz#9421a5a247f10d31d2faed2a30df5ec89ceafcf0" - integrity sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww== - dependencies: - "@types/mdast" "^3.0.0" - "@types/unist" "^2.0.0" - decode-named-character-reference "^1.0.0" - mdast-util-to-string "^3.1.0" - micromark "^3.0.0" - micromark-util-decode-numeric-character-reference "^1.0.0" - micromark-util-decode-string "^1.0.0" - micromark-util-normalize-identifier "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - unist-util-stringify-position "^3.0.0" - uvu "^0.5.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" mdast-util-from-markdown@^2.0.0: version "2.0.2" @@ -5482,63 +5463,70 @@ mdast-util-from-markdown@^2.0.0: micromark-util-types "^2.0.0" unist-util-stringify-position "^4.0.0" -mdast-util-gfm-autolink-literal@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-1.0.3.tgz#67a13abe813d7eba350453a5333ae1bc0ec05c06" - integrity sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA== +mdast-util-gfm-autolink-literal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz#abd557630337bd30a6d5a4bd8252e1c2dc0875d5" + integrity sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ== dependencies: - "@types/mdast" "^3.0.0" + "@types/mdast" "^4.0.0" ccount "^2.0.0" - mdast-util-find-and-replace "^2.0.0" - micromark-util-character "^1.0.0" + devlop "^1.0.0" + mdast-util-find-and-replace "^3.0.0" + micromark-util-character "^2.0.0" -mdast-util-gfm-footnote@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-1.0.2.tgz#ce5e49b639c44de68d5bf5399877a14d5020424e" - integrity sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ== +mdast-util-gfm-footnote@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz#7778e9d9ca3df7238cc2bd3fa2b1bf6a65b19403" + integrity sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ== dependencies: - "@types/mdast" "^3.0.0" - mdast-util-to-markdown "^1.3.0" - micromark-util-normalize-identifier "^1.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.1.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" -mdast-util-gfm-strikethrough@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.3.tgz#5470eb105b483f7746b8805b9b989342085795b7" - integrity sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ== +mdast-util-gfm-strikethrough@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz#d44ef9e8ed283ac8c1165ab0d0dfd058c2764c16" + integrity sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg== dependencies: - "@types/mdast" "^3.0.0" - mdast-util-to-markdown "^1.3.0" + "@types/mdast" "^4.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" -mdast-util-gfm-table@^1.0.0: - version "1.0.7" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.7.tgz#3552153a146379f0f9c4c1101b071d70bbed1a46" - integrity sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg== +mdast-util-gfm-table@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz#7a435fb6223a72b0862b33afbd712b6dae878d38" + integrity sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg== dependencies: - "@types/mdast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" markdown-table "^3.0.0" - mdast-util-from-markdown "^1.0.0" - mdast-util-to-markdown "^1.3.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" -mdast-util-gfm-task-list-item@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.2.tgz#b280fcf3b7be6fd0cc012bbe67a59831eb34097b" - integrity sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ== +mdast-util-gfm-task-list-item@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz#e68095d2f8a4303ef24094ab642e1047b991a936" + integrity sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ== dependencies: - "@types/mdast" "^3.0.0" - mdast-util-to-markdown "^1.3.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" -mdast-util-gfm@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-2.0.2.tgz#e92f4d8717d74bdba6de57ed21cc8b9552e2d0b6" - integrity sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg== +mdast-util-gfm@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz#2cdf63b92c2a331406b0fb0db4c077c1b0331751" + integrity sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ== dependencies: - mdast-util-from-markdown "^1.0.0" - mdast-util-gfm-autolink-literal "^1.0.0" - mdast-util-gfm-footnote "^1.0.0" - mdast-util-gfm-strikethrough "^1.0.0" - mdast-util-gfm-table "^1.0.0" - mdast-util-gfm-task-list-item "^1.0.0" - mdast-util-to-markdown "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-gfm-autolink-literal "^2.0.0" + mdast-util-gfm-footnote "^2.0.0" + mdast-util-gfm-strikethrough "^2.0.0" + mdast-util-gfm-table "^2.0.0" + mdast-util-gfm-task-list-item "^2.0.0" + mdast-util-to-markdown "^2.0.0" mdast-util-mdx-expression@^2.0.0: version "2.0.1" @@ -5582,14 +5570,6 @@ mdast-util-mdxjs-esm@^2.0.0: mdast-util-from-markdown "^2.0.0" mdast-util-to-markdown "^2.0.0" -mdast-util-phrasing@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz#c7c21d0d435d7fb90956038f02e8702781f95463" - integrity sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg== - dependencies: - "@types/mdast" "^3.0.0" - unist-util-is "^5.0.0" - mdast-util-phrasing@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" @@ -5613,20 +5593,6 @@ mdast-util-to-hast@^13.0.0: unist-util-visit "^5.0.0" vfile "^6.0.0" -mdast-util-to-markdown@^1.0.0, mdast-util-to-markdown@^1.3.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-1.5.0.tgz#c13343cb3fc98621911d33b5cd42e7d0731171c6" - integrity sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A== - dependencies: - "@types/mdast" "^3.0.0" - "@types/unist" "^2.0.0" - longest-streak "^3.0.0" - mdast-util-phrasing "^3.0.0" - mdast-util-to-string "^3.0.0" - micromark-util-decode-string "^1.0.0" - unist-util-visit "^4.0.0" - zwitch "^2.0.0" - mdast-util-to-markdown@^2.0.0: version "2.1.2" resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz#f910ffe60897f04bb4b7e7ee434486f76288361b" @@ -5642,13 +5608,6 @@ mdast-util-to-markdown@^2.0.0: unist-util-visit "^5.0.0" zwitch "^2.0.0" -mdast-util-to-string@^3.0.0, mdast-util-to-string@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz#66f7bb6324756741c5f47a53557f0cbf16b6f789" - integrity sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg== - dependencies: - "@types/mdast" "^3.0.0" - mdast-util-to-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" @@ -5691,28 +5650,6 @@ merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -micromark-core-commonmark@^1.0.0, micromark-core-commonmark@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz#1386628df59946b2d39fb2edfd10f3e8e0a75bb8" - integrity sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw== - dependencies: - decode-named-character-reference "^1.0.0" - micromark-factory-destination "^1.0.0" - micromark-factory-label "^1.0.0" - micromark-factory-space "^1.0.0" - micromark-factory-title "^1.0.0" - micromark-factory-whitespace "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-chunked "^1.0.0" - micromark-util-classify-character "^1.0.0" - micromark-util-html-tag-name "^1.0.0" - micromark-util-normalize-identifier "^1.0.0" - micromark-util-resolve-all "^1.0.0" - micromark-util-subtokenize "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.1" - uvu "^0.5.0" - micromark-core-commonmark@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz#c691630e485021a68cf28dbc2b2ca27ebf678cd4" @@ -5735,93 +5672,84 @@ micromark-core-commonmark@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-extension-gfm-autolink-literal@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.5.tgz#5853f0e579bbd8ef9e39a7c0f0f27c5a063a66e7" - integrity sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg== +micromark-extension-gfm-autolink-literal@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz#6286aee9686c4462c1e3552a9d505feddceeb935" + integrity sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw== dependencies: - micromark-util-character "^1.0.0" - micromark-util-sanitize-uri "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" -micromark-extension-gfm-footnote@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.1.2.tgz#05e13034d68f95ca53c99679040bc88a6f92fe2e" - integrity sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q== - dependencies: - micromark-core-commonmark "^1.0.0" - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-normalize-identifier "^1.0.0" - micromark-util-sanitize-uri "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" - -micromark-extension-gfm-strikethrough@^1.0.0: - version "1.0.7" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.7.tgz#c8212c9a616fa3bf47cb5c711da77f4fdc2f80af" - integrity sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw== +micromark-extension-gfm-footnote@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz#4dab56d4e398b9853f6fe4efac4fc9361f3e0750" + integrity sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw== dependencies: - micromark-util-chunked "^1.0.0" - micromark-util-classify-character "^1.0.0" - micromark-util-resolve-all "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" -micromark-extension-gfm-table@^1.0.0: - version "1.0.7" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.7.tgz#dcb46074b0c6254c3fc9cc1f6f5002c162968008" - integrity sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw== +micromark-extension-gfm-strikethrough@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz#86106df8b3a692b5f6a92280d3879be6be46d923" + integrity sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw== dependencies: - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" + devlop "^1.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" -micromark-extension-gfm-tagfilter@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-1.0.2.tgz#aa7c4dd92dabbcb80f313ebaaa8eb3dac05f13a7" - integrity sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g== +micromark-extension-gfm-table@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz#fac70bcbf51fe65f5f44033118d39be8a9b5940b" + integrity sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg== dependencies: - micromark-util-types "^1.0.0" + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" -micromark-extension-gfm-task-list-item@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.5.tgz#b52ce498dc4c69b6a9975abafc18f275b9dde9f4" - integrity sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ== +micromark-extension-gfm-tagfilter@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz#f26d8a7807b5985fba13cf61465b58ca5ff7dc57" + integrity sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg== dependencies: - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" + micromark-util-types "^2.0.0" -micromark-extension-gfm@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-2.0.3.tgz#e517e8579949a5024a493e49204e884aa74f5acf" - integrity sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ== - dependencies: - micromark-extension-gfm-autolink-literal "^1.0.0" - micromark-extension-gfm-footnote "^1.0.0" - micromark-extension-gfm-strikethrough "^1.0.0" - micromark-extension-gfm-table "^1.0.0" - micromark-extension-gfm-tagfilter "^1.0.0" - micromark-extension-gfm-task-list-item "^1.0.0" - micromark-util-combine-extensions "^1.0.0" - micromark-util-types "^1.0.0" - -micromark-factory-destination@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz#eb815957d83e6d44479b3df640f010edad667b9f" - integrity sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg== +micromark-extension-gfm-task-list-item@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz#bcc34d805639829990ec175c3eea12bb5b781f2c" + integrity sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw== dependencies: - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz#3e13376ab95dd7a5cfd0e29560dfe999657b3c5b" + integrity sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w== + dependencies: + micromark-extension-gfm-autolink-literal "^2.0.0" + micromark-extension-gfm-footnote "^2.0.0" + micromark-extension-gfm-strikethrough "^2.0.0" + micromark-extension-gfm-table "^2.0.0" + micromark-extension-gfm-tagfilter "^2.0.0" + micromark-extension-gfm-task-list-item "^2.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-types "^2.0.0" micromark-factory-destination@^2.0.0: version "2.0.1" @@ -5832,16 +5760,6 @@ micromark-factory-destination@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-factory-label@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz#cc95d5478269085cfa2a7282b3de26eb2e2dec68" - integrity sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w== - dependencies: - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" - micromark-factory-label@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz#5267efa97f1e5254efc7f20b459a38cb21058ba1" @@ -5852,14 +5770,6 @@ micromark-factory-label@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-factory-space@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz#c8f40b0640a0150751d3345ed885a080b0d15faf" - integrity sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ== - dependencies: - micromark-util-character "^1.0.0" - micromark-util-types "^1.0.0" - micromark-factory-space@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz#36d0212e962b2b3121f8525fc7a3c7c029f334fc" @@ -5868,16 +5778,6 @@ micromark-factory-space@^2.0.0: micromark-util-character "^2.0.0" micromark-util-types "^2.0.0" -micromark-factory-title@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz#dd0fe951d7a0ac71bdc5ee13e5d1465ad7f50ea1" - integrity sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ== - dependencies: - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - micromark-factory-title@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz#237e4aa5d58a95863f01032d9ee9b090f1de6e94" @@ -5888,16 +5788,6 @@ micromark-factory-title@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-factory-whitespace@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz#798fb7489f4c8abafa7ca77eed6b5745853c9705" - integrity sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ== - dependencies: - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - micromark-factory-whitespace@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz#06b26b2983c4d27bfcc657b33e25134d4868b0b1" @@ -5908,14 +5798,6 @@ micromark-factory-whitespace@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-util-character@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-1.2.0.tgz#4fedaa3646db249bc58caeb000eb3549a8ca5dcc" - integrity sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg== - dependencies: - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - micromark-util-character@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" @@ -5924,13 +5806,6 @@ micromark-util-character@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-util-chunked@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz#37a24d33333c8c69a74ba12a14651fd9ea8a368b" - integrity sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ== - dependencies: - micromark-util-symbol "^1.0.0" - micromark-util-chunked@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz#47fbcd93471a3fccab86cff03847fc3552db1051" @@ -5938,15 +5813,6 @@ micromark-util-chunked@^2.0.0: dependencies: micromark-util-symbol "^2.0.0" -micromark-util-classify-character@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz#6a7f8c8838e8a120c8e3c4f2ae97a2bff9190e9d" - integrity sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw== - dependencies: - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - micromark-util-classify-character@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz#d399faf9c45ca14c8b4be98b1ea481bced87b629" @@ -5956,14 +5822,6 @@ micromark-util-classify-character@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-util-combine-extensions@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz#192e2b3d6567660a85f735e54d8ea6e3952dbe84" - integrity sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA== - dependencies: - micromark-util-chunked "^1.0.0" - micromark-util-types "^1.0.0" - micromark-util-combine-extensions@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz#2a0f490ab08bff5cc2fd5eec6dd0ca04f89b30a9" @@ -5972,13 +5830,6 @@ micromark-util-combine-extensions@^2.0.0: micromark-util-chunked "^2.0.0" micromark-util-types "^2.0.0" -micromark-util-decode-numeric-character-reference@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz#b1e6e17009b1f20bc652a521309c5f22c85eb1c6" - integrity sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw== - dependencies: - micromark-util-symbol "^1.0.0" - micromark-util-decode-numeric-character-reference@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz#fcf15b660979388e6f118cdb6bf7d79d73d26fe5" @@ -5986,16 +5837,6 @@ micromark-util-decode-numeric-character-reference@^2.0.0: dependencies: micromark-util-symbol "^2.0.0" -micromark-util-decode-string@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz#dc12b078cba7a3ff690d0203f95b5d5537f2809c" - integrity sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ== - dependencies: - decode-named-character-reference "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-decode-numeric-character-reference "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-decode-string@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz#6cb99582e5d271e84efca8e61a807994d7161eb2" @@ -6006,33 +5847,16 @@ micromark-util-decode-string@^2.0.0: micromark-util-decode-numeric-character-reference "^2.0.0" micromark-util-symbol "^2.0.0" -micromark-util-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz#92e4f565fd4ccb19e0dcae1afab9a173bbeb19a5" - integrity sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw== - micromark-util-encode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== -micromark-util-html-tag-name@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz#48fd7a25826f29d2f71479d3b4e83e94829b3588" - integrity sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q== - micromark-util-html-tag-name@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz#e40403096481986b41c106627f98f72d4d10b825" integrity sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA== -micromark-util-normalize-identifier@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz#7a73f824eb9f10d442b4d7f120fecb9b38ebf8b7" - integrity sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q== - dependencies: - micromark-util-symbol "^1.0.0" - micromark-util-normalize-identifier@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz#c30d77b2e832acf6526f8bf1aa47bc9c9438c16d" @@ -6040,13 +5864,6 @@ micromark-util-normalize-identifier@^2.0.0: dependencies: micromark-util-symbol "^2.0.0" -micromark-util-resolve-all@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz#4652a591ee8c8fa06714c9b54cd6c8e693671188" - integrity sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA== - dependencies: - micromark-util-types "^1.0.0" - micromark-util-resolve-all@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz#e1a2d62cdd237230a2ae11839027b19381e31e8b" @@ -6054,15 +5871,6 @@ micromark-util-resolve-all@^2.0.0: dependencies: micromark-util-types "^2.0.0" -micromark-util-sanitize-uri@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz#613f738e4400c6eedbc53590c67b197e30d7f90d" - integrity sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A== - dependencies: - micromark-util-character "^1.0.0" - micromark-util-encode "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-sanitize-uri@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" @@ -6072,16 +5880,6 @@ micromark-util-sanitize-uri@^2.0.0: micromark-util-encode "^2.0.0" micromark-util-symbol "^2.0.0" -micromark-util-subtokenize@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz#941c74f93a93eaf687b9054aeb94642b0e92edb1" - integrity sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A== - dependencies: - micromark-util-chunked "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" - micromark-util-subtokenize@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz#d8ade5ba0f3197a1cf6a2999fbbfe6357a1a19ee" @@ -6092,49 +5890,16 @@ micromark-util-subtokenize@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromark-util-symbol@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz#813cd17837bdb912d069a12ebe3a44b6f7063142" - integrity sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag== - micromark-util-symbol@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== -micromark-util-types@^1.0.0, micromark-util-types@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-1.1.0.tgz#e6676a8cae0bb86a2171c498167971886cb7e283" - integrity sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg== - micromark-util-types@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== -micromark@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/micromark/-/micromark-3.2.0.tgz#1af9fef3f995ea1ea4ac9c7e2f19c48fd5c006e9" - integrity sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA== - dependencies: - "@types/debug" "^4.0.0" - debug "^4.0.0" - decode-named-character-reference "^1.0.0" - micromark-core-commonmark "^1.0.1" - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-chunked "^1.0.0" - micromark-util-combine-extensions "^1.0.0" - micromark-util-decode-numeric-character-reference "^1.0.0" - micromark-util-encode "^1.0.0" - micromark-util-normalize-identifier "^1.0.0" - micromark-util-resolve-all "^1.0.0" - micromark-util-sanitize-uri "^1.0.0" - micromark-util-subtokenize "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.1" - uvu "^0.5.0" - micromark@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.2.tgz#91395a3e1884a198e62116e33c9c568e39936fdb" @@ -6204,11 +5969,6 @@ monaco-editor@^0.53.0: dependencies: "@types/trusted-types" "^1.0.6" -mri@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" - integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== - ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" @@ -6247,26 +6007,27 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -next@^15.2.2: - version "15.5.9" - resolved "https://registry.yarnpkg.com/next/-/next-15.5.9.tgz#1b80d05865cc27e710fb4dcfc6fd9e726ed12ad4" - integrity sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg== +next@^16.1.2: + version "16.1.2" + resolved "https://registry.yarnpkg.com/next/-/next-16.1.2.tgz#7b12fdc499448a809c3e6fe42ac3cbbcf543982d" + integrity sha512-SVSWX7wjUUDrIDVqhl4xm/jiOrvYGMG7NzVE/dGzzgs7r3dFGm4V19ia0xn3GDNtHCKM7C9h+5BoimnJBhmt9A== dependencies: - "@next/env" "15.5.9" + "@next/env" "16.1.2" "@swc/helpers" "0.5.15" + baseline-browser-mapping "^2.8.3" caniuse-lite "^1.0.30001579" postcss "8.4.31" styled-jsx "5.1.6" optionalDependencies: - "@next/swc-darwin-arm64" "15.5.7" - "@next/swc-darwin-x64" "15.5.7" - "@next/swc-linux-arm64-gnu" "15.5.7" - "@next/swc-linux-arm64-musl" "15.5.7" - "@next/swc-linux-x64-gnu" "15.5.7" - "@next/swc-linux-x64-musl" "15.5.7" - "@next/swc-win32-arm64-msvc" "15.5.7" - "@next/swc-win32-x64-msvc" "15.5.7" - sharp "^0.34.3" + "@next/swc-darwin-arm64" "16.1.2" + "@next/swc-darwin-x64" "16.1.2" + "@next/swc-linux-arm64-gnu" "16.1.2" + "@next/swc-linux-arm64-musl" "16.1.2" + "@next/swc-linux-x64-gnu" "16.1.2" + "@next/swc-linux-x64-musl" "16.1.2" + "@next/swc-win32-arm64-msvc" "16.1.2" + "@next/swc-win32-x64-msvc" "16.1.2" + sharp "^0.34.4" no-case@^3.0.4: version "3.0.4" @@ -6855,12 +6616,12 @@ react-copy-to-clipboard@^5.1.0: copy-to-clipboard "^3.3.1" prop-types "^15.8.1" -react-dom@19.1.1: - version "19.1.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.1.1.tgz#2daa9ff7f3ae384aeb30e76d5ee38c046dc89893" - integrity sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw== +react-dom@19.2.3: + version "19.2.3" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.3.tgz#f0b61d7e5c4a86773889fcc1853af3ed5f215b17" + integrity sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg== dependencies: - scheduler "^0.26.0" + scheduler "^0.27.0" react-draggable@^4.0.3, react-draggable@^4.4.6: version "4.5.0" @@ -6879,12 +6640,10 @@ react-dropzone@14.3.8: file-selector "^2.1.0" prop-types "^15.8.1" -react-error-boundary@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-6.0.0.tgz#a9e552146958fa77d873b587aa6a5e97544ee954" - integrity sha512-gdlJjD7NWr0IfkPlaREN2d9uUZUlksrfOx7SX62VRerwXbMY6ftGCIZua1VG1aXFNOimhISsTq+Owp725b9SiA== - dependencies: - "@babel/runtime" "^7.12.5" +react-error-boundary@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-6.1.0.tgz#d2965de0723d65d60d20aef2e120bd2c171ae4d7" + integrity sha512-02k9WQ/mUhdbXir0tC1NiMesGzRPaCsJEWU/4bcFrbY1YMZOtHShtZP6zw0SJrBWA/31H0KT9/FgdL8+sPKgHA== react-fast-compare@^2.0.1: version "2.0.4" @@ -6941,7 +6700,7 @@ react-is@^17.0.2: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^19.1.1, react-is@^19.2.0: +react-is@^19.2.3: version "19.2.3" resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.3.tgz#eec2feb69c7fb31f77d0b5c08c10ae1c88886b29" integrity sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA== @@ -7076,10 +6835,10 @@ react-window@^2.1.0: resolved "https://registry.yarnpkg.com/react-window/-/react-window-2.2.3.tgz#f8ffdddbb612ccd3e1314b59fce79af85d3f15e3" integrity sha512-gTRqQYC8ojbiXyd9duYFiSn2TJw0ROXCgYjenOvNKITWzK0m0eCvkUsEUM08xvydkMh7ncp+LE0uS3DeNGZxnQ== -react@19.1.1: - version "19.1.1" - resolved "https://registry.yarnpkg.com/react/-/react-19.1.1.tgz#06d9149ec5e083a67f9a1e39ce97b06a03b644af" - integrity sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ== +react@19.2.3: + version "19.2.3" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.3.tgz#d83e5e8e7a258cf6b4fe28640515f99b87cd19b8" + integrity sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA== readable-stream@^2.0.2: version "2.3.8" @@ -7247,15 +7006,17 @@ relative-time-format@^1.1.7: resolved "https://registry.yarnpkg.com/relative-time-format/-/relative-time-format-1.1.11.tgz#b193d5192434e7c1c6a53e362f811c68a4f18c45" integrity sha512-TH+oV/w77hjaB9xCzoFYJ/Icmr/12+02IAoCI/YGS2UBTbjCbBjHGEBxGnVy4EJvOR1qadGzyFRI6hGaJJG93Q== -remark-gfm@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-3.0.1.tgz#0b180f095e3036545e9dddac0e8df3fa5cfee54f" - integrity sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig== +remark-gfm@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-4.0.1.tgz#33227b2a74397670d357bf05c098eaf8513f0d6b" + integrity sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg== dependencies: - "@types/mdast" "^3.0.0" - mdast-util-gfm "^2.0.0" - micromark-extension-gfm "^2.0.0" - unified "^10.0.0" + "@types/mdast" "^4.0.0" + mdast-util-gfm "^3.0.0" + micromark-extension-gfm "^3.0.0" + remark-parse "^11.0.0" + remark-stringify "^11.0.0" + unified "^11.0.0" remark-parse@^11.0.0: version "11.0.0" @@ -7278,6 +7039,15 @@ remark-rehype@^11.0.0: unified "^11.0.0" vfile "^6.0.0" +remark-stringify@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-11.0.0.tgz#4c5b01dd711c269df1aaae11743eb7e2e7636fd3" + integrity sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-to-markdown "^2.0.0" + unified "^11.0.0" + remove-accents@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.5.0.tgz#77991f37ba212afba162e375b627631315bed687" @@ -7353,13 +7123,6 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -sade@^1.7.3: - version "1.8.1" - resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701" - integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A== - dependencies: - mri "^1.1.0" - safe-array-concat@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" @@ -7403,10 +7166,10 @@ scheduler@0.25.0-rc-603e6108-20241029: resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.25.0-rc-603e6108-20241029.tgz#684dd96647e104d23e0d29a37f18937daf82df19" integrity sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA== -scheduler@^0.26.0: - version "0.26.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.26.0.tgz#4ce8a8c2a2095f13ea11bf9a445be50c555d6337" - integrity sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA== +scheduler@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" + integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== section-matter@^1.0.0: version "1.0.0" @@ -7457,7 +7220,7 @@ set-proto@^1.0.0: es-errors "^1.3.0" es-object-atoms "^1.0.0" -sharp@^0.34.3: +sharp@^0.34.4: version "0.34.5" resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.34.5.tgz#b6f148e4b8c61f1797bde11a9d1cfebbae2c57b0" integrity sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg== @@ -8027,19 +7790,6 @@ unicode-trie@^2.0.0: pako "^0.2.5" tiny-inflate "^1.0.0" -unified@^10.0.0: - version "10.1.2" - resolved "https://registry.yarnpkg.com/unified/-/unified-10.1.2.tgz#b1d64e55dafe1f0b98bb6c719881103ecf6c86df" - integrity sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q== - dependencies: - "@types/unist" "^2.0.0" - bail "^2.0.0" - extend "^3.0.0" - is-buffer "^2.0.0" - is-plain-obj "^4.0.0" - trough "^2.0.0" - vfile "^5.0.0" - unified@^11.0.0: version "11.0.5" resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1" @@ -8053,13 +7803,6 @@ unified@^11.0.0: trough "^2.0.0" vfile "^6.0.0" -unist-util-is@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.2.1.tgz#b74960e145c18dcb6226bc57933597f5486deae9" - integrity sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw== - dependencies: - "@types/unist" "^2.0.0" - unist-util-is@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.1.tgz#d0a3f86f2dd0db7acd7d8c2478080b5c67f9c6a9" @@ -8074,13 +7817,6 @@ unist-util-position@^5.0.0: dependencies: "@types/unist" "^3.0.0" -unist-util-stringify-position@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz#03ad3348210c2d930772d64b489580c13a7db39d" - integrity sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg== - dependencies: - "@types/unist" "^2.0.0" - unist-util-stringify-position@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" @@ -8088,14 +7824,6 @@ unist-util-stringify-position@^4.0.0: dependencies: "@types/unist" "^3.0.0" -unist-util-visit-parents@^5.0.0, unist-util-visit-parents@^5.1.1: - version "5.1.3" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz#b4520811b0ca34285633785045df7a8d6776cfeb" - integrity sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg== - dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^5.0.0" - unist-util-visit-parents@^6.0.0: version "6.0.2" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz#777df7fb98652ce16b4b7cd999d0a1a40efa3a02" @@ -8104,15 +7832,6 @@ unist-util-visit-parents@^6.0.0: "@types/unist" "^3.0.0" unist-util-is "^6.0.0" -unist-util-visit@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-4.1.2.tgz#125a42d1eb876283715a3cb5cceaa531828c72e2" - integrity sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg== - dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^5.0.0" - unist-util-visit-parents "^5.1.1" - unist-util-visit@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" @@ -8191,16 +7910,6 @@ utrie@^1.0.2: dependencies: base64-arraybuffer "^1.0.2" -uvu@^0.5.0: - version "0.5.6" - resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.6.tgz#2754ca20bcb0bb59b64e9985e84d2e81058502df" - integrity sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA== - dependencies: - dequal "^2.0.0" - diff "^5.0.0" - kleur "^4.0.3" - sade "^1.7.3" - vfile-location@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-5.0.3.tgz#cb9eacd20f2b6426d19451e0eafa3d0a846225c3" @@ -8209,14 +7918,6 @@ vfile-location@^5.0.0: "@types/unist" "^3.0.0" vfile "^6.0.0" -vfile-message@^3.0.0: - version "3.1.4" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-3.1.4.tgz#15a50816ae7d7c2d1fa87090a7f9f96612b59dea" - integrity sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw== - dependencies: - "@types/unist" "^2.0.0" - unist-util-stringify-position "^3.0.0" - vfile-message@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.3.tgz#87b44dddd7b70f0641c2e3ed0864ba73e2ea8df4" @@ -8225,16 +7926,6 @@ vfile-message@^4.0.0: "@types/unist" "^3.0.0" unist-util-stringify-position "^4.0.0" -vfile@^5.0.0: - version "5.3.7" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-5.3.7.tgz#de0677e6683e3380fafc46544cfe603118826ab7" - integrity sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g== - dependencies: - "@types/unist" "^2.0.0" - is-buffer "^2.0.0" - unist-util-stringify-position "^3.0.0" - vfile-message "^3.0.0" - vfile@^6.0.0: version "6.0.3" resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab"