diff --git a/src/components/CippComponents/CippAlertSnoozeDialog.jsx b/src/components/CippComponents/CippAlertSnoozeDialog.jsx index cc5638433411..db8ced18cec8 100644 --- a/src/components/CippComponents/CippAlertSnoozeDialog.jsx +++ b/src/components/CippComponents/CippAlertSnoozeDialog.jsx @@ -8,6 +8,7 @@ import { RadioGroup, FormControlLabel, Radio, + TextField, Typography, Box, Stack, @@ -35,6 +36,7 @@ export const CippAlertSnoozeDialog = ({ relatedQueryKeys, }) => { const [duration, setDuration] = useState('7') + const [reason, setReason] = useState('') const [submitted, setSubmitted] = useState(false) const snoozeRequest = ApiPostCall({ @@ -50,6 +52,7 @@ export const CippAlertSnoozeDialog = ({ TenantFilter: tenantFilter, AlertItem: alertItem, Duration: parseInt(duration, 10), + Reason: reason, }, }) } @@ -58,6 +61,7 @@ export const CippAlertSnoozeDialog = ({ setSubmitted(false) snoozeRequest.reset() setDuration('7') + setReason('') onClose() } @@ -125,6 +129,16 @@ export const CippAlertSnoozeDialog = ({ /> ))} + setReason(e.target.value)} + fullWidth + multiline + minRows={2} + sx={{ mt: 2 }} + placeholder="Why is this alert being snoozed?" + /> ) : ( diff --git a/src/components/CippComponents/CippIntuneDeviceActions.jsx b/src/components/CippComponents/CippIntuneDeviceActions.jsx index 0fa86320f95d..c2838ce81203 100644 --- a/src/components/CippComponents/CippIntuneDeviceActions.jsx +++ b/src/components/CippComponents/CippIntuneDeviceActions.jsx @@ -13,6 +13,7 @@ import { AutoMode, Recycling, ManageAccounts, + GroupAdd, } from '@mui/icons-material' // Shared between the MEM devices list page and the View Device detail page. @@ -73,6 +74,61 @@ export const getIntuneDeviceActions = ({ tenantFilter } = {}) => [ ], confirmText: 'Select the User to set as the primary user for [deviceName]', }, + { + label: 'Add to Group', + type: 'POST', + icon: , + url: '/api/EditGroup', + customDataformatter: (row, action, formData) => { + // Build the device list from selected devices - the backend resolves the Entra + // directory object id from azureADDeviceId + const rows = Array.isArray(row) ? row : [row] + const addDevice = rows.map((r) => ({ + label: r.deviceName, + value: r.azureADDeviceId, + addedFields: { + azureADDeviceId: r.azureADDeviceId, + deviceName: r.deviceName, + }, + })) + + // Handle multiple groups - return an array of requests (one per group) + const selectedGroups = Array.isArray(formData.groupId) ? formData.groupId : [formData.groupId] + + return selectedGroups.map((group) => ({ + AddDevice: addDevice, + tenantFilter: tenantFilter, + groupId: group, + })) + }, + fields: [ + { + type: 'autoComplete', + name: 'groupId', + label: 'Select groups to add the device to', + multiple: true, + creatable: false, + validators: { required: 'Please select at least one group' }, + api: { + url: '/api/ListGroups', + labelField: (option) => + option?.calculatedGroupType + ? `${option.displayName} (${option.calculatedGroupType})` + : (option?.displayName ?? ''), + valueField: 'id', + addedField: { + groupType: 'groupType', + groupName: 'displayName', + }, + queryKey: `groups-${tenantFilter}`, + showRefresh: true, + }, + }, + ], + confirmText: 'Are you sure you want to add [deviceName] to the selected groups?', + multiPost: false, + allowResubmit: true, + }, { label: 'Rename Device', type: 'POST', diff --git a/src/components/CippComponents/CippReportToolbar.jsx b/src/components/CippComponents/CippReportToolbar.jsx index bc7a9274a56c..f42b468ef36d 100644 --- a/src/components/CippComponents/CippReportToolbar.jsx +++ b/src/components/CippComponents/CippReportToolbar.jsx @@ -18,8 +18,14 @@ export const CippReportToolbar = () => { const [deleteDialog, setDeleteDialog] = useState({ open: false }) const [refreshDialog, setRefreshDialog] = useState({ open: false }) + const defaultReportId = + settings.UserSpecificSettings?.defaultTestSuite?.value || + settings.defaultTestSuite?.value || + 'ztna' const selectedReport = - router.isReady && !router.query.reportId ? 'ztna' : router.query.reportId || 'ztna' + router.isReady && !router.query.reportId + ? defaultReportId + : router.query.reportId || defaultReportId const formControl = useForm({ mode: 'onChange' }) const reportIdValue = useWatch({ control: formControl.control }) diff --git a/src/pages/cipp/preferences.js b/src/pages/cipp/preferences.js index 6b5644ac595a..129674774bed 100644 --- a/src/pages/cipp/preferences.js +++ b/src/pages/cipp/preferences.js @@ -38,6 +38,12 @@ const Page = () => { queryKey: "authmecipp", }); + // Test suites for the default Home page suite selector (shared cache with the dashboard) + const reportsApi = ApiGetCall({ + url: "/api/ListTestReports", + queryKey: "ListTestReports", + }); + const cleanedSettings = { ...settings }; if (cleanedSettings.offboardingDefaults?.keepCopy) { @@ -287,6 +293,23 @@ const Page = () => { /> ), }, + { + label: "Default test suite on the Home page", + value: ( + ({ + value: report.id, + label: report.name, + }))} + isFetching={reportsApi.isFetching} + /> + ), + }, { label: "Added Attributes when creating a new user", value: ( diff --git a/src/pages/cipp/snooze-alert.js b/src/pages/cipp/snooze-alert.js index c902fa4f59c0..e998acb7bd16 100644 --- a/src/pages/cipp/snooze-alert.js +++ b/src/pages/cipp/snooze-alert.js @@ -15,7 +15,7 @@ const durationLabel = (d) => { const Page = () => { const router = useRouter() - const { cmdlet, tenant, data, duration } = router.query + const { cmdlet, tenant, data, duration, reason } = router.query const [submitted, setSubmitted] = useState(false) const [parseError, setParseError] = useState(null) @@ -54,9 +54,10 @@ const Page = () => { TenantFilter: tenant, AlertItem: alertItem, Duration: durationNum, + Reason: reason || '', }, }) - }, [router.isReady, cmdlet, tenant, data, duration]) + }, [router.isReady, cmdlet, tenant, data, duration, reason]) const preview = (() => { if (!data) return null diff --git a/src/pages/dashboardv2/custom/index.js b/src/pages/dashboardv2/custom/index.js index aae13a0922e2..1cc50d284964 100644 --- a/src/pages/dashboardv2/custom/index.js +++ b/src/pages/dashboardv2/custom/index.js @@ -14,8 +14,14 @@ const Page = () => { const settings = useSettings() const { currentTenant } = settings const router = useRouter() + const defaultReportId = + settings.UserSpecificSettings?.defaultTestSuite?.value || + settings.defaultTestSuite?.value || + 'ztna' const selectedReport = - router.isReady && !router.query.reportId ? 'ztna' : router.query.reportId || 'ztna' + router.isReady && !router.query.reportId + ? defaultReportId + : router.query.reportId || defaultReportId const testsApi = ApiGetCall({ url: '/api/ListTests', diff --git a/src/pages/dashboardv2/devices/index.js b/src/pages/dashboardv2/devices/index.js index 49c8ca0028a6..7142a5a4f682 100644 --- a/src/pages/dashboardv2/devices/index.js +++ b/src/pages/dashboardv2/devices/index.js @@ -16,8 +16,14 @@ const Page = () => { const { currentTenant } = settings const router = useRouter() // Only use default if router is ready and reportId is still not present + const defaultReportId = + settings.UserSpecificSettings?.defaultTestSuite?.value || + settings.defaultTestSuite?.value || + 'ztna' const selectedReport = - router.isReady && !router.query.reportId ? 'ztna' : router.query.reportId || 'ztna' + router.isReady && !router.query.reportId + ? defaultReportId + : router.query.reportId || defaultReportId const testsApi = ApiGetCall({ url: '/api/ListTests', diff --git a/src/pages/dashboardv2/identity/index.js b/src/pages/dashboardv2/identity/index.js index 0d460ab7dddd..9af4b9b32939 100644 --- a/src/pages/dashboardv2/identity/index.js +++ b/src/pages/dashboardv2/identity/index.js @@ -15,8 +15,14 @@ const Page = () => { const { currentTenant } = settings const router = useRouter() // Only use default if router is ready and reportId is still not present + const defaultReportId = + settings.UserSpecificSettings?.defaultTestSuite?.value || + settings.defaultTestSuite?.value || + 'ztna' const selectedReport = - router.isReady && !router.query.reportId ? 'ztna' : router.query.reportId || 'ztna' + router.isReady && !router.query.reportId + ? defaultReportId + : router.query.reportId || defaultReportId const testsApi = ApiGetCall({ url: '/api/ListTests', diff --git a/src/pages/dashboardv2/index.js b/src/pages/dashboardv2/index.js index eb03b7618038..bd7dda13bdd1 100644 --- a/src/pages/dashboardv2/index.js +++ b/src/pages/dashboardv2/index.js @@ -41,10 +41,16 @@ const Page = () => { const [portalMenuItems, setPortalMenuItems] = useState([]) const isWide = useMediaQuery('(min-width:1513px)') const [reportsMenuAnchor, setReportsMenuAnchor] = useState(null) - // Get reportId from query params or default to "ztna" + // Get reportId from query params or default to the user's preferred suite (Preferences page) // Only use default if router is ready and reportId is still not present + const defaultReportId = + settings.UserSpecificSettings?.defaultTestSuite?.value || + settings.defaultTestSuite?.value || + 'ztna' const selectedReport = - router.isReady && !router.query.reportId ? 'ztna' : router.query.reportId || 'ztna' + router.isReady && !router.query.reportId + ? defaultReportId + : router.query.reportId || defaultReportId // Fetch available reports (shared cache with CippReportToolbar) const reportsApi = ApiGetCall({ diff --git a/src/pages/identity/administration/groups/edit.jsx b/src/pages/identity/administration/groups/edit.jsx index 5e37a98ad364..6562e183f9a7 100644 --- a/src/pages/identity/administration/groups/edit.jsx +++ b/src/pages/identity/administration/groups/edit.jsx @@ -47,6 +47,7 @@ const EditGroup = () => { RemoveOwner: [], AddContact: [], RemoveContact: [], + AddDevice: [], AddLicenses: [], RemoveLicenses: [], visibility: 'Public', @@ -113,6 +114,7 @@ const EditGroup = () => { RemoveOwner: [], AddContact: [], RemoveContact: [], + AddDevice: [], AddLicenses: [], RemoveLicenses: [], } @@ -310,6 +312,47 @@ const EditGroup = () => { /> + {groupType !== 'Distribution List' && groupType !== 'Mail-Enabled Security' && ( + + + option?.operatingSystem + ? `${option.displayName} (${option.operatingSystem})` + : (option?.displayName ?? ''), + valueField: 'id', + addedField: { + displayName: 'displayName', + }, + queryKey: `ListDevices-${tenantFilter}`, + dataFilter: (options) => + options.filter( + (option) => !groupInfo.data?.members?.some((m) => m.id === option.value) + ), + showRefresh: true, + }} + /> + + )} + Remove Members diff --git a/src/pages/tenant/administration/alert-configuration/snoozed-alerts.js b/src/pages/tenant/administration/alert-configuration/snoozed-alerts.js index b434ceba34fa..166c67b32868 100644 --- a/src/pages/tenant/administration/alert-configuration/snoozed-alerts.js +++ b/src/pages/tenant/administration/alert-configuration/snoozed-alerts.js @@ -32,6 +32,7 @@ const Page = () => { 'CmdletName', 'Tenant', 'ContentPreview', + 'SnoozeReason', 'SnoozedBy', 'Status', 'RemainingDays',