diff --git a/.github/workflows/Check_for_Version_Update.yml b/.github/workflows/Check_for_Version_Update.yml
index 001dfdb4ffa5..de9156a94582 100644
--- a/.github/workflows/Check_for_Version_Update.yml
+++ b/.github/workflows/Check_for_Version_Update.yml
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-slim
steps:
- name: Check for Changed Files
- uses: brettcannon/check-for-changed-files@v1.1.0
+ uses: brettcannon/check-for-changed-files@v1.2.1
with:
file-pattern: public/version.json
failure-message: "You have not updated version.json. This is a required file to update at each PR. Please sync your latest changes and update the version number."
diff --git a/.github/workflows/Close_Stale_Issues.yml b/.github/workflows/Close_Stale_Issues.yml
index f061a5a5a7ff..b1878078ac90 100644
--- a/.github/workflows/Close_Stale_Issues.yml
+++ b/.github/workflows/Close_Stale_Issues.yml
@@ -8,7 +8,7 @@ jobs:
if: github.repository_owner == 'KelvinTegelaar'
runs-on: ubuntu-slim
steps:
- - uses: actions/stale@v4
+ - uses: actions/stale@v10
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."
diff --git a/.github/workflows/Label_Issues.yml b/.github/workflows/Label_Issues.yml
index ebdc3992980f..cc0032ee08e8 100644
--- a/.github/workflows/Label_Issues.yml
+++ b/.github/workflows/Label_Issues.yml
@@ -12,7 +12,7 @@ jobs:
issues: write
steps:
- name: Label Issues
- uses: andymckay/labeler@5c59dabdfd4dd5bd9c6e6d255b01b9d764af4414
+ uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90
with:
add-labels: "not-assigned"
repo-token: ${{ secrets.GITHUB_TOKEN }}
@@ -23,7 +23,7 @@ jobs:
issues: write
steps:
- name: Label Issues
- uses: andymckay/labeler@5c59dabdfd4dd5bd9c6e6d255b01b9d764af4414
+ uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90
with:
add-labels: "enhancement, not-assigned"
repo-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/Node_Project_Check.yml b/.github/workflows/Node_Project_Check.yml
index 0a777ff925da..a1581e036833 100644
--- a/.github/workflows/Node_Project_Check.yml
+++ b/.github/workflows/Node_Project_Check.yml
@@ -20,7 +20,7 @@ jobs:
steps:
- uses: actions/checkout@v6
- name: Use Node.js ${{ matrix.node-version }}
- uses: actions/setup-node@v6
+ uses: actions/setup-node@v6.2.0
with:
node-version: ${{ matrix.node-version }}
- name: Install and Build Test
diff --git a/.github/workflows/cipp_dev_build.yml b/.github/workflows/cipp_dev_build.yml
index dad0dbebe307..31d0f846d016 100644
--- a/.github/workflows/cipp_dev_build.yml
+++ b/.github/workflows/cipp_dev_build.yml
@@ -15,7 +15,7 @@ jobs:
steps:
# Checkout the repository
- name: Checkout Code
- uses: actions/checkout@v4.2.2
+ uses: actions/checkout@v6
# Set up Node.js
- name: Get Node version
@@ -26,7 +26,7 @@ jobs:
echo "node_version=$node_sanitized_version" >> $GITHUB_OUTPUT
- name: Set up Node.js
- uses: actions/setup-node@v4.2.0
+ uses: actions/setup-node@v6.2.0
with:
node-version: ${{ steps.get_node_version.outputs.node_version }}
diff --git a/.github/workflows/cipp_frontend_build.yml b/.github/workflows/cipp_frontend_build.yml
index 76a7dbb2fbbf..d6df65f2320e 100644
--- a/.github/workflows/cipp_frontend_build.yml
+++ b/.github/workflows/cipp_frontend_build.yml
@@ -15,7 +15,7 @@ jobs:
steps:
# Checkout the repository
- name: Checkout Code
- uses: actions/checkout@v4.2.2
+ uses: actions/checkout@v6
# Set up Node.js
- name: Get Node version
@@ -26,7 +26,7 @@ jobs:
echo "node_version=$node_sanitized_version" >> $GITHUB_OUTPUT
- name: Set up Node.js
- uses: actions/setup-node@v4.2.0
+ uses: actions/setup-node@v6.2.0
with:
node-version: ${{ steps.get_node_version.outputs.node_version }}
diff --git a/.github/workflows/dev_deploy.yml b/.github/workflows/dev_deploy.yml
index d8af6078d94f..027b23b85658 100644
--- a/.github/workflows/dev_deploy.yml
+++ b/.github/workflows/dev_deploy.yml
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
name: Build and Deploy Job
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
submodules: true
- name: Build And Deploy
diff --git a/Tools/Start-CippDevEmulators.ps1 b/Tools/Start-CippDevEmulators.ps1
index 74de265cc59a..e0d0146f3ab6 100644
--- a/Tools/Start-CippDevEmulators.ps1
+++ b/Tools/Start-CippDevEmulators.ps1
@@ -1,18 +1,26 @@
+Write-Host 'Starting CIPP Dev Emulators' -ForegroundColor Cyan
+
+# Verify Windows Terminal is available
Get-Command wt -ErrorAction Stop | Out-Null
+
+# Stop any existing node processes
Get-Process node -ErrorAction SilentlyContinue | Stop-Process -ErrorAction SilentlyContinue
+
+# Get paths
$Path = (Get-Item $PSScriptRoot).Parent.Parent.FullName
-Write-Host "CIPP Dev Emulators starting in $Path" -ForegroundColor Green
-pwsh -file (Join-Path $PSScriptRoot 'Start-CippDevInstallation.ps1')
+# Run installation script to ensure dependencies are installed and updated before starting emulators
+pwsh -File (Join-Path $PSScriptRoot 'Start-CippDevInstallation.ps1')
+$ApiPath = Join-Path -Path $Path -ChildPath 'CIPP-API'
+$FrontendPath = Join-Path -Path $Path -ChildPath 'CIPP'
-Write-Host 'Starting CIPP Dev Emulators'
+Write-Host 'Starting emulators...' -ForegroundColor Cyan
-if (Test-Path (Join-Path $Path 'CIPP-API-Processor')) {
- $Process = Read-Host -Prompt 'Start Process Function (y/N)?'
-}
+# Build commands with error handling
+$azuriteCommand = 'try { azurite } catch { Write-Error $_.Exception.Message } finally { Read-Host "Press Enter to exit" }'
+$apiCommand = 'try { func start } catch { Write-Error $_.Exception.Message } finally { Read-Host "Press Enter to exit" }'
+$frontendCommand = 'try { npm run dev } catch { Write-Error $_.Exception.Message } finally { Read-Host "Press Enter to exit" }'
+$swaCommand = 'try { npm run start-swa } catch { Write-Error $_.Exception.Message } finally { Read-Host "Press Enter to exit" }'
-if ($Process -eq 'y') {
- wt --title CIPP`; new-tab --title 'Azurite' -d $Path pwsh -c azurite`; new-tab --title 'FunctionApp' -d $Path\CIPP-API pwsh -c func start`; new-tab --title 'CIPP Frontend' -d $Path\CIPP pwsh -c npm run dev`; new-tab --title 'SWA' -d $Path\CIPP pwsh -c npm run start-swa`; new-tab --title 'CIPP-API-Processor' -d $Path\CIPP-API-Processor pwsh -c func start --port 7072
-} else {
- wt --title CIPP`; new-tab --title 'Azurite' -d $Path pwsh -c azurite`; new-tab --title 'FunctionApp' -d $Path\CIPP-API pwsh -c func start`; new-tab --title 'CIPP Frontend' -d $Path\CIPP pwsh -c npm run dev`; new-tab --title 'SWA' -d $Path\CIPP pwsh -c npm run start-swa
-}
+# Start Windows Terminal with all tabs
+wt --title CIPP`; new-tab --title 'Azurite' -d $Path pwsh -c $azuriteCommand`; new-tab --title 'FunctionApp' -d $ApiPath pwsh -c $apiCommand`; new-tab --title 'CIPP Frontend' -d $FrontendPath pwsh -c $frontendCommand`; new-tab --title 'SWA' -d $FrontendPath pwsh -c $swaCommand
diff --git a/Tools/Start-CippDevInstallation.ps1 b/Tools/Start-CippDevInstallation.ps1
index 2d00a12c6a26..0cc8c07f8633 100644
--- a/Tools/Start-CippDevInstallation.ps1
+++ b/Tools/Start-CippDevInstallation.ps1
@@ -29,4 +29,5 @@ if (-not(yarn global list | Select-String -Pattern 'next')) {
yarn global add 'next'
}
-yarn install --cwd (Join-Path $Path "CIPP") --network-timeout 500000
+Write-Host 'Running yarn install for CIPP frontend...' -ForegroundColor Cyan
+yarn install --cwd (Join-Path $Path 'CIPP') --network-timeout 500000
diff --git a/package.json b/package.json
index 3a21cb75f840..d575cae8d4cb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cipp",
- "version": "10.1.0",
+ "version": "10.1.1",
"author": "CIPP Contributors",
"homepage": "https://cipp.app/",
"bugs": {
@@ -34,7 +34,7 @@
"@mui/lab": "7.0.0-beta.17",
"@mui/material": "7.3.7",
"@mui/system": "7.3.2",
- "@mui/x-date-pickers": "^8.27.0",
+ "@mui/x-date-pickers": "^8.27.2",
"@musement/iso-duration": "^1.0.0",
"@nivo/core": "^0.99.0",
"@nivo/sankey": "^0.99.0",
@@ -71,7 +71,7 @@
"lodash.isequal": "4.5.0",
"material-react-table": "^3.0.1",
"monaco-editor": "^0.55.1",
- "mui-tiptap": "^1.14.0",
+ "mui-tiptap": "^1.29.0",
"next": "^16.1.6",
"nprogress": "0.2.0",
"numeral": "2.0.6",
@@ -88,7 +88,7 @@
"react-hook-form": "^7.71.1",
"react-hot-toast": "2.6.0",
"react-html-parser": "^2.0.2",
- "react-i18next": "15.7.3",
+ "react-i18next": "16.2.4",
"react-leaflet": "5.0.0",
"react-leaflet-markercluster": "^5.0.0-rc.0",
"react-markdown": "10.1.0",
@@ -98,9 +98,9 @@
"react-redux": "9.2.0",
"react-syntax-highlighter": "^16.1.0",
"react-time-ago": "^7.3.3",
- "react-virtuoso": "^4.12.8",
+ "react-virtuoso": "^4.18.1",
"react-window": "^2.2.5",
- "recharts": "^3.6.0",
+ "recharts": "^3.7.0",
"redux": "5.0.1",
"redux-devtools-extension": "2.13.9",
"redux-persist": "^6.0.0",
diff --git a/public/version.json b/public/version.json
index af189d5af279..eb5d53d8afba 100644
--- a/public/version.json
+++ b/public/version.json
@@ -1,3 +1,3 @@
{
- "version": "10.1.0"
+ "version": "10.1.1"
}
diff --git a/src/components/CippComponents/CippAddTenantAllowBlockListDrawer.jsx b/src/components/CippComponents/CippAddTenantAllowBlockListDrawer.jsx
index 23fbe252ae55..182f897966aa 100644
--- a/src/components/CippComponents/CippAddTenantAllowBlockListDrawer.jsx
+++ b/src/components/CippComponents/CippAddTenantAllowBlockListDrawer.jsx
@@ -1,4 +1,4 @@
-import { useEffect, useState } from "react";
+import { useEffect, useState } from "react";
import { Button, Divider } from "@mui/material";
import { Grid } from "@mui/system";
import { useForm, useFormState, useWatch } from "react-hook-form";
@@ -67,7 +67,7 @@ export const CippAddTenantAllowBlockListDrawer = ({
formControl.setValue(
"listMethod",
{ label: "Block", value: "Block" },
- { shouldValidate: true }
+ { shouldValidate: true },
);
}
@@ -86,16 +86,6 @@ export const CippAddTenantAllowBlockListDrawer = ({
formControl,
]);
- useEffect(() => {
- if (addEntry.isSuccess) {
- const currentTenants = formControl.getValues("tenantID");
- formControl.reset({
- ...defaultValues,
- tenantID: currentTenants,
- });
- }
- }, [addEntry.isSuccess, formControl]);
-
const validateEntries = (value) => {
if (!value) return true;
@@ -273,12 +263,12 @@ export const CippAddTenantAllowBlockListDrawer = ({
listType?.value === "FileHash"
? "Enter SHA256 hash values separated by commas or semicolons (e.g., 768a813668695ef2483b2bde7cf5d1b2db0423a0d3e63e498f3ab6f2eb13ea3e)"
: listType?.value === "Url"
- ? "Enter URLs, IPv4, or IPv6 addresses with optional wildcards separated by commas or semicolons"
- : listType?.value === "Sender"
- ? "Enter domains or email addresses separated by commas or semicolons (e.g., contoso.com,user@example.com)"
- : listType?.value === "IP"
- ? "Enter IPv6 addresses only in colon-hexadecimal format or CIDR notation"
- : ""
+ ? "Enter URLs, IPv4, or IPv6 addresses with optional wildcards separated by commas or semicolons"
+ : listType?.value === "Sender"
+ ? "Enter domains or email addresses separated by commas or semicolons (e.g., contoso.com,user@example.com)"
+ : listType?.value === "IP"
+ ? "Enter IPv6 addresses only in colon-hexadecimal format or CIDR notation"
+ : ""
}
/>
diff --git a/src/components/CippComponents/CippAutocomplete.jsx b/src/components/CippComponents/CippAutocomplete.jsx
index ab869bf1cc55..d09551f1f316 100644
--- a/src/components/CippComponents/CippAutocomplete.jsx
+++ b/src/components/CippComponents/CippAutocomplete.jsx
@@ -89,6 +89,8 @@ export const CippAutoComplete = (props) => {
const [getRequestInfo, setGetRequestInfo] = useState({ url: "", waiting: false, queryKey: "" });
const hasPreselectedRef = useRef(false);
const autocompleteRef = useRef(null); // Ref for focusing input after selection
+ const listboxRef = useRef(null); // Ref for the listbox to preserve scroll position
+ const scrollPositionRef = useRef(0); // Store scroll position
const filter = createFilterOptions({
stringify: (option) => JSON.stringify(option),
});
@@ -367,6 +369,11 @@ export const CippAutoComplete = (props) => {
}
name={name}
onChange={(event, newValue) => {
+ // Store scroll position before processing the change
+ if (multiple && listboxRef.current) {
+ scrollPositionRef.current = listboxRef.current.scrollTop;
+ }
+
if (Array.isArray(newValue)) {
newValue = newValue.map((item) => {
// If user typed a new item or missing label
@@ -407,7 +414,7 @@ export const CippAutoComplete = (props) => {
onChange(newValue, newValue?.addedFields);
}
- // In multiple mode, refocus the input after selection to allow continuous adding
+ // In multiple mode, refocus the input and restore scroll position
if (multiple && newValue && autocompleteRef.current) {
// Use setTimeout to ensure the selection is processed first
setTimeout(() => {
@@ -415,6 +422,11 @@ export const CippAutoComplete = (props) => {
if (input) {
input.focus();
}
+
+ // Restore the scroll position
+ if (listboxRef.current && scrollPositionRef.current > 0) {
+ listboxRef.current.scrollTop = scrollPositionRef.current;
+ }
}, 0);
}
}}
@@ -625,6 +637,16 @@ export const CippAutoComplete = (props) => {
}}
groupBy={groupBy}
renderGroup={renderGroup}
+ slotProps={{
+ listbox: {
+ ref: listboxRef,
+ onScroll: (e) => {
+ if (listboxRef.current) {
+ scrollPositionRef.current = e.target.scrollTop;
+ }
+ },
+ },
+ }}
renderOption={(props, option) => {
const { key, ...optionProps } = props;
return (
diff --git a/src/components/CippComponents/CippSponsor.jsx b/src/components/CippComponents/CippSponsor.jsx
new file mode 100644
index 000000000000..6db3086f6e75
--- /dev/null
+++ b/src/components/CippComponents/CippSponsor.jsx
@@ -0,0 +1,97 @@
+import { useMemo } from "react";
+import { Box, Divider, Tooltip, Typography } from "@mui/material";
+import { useSettings } from "../../hooks/use-settings";
+import sponsorsData from "../../data/sponsors.json";
+
+// Filter sponsors by date (runs once on module load)
+const getActiveSponsors = () => {
+ const now = new Date();
+ return sponsorsData.filter((sponsor) => {
+ if (!sponsor.startDate && !sponsor.endDate) {
+ return true;
+ }
+ const startDate = sponsor.startDate ? new Date(sponsor.startDate) : null;
+ const endDate = sponsor.endDate ? new Date(sponsor.endDate) : null;
+ const afterStart = !startDate || now >= startDate;
+ const beforeEnd = !endDate || now <= endDate;
+ return afterStart && beforeEnd;
+ });
+};
+
+// Select random sponsor based on priority (runs once on module load)
+const selectRandomSponsor = (sponsors) => {
+ if (sponsors.length === 0) return null;
+
+ let totalPriority = 0;
+ for (let i = 0; i < sponsors.length; i++) {
+ totalPriority += sponsors[i].priority;
+ }
+ let random = Math.floor(Math.random() * totalPriority);
+ let runningTotal = 0;
+ for (let i = 0; i < sponsors.length; i++) {
+ runningTotal += sponsors[i].priority;
+ if (random < runningTotal) {
+ return sponsors[i];
+ }
+ }
+ return null;
+};
+
+const activeSponsors = getActiveSponsors();
+const selectedSponsor = selectRandomSponsor(activeSponsors);
+
+export const CippSponsor = () => {
+ const currentSettings = useSettings();
+ const theme = currentSettings?.currentTheme?.value;
+
+ // Get the appropriate image based on current theme
+ const randomimg = useMemo(() => {
+ if (!selectedSponsor) return null;
+ return {
+ link: selectedSponsor.link,
+ imagesrc: theme === "light" ? selectedSponsor.imagesrcLight : selectedSponsor.imagesrcDark,
+ altText: selectedSponsor.altText,
+ tooltip: selectedSponsor.tooltip,
+ };
+ }, [theme]);
+
+ // Don't render if no sponsors are available
+ if (!randomimg) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+ This application is sponsored by
+
+
+
+
window.open(randomimg.link)}
+ />
+
+
+ >
+ );
+};
diff --git a/src/components/CippComponents/CippTenantLookup.jsx b/src/components/CippComponents/CippTenantLookup.jsx
new file mode 100644
index 000000000000..a1f8b3680cfc
--- /dev/null
+++ b/src/components/CippComponents/CippTenantLookup.jsx
@@ -0,0 +1,415 @@
+import React, { useState, useEffect } from "react";
+import {
+ Box,
+ Button,
+ Typography,
+ Skeleton,
+ Chip,
+ Grid,
+ Paper,
+ Divider,
+ useTheme,
+ TextField,
+ InputAdornment,
+} from "@mui/material";
+import {
+ Search,
+ Public,
+ Language,
+ LocationOn,
+ Cloud,
+} from "@mui/icons-material";
+import { useForm, useWatch } from "react-hook-form";
+import CippButtonCard from "../CippCards/CippButtonCard";
+import { ApiGetCall } from "../../api/ApiCall";
+import { CippCopyToClipBoard } from "./CippCopyToClipboard";
+
+// Region icon mapping
+const getRegionIcon = (region) => {
+ const regionUpper = region?.toUpperCase();
+ switch (regionUpper) {
+ case "EU":
+ return ;
+ case "US":
+ return ;
+ case "ASIA":
+ return ;
+ case "GCC":
+ case "GCC-HIGH":
+ return ;
+ case "DE":
+ return ;
+ case "CN":
+ return ;
+ default:
+ return ;
+ }
+};
+
+// Region color mapping
+const getRegionColor = (region) => {
+ const regionUpper = region?.toUpperCase();
+ switch (regionUpper) {
+ case "EU":
+ return "primary";
+ case "US":
+ return "success";
+ case "ASIA":
+ return "warning";
+ case "GCC":
+ case "GCC-HIGH":
+ return "info";
+ case "DE":
+ return "secondary";
+ case "CN":
+ return "error";
+ default:
+ return "default";
+ }
+};
+
+export const CippTenantLookup = () => {
+ const formControl = useForm({ mode: "onBlur" });
+ const domain = useWatch({ control: formControl.control, name: "domain" });
+
+ const getTenant = ApiGetCall({
+ url: "/api/ListExternalTenantInfo",
+ data: { tenant: domain },
+ queryKey: `tenant-${domain}`,
+ waiting: false,
+ });
+
+ const theme = useTheme();
+ const tenantData = getTenant.data;
+ const graphData = tenantData?.GraphRequest;
+ const openIdData = tenantData?.OpenIdConfig;
+ const brandingData = tenantData?.UserTenantBranding?.[0];
+ const [illustrationUrl, setIllustrationUrl] = useState(null);
+ const [tileLogoUrl, setTileLogoUrl] = useState(null);
+
+ // Fetch illustration as blob and convert to object URL
+ useEffect(() => {
+ let currentObjectUrl = null;
+
+ if (brandingData?.Illustration && typeof brandingData.Illustration === "string" && brandingData.Illustration.trim() !== "") {
+ const fetchIllustration = async () => {
+ try {
+ const response = await fetch(brandingData.Illustration);
+ if (response.ok && response.headers.get("content-type")?.startsWith("image/")) {
+ const blob = await response.blob();
+ if (blob.size > 0) {
+ currentObjectUrl = URL.createObjectURL(blob);
+ setIllustrationUrl(currentObjectUrl);
+ } else {
+ setIllustrationUrl(null);
+ }
+ } else {
+ setIllustrationUrl(null);
+ }
+ } catch (error) {
+ console.error("Failed to fetch illustration:", error);
+ setIllustrationUrl(null);
+ }
+ };
+ fetchIllustration();
+ } else {
+ setIllustrationUrl(null);
+ }
+
+ // Cleanup: revoke object URL when component unmounts or illustration changes
+ return () => {
+ if (currentObjectUrl) {
+ URL.revokeObjectURL(currentObjectUrl);
+ }
+ };
+ }, [brandingData?.Illustration]);
+
+ // Cleanup illustration URL on unmount
+ useEffect(() => {
+ return () => {
+ if (illustrationUrl) {
+ URL.revokeObjectURL(illustrationUrl);
+ }
+ };
+ }, [illustrationUrl]);
+
+ // Fetch tile logo as blob and convert to object URL (respects theme, falls back to available logo)
+ useEffect(() => {
+ let currentObjectUrl = null;
+ const isDarkMode = theme.palette.mode === "dark";
+
+ // Determine which logo to use: prefer theme-appropriate, but fall back to whichever is available
+ let logoUrl = null;
+ if (isDarkMode) {
+ logoUrl = brandingData?.TileDarkLogo || brandingData?.TileLogo;
+ } else {
+ logoUrl = brandingData?.TileLogo || brandingData?.TileDarkLogo;
+ }
+
+ if (logoUrl && typeof logoUrl === "string" && logoUrl.trim() !== "") {
+ const fetchLogo = async () => {
+ try {
+ const response = await fetch(logoUrl);
+ if (response.ok && response.headers.get("content-type")?.startsWith("image/")) {
+ const blob = await response.blob();
+ if (blob.size > 0) {
+ currentObjectUrl = URL.createObjectURL(blob);
+ setTileLogoUrl(currentObjectUrl);
+ } else {
+ setTileLogoUrl(null);
+ }
+ } else {
+ setTileLogoUrl(null);
+ }
+ } catch (error) {
+ console.error("Failed to fetch tile logo:", error);
+ setTileLogoUrl(null);
+ }
+ };
+ fetchLogo();
+ } else {
+ setTileLogoUrl(null);
+ }
+
+ // Cleanup: revoke object URL when component unmounts or logo changes
+ return () => {
+ if (currentObjectUrl) {
+ URL.revokeObjectURL(currentObjectUrl);
+ }
+ };
+ }, [brandingData?.TileLogo, brandingData?.TileDarkLogo, theme.palette.mode]);
+
+ // Cleanup tile logo URL on unmount
+ useEffect(() => {
+ return () => {
+ if (tileLogoUrl) {
+ URL.revokeObjectURL(tileLogoUrl);
+ }
+ };
+ }, [tileLogoUrl]);
+
+ return (
+
+
+ {/* Search Section */}
+
+ {
+ e.preventDefault();
+ if (domain && !getTenant.isFetching) {
+ getTenant.refetch();
+ }
+ }}
+ sx={{ width: "100%", maxWidth: "600px", display: "flex", gap: 1 }}
+ >
+ formControl.setValue("domain", e.target.value)}
+ InputProps={{
+ startAdornment: (
+
+
+
+ ),
+ sx: {
+ "& .MuiInputAdornment-root": {
+ marginTop: "0 !important",
+ alignSelf: "center",
+ },
+ },
+ }}
+ />
+
+
+
+
+ {/* Results Section */}
+ {getTenant.isFetching ? (
+
+
+
+ Fetching Results
+
+
+
+
+
+ ) : tenantData ? (
+ <>
+
+
+
+
+ {/* Preview Container with Illustration Background */}
+
+
+ {/* Overlay Content */}
+
+ {/* Tenant Details Container */}
+
+
+ Tenant Information
+
+
+
+
+ Tenant Name
+
+
+
+ {domain || "Not Available"}
+
+ {domain && }
+
+
+
+
+ Default Domain Name
+
+
+
+ {graphData?.defaultDomainName || "Not Available"}
+
+ {graphData?.defaultDomainName && }
+
+
+
+
+ Tenant ID
+
+
+
+ {graphData?.tenantId || "Not Available"}
+
+ {graphData?.tenantId && }
+
+
+
+
+ Tenant Region
+
+ {openIdData?.tenant_region_scope ? (
+
+ ) : (
+
+ Not Available
+
+ )}
+
+
+
+
+ {/* Tile Logo Container */}
+ {(brandingData?.TileLogo || brandingData?.TileDarkLogo) && (
+
+
+ Tenant Logo
+
+ {tileLogoUrl ? (
+ {
+ e.target.style.display = "none";
+ }}
+ />
+ ) : (
+
+ )}
+
+ )}
+
+
+
+ >
+ ) : null}
+
+
+ );
+};
+
+export default CippTenantLookup;
diff --git a/src/components/CippSettings/CippPermissionResults.jsx b/src/components/CippSettings/CippPermissionResults.jsx
index 95c1d8739458..bd8e37000c0e 100644
--- a/src/components/CippSettings/CippPermissionResults.jsx
+++ b/src/components/CippSettings/CippPermissionResults.jsx
@@ -218,7 +218,7 @@ export const CippPermissionResults = (props) => {
/>
)}
- {results?.Results?.AccessTokenDetails?.Scope.length > 0 && (
+ {results?.Results?.AccessTokenDetails?.Scope?.length > 0 && (
<>
{
/>
>
)}
- {results?.Results?.ApplicationTokenDetails?.Roles.length > 0 && (
+ {results?.Results?.ApplicationTokenDetails?.Roles?.length > 0 && (
<>
{
// Preprocess items to mark which should be open
const processedItems = markOpenItems(items, pathname);
- //select a random sponsor image based on priority, priority 1 should be higher than priority 2 or higher
- const currentSettings = useSettings();
- const theme = currentSettings?.currentTheme?.value;
- const sponsorimages = [
- {
- link: "https://rewst.io",
- imagesrc: theme === "light" ? "/sponsors/rewst.png" : "/sponsors/rewst_dark.png",
- priority: 1,
- },
- {
- link: "https://www.domotz.com/cipp-community-free-domotz-beta.php?utm_source=Community_CIPP&utm_medium=Community_CIPP&utm_campaign=Community_CIPP",
- imagesrc: theme === "light" ? "/sponsors/domotz-light.png" : "/sponsors/domotz-dark.png",
- priority: 1,
- },
- {
- link: "https://ninjaone.com",
- imagesrc: theme === "light" ? "/sponsors/ninjaone.png" : "/sponsors/ninjaone_white.png",
- priority: 1,
- },
- {
- link: "https://augmentt.com",
- imagesrc: theme === "light" ? "/sponsors/augmentt-light.png" : "/sponsors/augmentt-dark.png",
- priority: 1,
- },
- {
- link: "https://huntress.com",
- imagesrc: "/sponsors/huntress_teal.png",
- priority: 1,
- },
- {
- link: "https://rightofboom.com/rob-2026-overview/rob-2026-registration/?utm_source=CIPP&utm_medium=referral&utm_campaign=CIPPM365&utm_content=cta_button",
- 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 = () => {
- let totalPriority = 0;
- for (let i = 0; i < sponsorimages.length; i++) {
- totalPriority += sponsorimages[i].priority;
- }
- let random = Math.floor(Math.random() * totalPriority);
- let runningTotal = 0;
- for (let i = 0; i < sponsorimages.length; i++) {
- runningTotal += sponsorimages[i].priority;
- if (random < runningTotal) {
- return sponsorimages[i];
- }
- }
- };
-
- const randomimg = randomSponsorImage();
return (
<>
{profile?.clientPrincipal && profile?.clientPrincipal?.userRoles?.length > 2 && (
@@ -225,38 +167,7 @@ export const SideNav = (props) => {
})}
{" "}
{/* Add this closing tag */}
- {profile?.clientPrincipal && (
- <>
-
-
- This application is sponsored by
-
-
-
window.open(randomimg.link)}
- />
-
- >
- )}
+ {profile?.clientPrincipal && }
{" "}
{/* Closing tag for the parent Box */}
diff --git a/src/pages/cipp/preferences.js b/src/pages/cipp/preferences.js
index bd15fbf9ae70..fd99bf3a10a7 100644
--- a/src/pages/cipp/preferences.js
+++ b/src/pages/cipp/preferences.js
@@ -254,7 +254,7 @@ const Page = () => {
title="General Settings"
propertyItems={[
{
- label: "Default usage location for users",
+ label: "Default usage location for users *",
value: (
{
),
},
{
- label: "Default Page Size",
+ label: "Default Page Size *",
value: (
{
) : (
- This data has not yet been collected. Collect the data by pressing
- the report button on the top of the page.
+ This data has not yet been collected. Collect the data by selecting Refresh Data from the Actions dropdown on the top of the page.
)}
diff --git a/src/pages/tenant/tools/tenantlookup/index.js b/src/pages/tenant/tools/tenantlookup/index.js
index aea206a02e98..0f178081e8cb 100644
--- a/src/pages/tenant/tools/tenantlookup/index.js
+++ b/src/pages/tenant/tools/tenantlookup/index.js
@@ -1,22 +1,8 @@
-import { Box, Button, Container, Typography, Skeleton, Link } from "@mui/material";
-import { Grid } from "@mui/system";
+import { Box, Container } from "@mui/material";
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";
-import CippFormComponent from "../../../../components/CippComponents/CippFormComponent";
-import { ApiGetCall } from "../../../../api/ApiCall";
+import CippTenantLookup from "../../../../components/CippComponents/CippTenantLookup";
const Page = () => {
- const formControl = useForm({ mode: "onBlur" });
- const domain = useWatch({ control: formControl.control, name: "domain" });
- const getTenant = ApiGetCall({
- url: "/api/ListExternalTenantInfo",
- data: { tenant: domain },
- queryKey: `tenant-${domain}`,
- waiting: false,
- });
-
return (
{
}}
>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Results Card */}
- {getTenant.isFetching ? (
-
-
-
-
-
-
-
-
-
- ) : getTenant.data ? (
-
-
-
-
-
- Tenant Name: {domain}
-
-
- Tenant Id: {getTenant.data?.GraphRequest?.tenantId}
-
-
- Default Domain Name:{" "}
- {getTenant.data?.GraphRequest?.defaultDomainName}
-
-
- Tenant Brand Name :{" "}
- {getTenant.data?.GraphRequest?.federationBrandName
- ? getTenant.data?.GraphRequest?.federationBrandName
- : "Not Specified"}
-
-
- Tenant Region:{" "}
- {getTenant.data?.OpenIdConfig?.tenant_region_scope}
-
-
-
-
-
- ) : null}
-
+
);
diff --git a/src/pages/tools/community-repos/index.js b/src/pages/tools/community-repos/index.js
index 06706162ea9f..4213f74127d9 100644
--- a/src/pages/tools/community-repos/index.js
+++ b/src/pages/tools/community-repos/index.js
@@ -140,7 +140,7 @@ const Page = () => {
const watchIncludeForks = searchForm.watch("includeforks");
const handleSearch = () => {
- const searchTerms = watchSearchTerm.map((t) => t.value) ?? [];
+ const searchTerms = watchSearchTerm?.map((t) => t.value) ?? [];
searchMutation.mutate({
url: "/api/ExecGitHubAction",
data: {
diff --git a/yarn.lock b/yarn.lock
index 207a28973850..435c9b238443 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1584,10 +1584,10 @@
prop-types "^15.8.1"
react-is "^19.2.3"
-"@mui/x-date-pickers@^8.27.0":
- version "8.27.0"
- resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-8.27.0.tgz#fdc2f4a116cab28e86b981105e902455c5b4432c"
- integrity sha512-mw47IgelP5qFSBANqxUhqDEly2XO9RT/BcNKwgumy8BmmdosrGAmTev8dgFMoWg20iPHxEczlpBdDGyV6ht0jg==
+"@mui/x-date-pickers@^8.27.2":
+ version "8.27.2"
+ resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-8.27.2.tgz#5ada1fb3adffff3e0fd0fee7702fba7f770dca68"
+ integrity sha512-06LFkHFRXJ2O9DMXtWAA3kY0jpbL7XH8iqa8L5cBlN+8bRx/UVLKlZYlhGv06C88jF9kuZWY1bUgrv/EoY/2Ww==
dependencies:
"@babel/runtime" "^7.28.4"
"@mui/utils" "^7.3.5"
@@ -6070,10 +6070,10 @@ ms@^2.1.1, ms@^2.1.3:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
-mui-tiptap@^1.14.0:
- version "1.28.1"
- resolved "https://registry.yarnpkg.com/mui-tiptap/-/mui-tiptap-1.28.1.tgz#d494eed6fa78897791815e1ec86b8abc17af1e70"
- integrity sha512-tKSToZBti+qMkHHPYU33ws4bnQ7ssIKUgpCRfVRAkEU5hC7jSFRdEjlSyDiympQaXgSe0XBxdD+XxF25WXm9uA==
+mui-tiptap@^1.29.0:
+ version "1.29.0"
+ resolved "https://registry.yarnpkg.com/mui-tiptap/-/mui-tiptap-1.29.0.tgz#5316a5aad8f9c6d14d317daf17e1d5b3e5a59477"
+ integrity sha512-2fupRo0RI2o+xX59qoSGpNertfMoDGLhxpzZjXP/loXlrbzuIVw+AGbUg4zeHbFJrbXH2RGIiPbI4ysPbvO8mg==
dependencies:
clsx "^2.1.1"
encodeurl "^2.0.0"
@@ -6754,13 +6754,14 @@ react-html-parser@^2.0.2:
dependencies:
htmlparser2 "^3.9.0"
-react-i18next@15.7.3:
- version "15.7.3"
- resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-15.7.3.tgz#2eba235247dff0cbf9f0338e2ab85e10e127aa54"
- integrity sha512-AANws4tOE+QSq/IeMF/ncoHlMNZaVLxpa5uUGW1wjike68elVYr0018L9xYoqBr1OFO7G7boDPrbn0HpMCJxTw==
+react-i18next@16.2.4:
+ version "16.2.4"
+ resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-16.2.4.tgz#0a6f3eb982b702b8810323c97c09e7452448e03b"
+ integrity sha512-pvbcPQ+YuQQoRkKBA4VCU9aO8dOgP/vdKEizIYXcAk3+AmI8yQKSJaCzxQQu4Kgg2zWZm3ax9KqHv8ItUlRY0A==
dependencies:
"@babel/runtime" "^7.27.6"
html-parse-stringify "^3.0.1"
+ use-sync-external-store "^1.6.0"
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
@@ -6897,10 +6898,10 @@ react-virtualized-auto-sizer@^1.0.26:
resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.26.tgz#e9470ef6a778dc4f1d5fd76305fa2d8b610c357a"
integrity sha512-CblNyiNVw2o+hsa5/49NH2ogGxZ+t+3aweRvNSq7TVjDIlwk7ir4lencEg5HxHeSzwNarSkNkiu0qJSOXtxm5A==
-react-virtuoso@^4.12.8:
- version "4.17.0"
- resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.17.0.tgz#e81f2da99792cfd9317e910b243d847ebeb09248"
- integrity sha512-od3pi2v13v31uzn5zPXC2u3ouISFCVhjFVFch2VvS2Cx7pWA2F1aJa3XhNTN2F07M3lhfnMnsmGeH+7wZICr7w==
+react-virtuoso@^4.18.1:
+ version "4.18.1"
+ resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.18.1.tgz#3eb7078f2739a31b96c723374019e587deeb6ebc"
+ integrity sha512-KF474cDwaSb9+SJ380xruBB4P+yGWcVkcu26HtMqYNMTYlYbrNy8vqMkE+GpAApPPufJqgOLMoWMFG/3pJMXUA==
react-window@^2.2.5:
version "2.2.5"
@@ -6944,10 +6945,10 @@ readable-stream@~1.0.17, readable-stream@~1.0.27-1:
isarray "0.0.1"
string_decoder "~0.10.x"
-recharts@^3.6.0:
- version "3.6.0"
- resolved "https://registry.yarnpkg.com/recharts/-/recharts-3.6.0.tgz#403f0606581153601857e46733277d1411633df3"
- integrity sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg==
+recharts@^3.7.0:
+ version "3.7.0"
+ resolved "https://registry.yarnpkg.com/recharts/-/recharts-3.7.0.tgz#e3c72656ba18841085293e83bfc9a4f78b20abdd"
+ integrity sha512-l2VCsy3XXeraxIID9fx23eCb6iCBsxUQDnE8tWm6DFdszVAO7WVY/ChAD9wVit01y6B2PMupYiMmQwhgPHc9Ew==
dependencies:
"@reduxjs/toolkit" "1.x.x || 2.x.x"
clsx "^2.1.1"