diff --git a/.github/workflows/Assign_Issue_Volunteer.yml b/.github/workflows/Assign_Issue_Volunteer.yml index 23ef1d16ffc0..fe199038b2e5 100644 --- a/.github/workflows/Assign_Issue_Volunteer.yml +++ b/.github/workflows/Assign_Issue_Volunteer.yml @@ -5,6 +5,6 @@ jobs: build: runs-on: ubuntu-slim steps: - - uses: bhermann/issue-volunteer@v0.1.12 + - uses: bhermann/issue-volunteer@v0.1.20 with: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/Comment_on_Issues.yml b/.github/workflows/Comment_on_Issues.yml index c408d8b38c84..6a6014f38dcd 100644 --- a/.github/workflows/Comment_on_Issues.yml +++ b/.github/workflows/Comment_on_Issues.yml @@ -12,7 +12,7 @@ jobs: issues: write steps: - name: Add Comment - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v5 with: issue-number: ${{ github.event.issue.number }} body: | diff --git a/.github/workflows/auto_comments.yml b/.github/workflows/auto_comments.yml index 6cd003a36ae8..9a7664c9007c 100644 --- a/.github/workflows/auto_comments.yml +++ b/.github/workflows/auto_comments.yml @@ -17,7 +17,7 @@ jobs: # 1) If the comment includes '!notasponsor', delete it using GitHub Script - name: Delete !notasponsor comment if: contains(github.event.comment.body, '!notasponsor') - uses: actions/github-script@v6 + uses: actions/github-script@v8 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | @@ -30,7 +30,7 @@ jobs: # 2) Post a sponsor-specific reply - name: Reply to !notasponsor if: contains(github.event.comment.body, '!notasponsor') - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v5 with: issue-number: ${{ github.event.issue.number }} body: | @@ -51,7 +51,7 @@ jobs: # 3) If the comment includes '!support', classify as a support request - name: Reply to !support if: contains(github.event.comment.body, '!support') - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v5 with: issue-number: ${{ github.event.issue.number }} body: | @@ -69,7 +69,7 @@ jobs: # 4) If the comment includes '!incomplete', note the bug or feature request is incomplete - name: Reply to !incomplete if: contains(github.event.comment.body, '!incomplete') - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v5 with: issue-number: ${{ github.event.issue.number }} body: | diff --git a/.github/workflows/cipp_dev_build.yml b/.github/workflows/cipp_dev_build.yml index 31d0f846d016..432d9363cade 100644 --- a/.github/workflows/cipp_dev_build.yml +++ b/.github/workflows/cipp_dev_build.yml @@ -47,7 +47,7 @@ jobs: # Upload to Azure Blob Storage - name: Azure Blob Upload - uses: LanceMcCarthy/Action-AzureBlobUpload@v3.3.1 + uses: LanceMcCarthy/Action-AzureBlobUpload@v3.7.0 with: connection_string: ${{ secrets.AZURE_CONNECTION_STRING }} container_name: cipp diff --git a/.github/workflows/cipp_frontend_build.yml b/.github/workflows/cipp_frontend_build.yml index d6df65f2320e..5db059b438a8 100644 --- a/.github/workflows/cipp_frontend_build.yml +++ b/.github/workflows/cipp_frontend_build.yml @@ -47,7 +47,7 @@ jobs: # Upload to Azure Blob Storage - name: Azure Blob Upload - uses: LanceMcCarthy/Action-AzureBlobUpload@v3.3.1 + uses: LanceMcCarthy/Action-AzureBlobUpload@v3.7.0 with: connection_string: ${{ secrets.AZURE_CONNECTION_STRING }} container_name: cipp diff --git a/.github/workflows/label_sponsor_requests.yml b/.github/workflows/label_sponsor_requests.yml index bb9d6a31b427..28b84ea4e9b6 100644 --- a/.github/workflows/label_sponsor_requests.yml +++ b/.github/workflows/label_sponsor_requests.yml @@ -11,7 +11,7 @@ jobs: issues: write steps: - name: Sponsor Labels - uses: JasonEtco/is-sponsor-label-action@v1.2.0 + uses: JasonEtco/is-sponsor-label-action@v2.0.0 with: label: "Sponsor Priority" env: diff --git a/.github/workflows/pr_check.yml b/.github/workflows/pr_check.yml index 08cce1de130b..15b52ecd4e82 100644 --- a/.github/workflows/pr_check.yml +++ b/.github/workflows/pr_check.yml @@ -25,7 +25,7 @@ jobs: github.event.pull_request.head.repo.fork == true && ((github.event.pull_request.head.ref == 'main' || github.event.pull_request.head.ref == 'master') || (github.event.pull_request.base.ref == 'main' || github.event.pull_request.base.ref == 'master')) - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | diff --git a/package.json b/package.json index d575cae8d4cb..11957bf4dc59 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "10.1.1", + "version": "10.1.2", "author": "CIPP Contributors", "homepage": "https://cipp.app/", "bugs": { @@ -51,7 +51,7 @@ "@tiptap/extension-table": "^3.19.0", "@tiptap/pm": "^3.4.1", "@tiptap/react": "^3.4.1", - "@tiptap/starter-kit": "^3.19.0", + "@tiptap/starter-kit": "^3.20.0", "@uiw/react-json-view": "^2.0.0-alpha.41", "@vvo/tzdb": "^6.198.0", "apexcharts": "5.3.5", @@ -61,9 +61,9 @@ "export-to-csv": "^1.3.0", "formik": "2.4.9", "gray-matter": "4.0.3", - "i18next": "25.5.2", + "i18next": "25.8.13", "javascript-time-ago": "^2.6.2", - "jspdf": "^4.1.0", + "jspdf": "^4.2.0", "jspdf-autotable": "^5.0.7", "leaflet": "^1.9.4", "leaflet-defaulticon-compatibility": "^0.1.2", diff --git a/public/version.json b/public/version.json index eb5d53d8afba..22ac50c109c0 100644 --- a/public/version.json +++ b/public/version.json @@ -1,3 +1,3 @@ { - "version": "10.1.1" -} + "version": "10.1.2" +} \ No newline at end of file diff --git a/src/components/CippComponents/CippAddUserDrawer.jsx b/src/components/CippComponents/CippAddUserDrawer.jsx index 6e8e333b317f..297fa91dd58f 100644 --- a/src/components/CippComponents/CippAddUserDrawer.jsx +++ b/src/components/CippComponents/CippAddUserDrawer.jsx @@ -18,7 +18,7 @@ export const CippAddUserDrawer = ({ const userSettingsDefaults = useSettings(); const formControl = useForm({ - mode: "onBlur", + mode: "onChange", defaultValues: { tenantFilter: userSettingsDefaults.currentTenant, usageLocation: userSettingsDefaults.usageLocation, @@ -52,22 +52,36 @@ export const CippAddUserDrawer = ({ } newFields.tenantFilter = userSettingsDefaults.currentTenant; + // Preserve the currently selected template when copying properties + const currentTemplate = formControl.getValues("userTemplate"); + if (currentTemplate) { + newFields.userTemplate = currentTemplate; + } + formControl.reset(newFields); } }, [formValues]); useEffect(() => { if (createUser.isSuccess) { - formControl.reset({ + const resetValues = { tenantFilter: userSettingsDefaults.currentTenant, usageLocation: userSettingsDefaults.usageLocation, - }); + }; + + // Preserve the default template if it exists + const currentTemplate = formControl.getValues("userTemplate"); + if (currentTemplate?.addedFields?.defaultForTenant) { + resetValues.userTemplate = currentTemplate; + } + + formControl.reset(resetValues); } }, [createUser.isSuccess]); - const handleSubmit = () => { - formControl.trigger(); - if (!isValid) { + const handleSubmit = async () => { + const isFormValid = await formControl.trigger(); + if (!isFormValid) { return; } const values = formControl.getValues(); @@ -84,17 +98,40 @@ export const CippAddUserDrawer = ({ const handleCloseDrawer = () => { setDrawerVisible(false); - formControl.reset({ + const resetValues = { tenantFilter: userSettingsDefaults.currentTenant, usageLocation: userSettingsDefaults.usageLocation, - }); + }; + + // Preserve the default template if it exists + const currentTemplate = formControl.getValues("userTemplate"); + if (currentTemplate?.addedFields?.defaultForTenant) { + resetValues.userTemplate = currentTemplate; + } + + formControl.reset(resetValues); + }; + + const handleOpenDrawer = () => { + const resetValues = { + tenantFilter: userSettingsDefaults.currentTenant, + usageLocation: userSettingsDefaults.usageLocation, + }; + + const currentTemplate = formControl.getValues("userTemplate"); + if (currentTemplate?.addedFields?.defaultForTenant) { + resetValues.userTemplate = currentTemplate; + } + + formControl.reset(resetValues); + setDrawerVisible(true); }; return ( <> setDrawerVisible(true)} + onClick={handleOpenDrawer} startIcon={} > {buttonText} @@ -117,8 +154,8 @@ export const CippAddUserDrawer = ({ {createUser.isPending ? "Creating User..." : createUser.isSuccess - ? "Create Another User" - : "Create User"} + ? "Create Another User" + : "Create User"} + + } + /> + { + if (response?.QueueId) { + setSyncQueueId(response.QueueId); + } + }, + }} + /> + ); }; -Page.getLayout = (page) => {page}; +Page.getLayout = (page) => {page}; export default Page; diff --git a/src/pages/email/resources/management/list-rooms/edit.jsx b/src/pages/email/resources/management/list-rooms/edit.jsx index a8125a3f8be8..9505c7e4c19b 100644 --- a/src/pages/email/resources/management/list-rooms/edit.jsx +++ b/src/pages/email/resources/management/list-rooms/edit.jsx @@ -115,6 +115,7 @@ const EditRoomMailbox = () => { } : null, }); + void formControl.trigger(); } }, [roomInfo.isSuccess, roomInfo.data]); diff --git a/src/pages/email/tools/mailbox-restores/add.jsx b/src/pages/email/tools/mailbox-restores/add.jsx index a238c0c29b1a..0d9a74ba686a 100644 --- a/src/pages/email/tools/mailbox-restores/add.jsx +++ b/src/pages/email/tools/mailbox-restores/add.jsx @@ -126,6 +126,7 @@ const MailboxRestoreForm = () => { valueField: "UPN", addedField: { displayName: "displayName", ExchangeGuid: "ExchangeGuid" }, url: "/api/ListMailboxes", + data: { UseReportDB: true }, }} validators={{ validate: (value) => (value ? true : "Please select a target mailbox.") }} /> diff --git a/src/pages/endpoint/MEM/list-appprotection-policies/index.js b/src/pages/endpoint/MEM/list-appprotection-policies/index.js index 3fe79bab30da..0c8fc70161fc 100644 --- a/src/pages/endpoint/MEM/list-appprotection-policies/index.js +++ b/src/pages/endpoint/MEM/list-appprotection-policies/index.js @@ -1,320 +1,23 @@ 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 "../../../../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" }, - { label: "Append to existing assignments", value: "append" }, -]; - -const assignmentFilterTypeOptions = [ - { label: "Include - Apply policy to devices matching filter", value: "include" }, - { label: "Exclude - Apply policy to devices NOT matching filter", value: "exclude" }, -]; +import { useCippIntunePolicyActions } from "../../../../components/CippComponents/CippIntunePolicyActions.jsx"; const Page = () => { const pageTitle = "App Protection & Configuration Policies"; const cardButtonPermissions = ["Endpoint.MEM.ReadWrite"]; const tenant = useSettings().currentTenant; - const actions = [ - { - label: "Create template based on policy", - type: "POST", - url: "/api/AddIntuneTemplate", - data: { - ID: "id", - URLName: "URLName", - }, - confirmText: "Are you sure you want to create a template based on this policy?", - icon: , - color: "info", - }, - { - label: "Assign to All Users", - type: "POST", - url: "/api/ExecAssignPolicy", - data: { - AssignTo: "allLicensedUsers", - ID: "id", - type: "URLName", - platformType: "!deviceAppManagement", - }, - fields: [ - { - type: "radio", - name: "assignmentMode", - label: "Assignment mode", - options: assignmentModeOptions, - defaultValue: "replace", - helperText: - "Replace will overwrite existing assignments. Append keeps current assignments and adds/overwrites only for the selected groups.", - }, - { - type: "autoComplete", - name: "assignmentFilter", - label: "Assignment Filter (Optional)", - multiple: false, - creatable: false, - api: { - url: "/api/ListAssignmentFilters", - queryKey: `ListAssignmentFilters-${tenant}`, - labelField: (filter) => filter.displayName, - valueField: "displayName", - }, - }, - { - type: "radio", - name: "assignmentFilterType", - label: "Assignment Filter Mode", - options: assignmentFilterTypeOptions, - defaultValue: "include", - helperText: "Choose whether to include or exclude devices matching the filter.", - }, - ], - customDataformatter: (row, action, formData) => { - const tenantFilterValue = tenant === "AllTenants" && row?.Tenant ? row.Tenant : tenant; - return { - tenantFilter: tenantFilterValue, - ID: row?.id, - type: row?.URLName, - platformType: "deviceAppManagement", - AssignTo: "allLicensedUsers", - assignmentMode: formData?.assignmentMode || "replace", - AssignmentFilterName: formData?.assignmentFilter?.value || null, - AssignmentFilterType: formData?.assignmentFilter?.value - ? formData?.assignmentFilterType || "include" - : null, - }; - }, - confirmText: 'Are you sure you want to assign "[displayName]" to all users?', - icon: , - color: "info", - }, - { - label: "Assign to All Devices", - type: "POST", - url: "/api/ExecAssignPolicy", - data: { - AssignTo: "AllDevices", - ID: "id", - type: "URLName", - platformType: "!deviceAppManagement", - }, - fields: [ - { - type: "radio", - name: "assignmentMode", - label: "Assignment mode", - options: assignmentModeOptions, - defaultValue: "replace", - helperText: - "Replace will overwrite existing assignments. Append keeps current assignments and adds/overwrites only for the selected groups.", - }, - { - type: "autoComplete", - name: "assignmentFilter", - label: "Assignment Filter (Optional)", - multiple: false, - creatable: false, - api: { - url: "/api/ListAssignmentFilters", - queryKey: `ListAssignmentFilters-${tenant}`, - labelField: (filter) => filter.displayName, - valueField: "displayName", - }, - }, - { - type: "radio", - name: "assignmentFilterType", - label: "Assignment Filter Mode", - options: assignmentFilterTypeOptions, - defaultValue: "include", - helperText: "Choose whether to include or exclude devices matching the filter.", - }, - ], - customDataformatter: (row, action, formData) => { - const tenantFilterValue = tenant === "AllTenants" && row?.Tenant ? row.Tenant : tenant; - return { - tenantFilter: tenantFilterValue, - ID: row?.id, - type: row?.URLName, - platformType: "deviceAppManagement", - AssignTo: "AllDevices", - assignmentMode: formData?.assignmentMode || "replace", - AssignmentFilterName: formData?.assignmentFilter?.value || null, - AssignmentFilterType: formData?.assignmentFilter?.value - ? formData?.assignmentFilterType || "include" - : null, - }; - }, - confirmText: 'Are you sure you want to assign "[displayName]" to all devices?', - icon: , - color: "info", + const actions = useCippIntunePolicyActions(tenant, "URLName", { + templateData: { + ID: "id", + URLName: "managedAppPolicies", }, - { - label: "Assign Globally (All Users / All Devices)", - type: "POST", - url: "/api/ExecAssignPolicy", - data: { - AssignTo: "AllDevicesAndUsers", - ID: "id", - type: "URLName", - platformType: "!deviceAppManagement", - }, - fields: [ - { - type: "radio", - name: "assignmentMode", - label: "Assignment mode", - options: assignmentModeOptions, - defaultValue: "replace", - helperText: - "Replace will overwrite existing assignments. Append keeps current assignments and adds/overwrites only for the selected groups.", - }, - { - type: "autoComplete", - name: "assignmentFilter", - label: "Assignment Filter (Optional)", - multiple: false, - creatable: false, - api: { - url: "/api/ListAssignmentFilters", - queryKey: `ListAssignmentFilters-${tenant}`, - labelField: (filter) => filter.displayName, - valueField: "displayName", - }, - }, - { - type: "radio", - name: "assignmentFilterType", - label: "Assignment Filter Mode", - options: assignmentFilterTypeOptions, - defaultValue: "include", - helperText: "Choose whether to include or exclude devices matching the filter.", - }, - ], - customDataformatter: (row, action, formData) => { - const tenantFilterValue = tenant === "AllTenants" && row?.Tenant ? row.Tenant : tenant; - return { - tenantFilter: tenantFilterValue, - ID: row?.id, - type: row?.URLName, - platformType: "deviceAppManagement", - AssignTo: "AllDevicesAndUsers", - assignmentMode: formData?.assignmentMode || "replace", - AssignmentFilterName: formData?.assignmentFilter?.value || null, - AssignmentFilterType: formData?.assignmentFilter?.value - ? formData?.assignmentFilterType || "include" - : null, - }; - }, - confirmText: 'Are you sure you want to assign "[displayName]" to all users and devices?', - icon: , - color: "info", - }, - { - label: "Assign to Custom Group", - type: "POST", - url: "/api/ExecAssignPolicy", - icon: , - color: "info", - confirmText: 'Select the target groups for "[displayName]".', - fields: [ - { - type: "autoComplete", - name: "groupTargets", - label: "Group(s)", - multiple: true, - creatable: false, - allowResubmit: true, - validators: { required: "Please select at least one group" }, - api: { - url: "/api/ListGraphRequest", - dataKey: "Results", - queryKey: `ListPolicyAssignmentGroups-${tenant}`, - labelField: (group) => - group.id ? `${group.displayName} (${group.id})` : group.displayName, - valueField: "id", - addedField: { - description: "description", - }, - data: { - Endpoint: "groups", - manualPagination: true, - $select: "id,displayName,description", - $orderby: "displayName", - $top: 999, - $count: true, - }, - }, - }, - { - type: "radio", - name: "assignmentMode", - label: "Assignment mode", - options: assignmentModeOptions, - defaultValue: "replace", - helperText: - "Replace will overwrite existing assignments. Append keeps current assignments and adds/overwrites only for the selected groups.", - }, - { - type: "autoComplete", - name: "assignmentFilter", - label: "Assignment Filter (Optional)", - multiple: false, - creatable: false, - api: { - url: "/api/ListAssignmentFilters", - queryKey: `ListAssignmentFilters-${tenant}`, - labelField: (filter) => filter.displayName, - valueField: "displayName", - }, - }, - { - type: "radio", - name: "assignmentFilterType", - label: "Assignment Filter Mode", - options: assignmentFilterTypeOptions, - defaultValue: "include", - helperText: "Choose whether to include or exclude devices matching the filter.", - }, - ], - customDataformatter: (row, action, formData) => { - const selectedGroups = Array.isArray(formData?.groupTargets) ? formData.groupTargets : []; - const tenantFilterValue = tenant === "AllTenants" && row?.Tenant ? row.Tenant : tenant; - return { - tenantFilter: tenantFilterValue, - ID: row?.id, - type: row?.URLName, - platformType: "deviceAppManagement", - GroupIds: selectedGroups.map((group) => group.value).filter(Boolean), - GroupNames: selectedGroups.map((group) => group.label).filter(Boolean), - assignmentMode: formData?.assignmentMode || "replace", - AssignmentFilterName: formData?.assignmentFilter?.value || null, - AssignmentFilterType: formData?.assignmentFilter?.value - ? formData?.assignmentFilterType || "include" - : null, - }; - }, - }, - { - label: "Delete Policy", - type: "POST", - url: "/api/RemovePolicy", - data: { - ID: "id", - URLName: "URLName", - }, - confirmText: "Are you sure you want to delete this policy?", - icon: , - color: "danger", - }, - ]; + platformType: "deviceAppManagement", + deleteUrlName: "URLName", + }); const offCanvas = { extendedInfoFields: [ diff --git a/src/pages/endpoint/MEM/list-compliance-policies/index.js b/src/pages/endpoint/MEM/list-compliance-policies/index.js index 54004aef6124..b3394023c492 100644 --- a/src/pages/endpoint/MEM/list-compliance-policies/index.js +++ b/src/pages/endpoint/MEM/list-compliance-policies/index.js @@ -1,313 +1,22 @@ 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 "../../../../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" }, - { label: "Append to existing assignments", value: "append" }, -]; - -const assignmentFilterTypeOptions = [ - { label: "Include - Apply policy to devices matching filter", value: "include" }, - { label: "Exclude - Apply policy to devices NOT matching filter", value: "exclude" }, -]; +import { useCippIntunePolicyActions } from "../../../../components/CippComponents/CippIntunePolicyActions.jsx"; const Page = () => { const pageTitle = "Intune Compliance Policies"; const cardButtonPermissions = ["Endpoint.MEM.ReadWrite"]; const tenant = useSettings().currentTenant; - const actions = [ - { - label: "Create template based on policy", - type: "POST", - url: "/api/AddIntuneTemplate", - data: { - ID: "id", - ODataType: "@odata.type", - }, - confirmText: "Are you sure you want to create a template based on this policy?", - icon: , - color: "info", - }, - { - label: "Assign to All Users", - type: "POST", - url: "/api/ExecAssignPolicy", - data: { - AssignTo: "allLicensedUsers", - ID: "id", - type: "deviceCompliancePolicies", - }, - fields: [ - { - type: "radio", - name: "assignmentMode", - label: "Assignment mode", - options: assignmentModeOptions, - defaultValue: "replace", - helperText: - "Replace will overwrite existing assignments. Append keeps current assignments and adds/overwrites only for the selected groups.", - }, - { - type: "autoComplete", - name: "assignmentFilter", - label: "Assignment Filter (Optional)", - multiple: false, - creatable: false, - api: { - url: "/api/ListAssignmentFilters", - queryKey: `ListAssignmentFilters-${tenant}`, - labelField: (filter) => filter.displayName, - valueField: "displayName", - }, - }, - { - type: "radio", - name: "assignmentFilterType", - label: "Assignment Filter Mode", - options: assignmentFilterTypeOptions, - defaultValue: "include", - helperText: "Choose whether to include or exclude devices matching the filter.", - }, - ], - customDataformatter: (row, action, formData) => { - const tenantFilterValue = tenant === "AllTenants" && row?.Tenant ? row.Tenant : tenant; - return { - tenantFilter: tenantFilterValue, - ID: row?.id, - type: "deviceCompliancePolicies", - AssignTo: "allLicensedUsers", - assignmentMode: formData?.assignmentMode || "replace", - AssignmentFilterName: formData?.assignmentFilter?.value || null, - AssignmentFilterType: formData?.assignmentFilter?.value - ? formData?.assignmentFilterType || "include" - : null, - }; - }, - confirmText: 'Are you sure you want to assign "[displayName]" to all users?', - icon: , - color: "info", - }, - { - label: "Assign to All Devices", - type: "POST", - url: "/api/ExecAssignPolicy", - data: { - AssignTo: "AllDevices", - ID: "id", - type: "deviceCompliancePolicies", - }, - fields: [ - { - type: "radio", - name: "assignmentMode", - label: "Assignment mode", - options: assignmentModeOptions, - defaultValue: "replace", - helperText: - "Replace will overwrite existing assignments. Append keeps current assignments and adds/overwrites only for the selected groups.", - }, - { - type: "autoComplete", - name: "assignmentFilter", - label: "Assignment Filter (Optional)", - multiple: false, - creatable: false, - api: { - url: "/api/ListAssignmentFilters", - queryKey: `ListAssignmentFilters-${tenant}`, - labelField: (filter) => filter.displayName, - valueField: "displayName", - }, - }, - { - type: "radio", - name: "assignmentFilterType", - label: "Assignment Filter Mode", - options: assignmentFilterTypeOptions, - defaultValue: "include", - helperText: "Choose whether to include or exclude devices matching the filter.", - }, - ], - customDataformatter: (row, action, formData) => { - const tenantFilterValue = tenant === "AllTenants" && row?.Tenant ? row.Tenant : tenant; - return { - tenantFilter: tenantFilterValue, - ID: row?.id, - type: "deviceCompliancePolicies", - AssignTo: "AllDevices", - assignmentMode: formData?.assignmentMode || "replace", - AssignmentFilterName: formData?.assignmentFilter?.value || null, - AssignmentFilterType: formData?.assignmentFilter?.value - ? formData?.assignmentFilterType || "include" - : null, - }; - }, - confirmText: 'Are you sure you want to assign "[displayName]" to all devices?', - icon: , - color: "info", + const actions = useCippIntunePolicyActions(tenant, "deviceCompliancePolicies", { + templateData: { + ID: "id", + ODataType: "@odata.type", }, - { - label: "Assign Globally (All Users / All Devices)", - type: "POST", - url: "/api/ExecAssignPolicy", - data: { - AssignTo: "AllDevicesAndUsers", - ID: "id", - type: "deviceCompliancePolicies", - }, - fields: [ - { - type: "radio", - name: "assignmentMode", - label: "Assignment mode", - options: assignmentModeOptions, - defaultValue: "replace", - helperText: - "Replace will overwrite existing assignments. Append keeps current assignments and adds/overwrites only for the selected groups.", - }, - { - type: "autoComplete", - name: "assignmentFilter", - label: "Assignment Filter (Optional)", - multiple: false, - creatable: false, - api: { - url: "/api/ListAssignmentFilters", - queryKey: `ListAssignmentFilters-${tenant}`, - labelField: (filter) => filter.displayName, - valueField: "displayName", - }, - }, - { - type: "radio", - name: "assignmentFilterType", - label: "Assignment Filter Mode", - options: assignmentFilterTypeOptions, - defaultValue: "include", - helperText: "Choose whether to include or exclude devices matching the filter.", - }, - ], - customDataformatter: (row, action, formData) => { - const tenantFilterValue = tenant === "AllTenants" && row?.Tenant ? row.Tenant : tenant; - return { - tenantFilter: tenantFilterValue, - ID: row?.id, - type: "deviceCompliancePolicies", - AssignTo: "AllDevicesAndUsers", - assignmentMode: formData?.assignmentMode || "replace", - AssignmentFilterName: formData?.assignmentFilter?.value || null, - AssignmentFilterType: formData?.assignmentFilter?.value - ? formData?.assignmentFilterType || "include" - : null, - }; - }, - confirmText: 'Are you sure you want to assign "[displayName]" to all users and devices?', - icon: , - color: "info", - }, - { - label: "Assign to Custom Group", - type: "POST", - url: "/api/ExecAssignPolicy", - icon: , - color: "info", - confirmText: 'Select the target groups for "[displayName]".', - fields: [ - { - type: "autoComplete", - name: "groupTargets", - label: "Group(s)", - multiple: true, - creatable: false, - allowResubmit: true, - validators: { required: "Please select at least one group" }, - api: { - url: "/api/ListGraphRequest", - dataKey: "Results", - queryKey: `ListPolicyAssignmentGroups-${tenant}`, - labelField: (group) => - group.id ? `${group.displayName} (${group.id})` : group.displayName, - valueField: "id", - addedField: { - description: "description", - }, - data: { - Endpoint: "groups", - manualPagination: true, - $select: "id,displayName,description", - $orderby: "displayName", - $top: 999, - $count: true, - }, - }, - }, - { - type: "radio", - name: "assignmentMode", - label: "Assignment mode", - options: assignmentModeOptions, - defaultValue: "replace", - helperText: - "Replace will overwrite existing assignments. Append keeps current assignments and adds/overwrites only for the selected groups.", - }, - { - type: "autoComplete", - name: "assignmentFilter", - label: "Assignment Filter (Optional)", - multiple: false, - creatable: false, - api: { - url: "/api/ListAssignmentFilters", - queryKey: `ListAssignmentFilters-${tenant}`, - labelField: (filter) => filter.displayName, - valueField: "displayName", - }, - }, - { - type: "radio", - name: "assignmentFilterType", - label: "Assignment Filter Mode", - options: assignmentFilterTypeOptions, - defaultValue: "include", - helperText: "Choose whether to include or exclude devices matching the filter.", - }, - ], - customDataformatter: (row, action, formData) => { - const selectedGroups = Array.isArray(formData?.groupTargets) ? formData.groupTargets : []; - const tenantFilterValue = tenant === "AllTenants" && row?.Tenant ? row.Tenant : tenant; - return { - tenantFilter: tenantFilterValue, - ID: row?.id, - type: "deviceCompliancePolicies", - GroupIds: selectedGroups.map((group) => group.value).filter(Boolean), - GroupNames: selectedGroups.map((group) => group.label).filter(Boolean), - assignmentMode: formData?.assignmentMode || "replace", - AssignmentFilterName: formData?.assignmentFilter?.value || null, - AssignmentFilterType: formData?.assignmentFilter?.value - ? formData?.assignmentFilterType || "include" - : null, - }; - }, - }, - { - label: "Delete Policy", - type: "POST", - url: "/api/RemovePolicy", - data: { - ID: "id", - URLName: "deviceCompliancePolicies", - }, - confirmText: "Are you sure you want to delete this policy?", - icon: , - color: "danger", - }, - ]; + deleteUrlName: "deviceCompliancePolicies", + }); const offCanvas = { extendedInfoFields: [ diff --git a/src/pages/endpoint/MEM/list-policies/index.js b/src/pages/endpoint/MEM/list-policies/index.js index 375d4b69d1cd..1b4d2d2c97bc 100644 --- a/src/pages/endpoint/MEM/list-policies/index.js +++ b/src/pages/endpoint/MEM/list-policies/index.js @@ -1,313 +1,22 @@ 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 "../../../../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" }, - { label: "Append to existing assignments", value: "append" }, -]; - -const assignmentFilterTypeOptions = [ - { label: "Include - Apply policy to devices matching filter", value: "include" }, - { label: "Exclude - Apply policy to devices NOT matching filter", value: "exclude" }, -]; +import { useCippIntunePolicyActions } from "../../../../components/CippComponents/CippIntunePolicyActions.jsx"; const Page = () => { const pageTitle = "Configuration Policies"; const cardButtonPermissions = ["Endpoint.MEM.ReadWrite"]; const tenant = useSettings().currentTenant; - const actions = [ - { - label: "Create template based on policy", - type: "POST", - url: "/api/AddIntuneTemplate", - data: { - ID: "id", - URLName: "URLName", - }, - confirmText: "Are you sure you want to create a template based on this policy?", - icon: , - color: "info", - }, - { - label: "Assign to All Users", - type: "POST", - url: "/api/ExecAssignPolicy", - data: { - AssignTo: "allLicensedUsers", - ID: "id", - type: "URLName", - }, - fields: [ - { - type: "radio", - name: "assignmentMode", - label: "Assignment mode", - options: assignmentModeOptions, - defaultValue: "replace", - helperText: - "Replace will overwrite existing assignments. Append keeps current assignments and adds/overwrites only for the selected groups.", - }, - { - type: "autoComplete", - name: "assignmentFilter", - label: "Assignment Filter (Optional)", - multiple: false, - creatable: false, - api: { - url: "/api/ListAssignmentFilters", - queryKey: `ListAssignmentFilters-${tenant}`, - labelField: (filter) => filter.displayName, - valueField: "displayName", - }, - }, - { - type: "radio", - name: "assignmentFilterType", - label: "Assignment Filter Mode", - options: assignmentFilterTypeOptions, - defaultValue: "include", - helperText: "Choose whether to include or exclude devices matching the filter.", - }, - ], - customDataformatter: (row, action, formData) => { - const tenantFilterValue = tenant === "AllTenants" && row?.Tenant ? row.Tenant : tenant; - return { - tenantFilter: tenantFilterValue, - ID: row?.id, - type: row?.URLName, - AssignTo: "allLicensedUsers", - assignmentMode: formData?.assignmentMode || "replace", - AssignmentFilterName: formData?.assignmentFilter?.value || null, - AssignmentFilterType: formData?.assignmentFilter?.value - ? formData?.assignmentFilterType || "include" - : null, - }; - }, - confirmText: 'Are you sure you want to assign "[displayName]" to all users?', - icon: , - color: "info", - }, - { - label: "Assign to All Devices", - type: "POST", - url: "/api/ExecAssignPolicy", - data: { - AssignTo: "AllDevices", - ID: "id", - type: "URLName", - }, - fields: [ - { - type: "radio", - name: "assignmentMode", - label: "Assignment mode", - options: assignmentModeOptions, - defaultValue: "replace", - helperText: - "Replace will overwrite existing assignments. Append keeps current assignments and adds/overwrites only for the selected groups.", - }, - { - type: "autoComplete", - name: "assignmentFilter", - label: "Assignment Filter (Optional)", - multiple: false, - creatable: false, - api: { - url: "/api/ListAssignmentFilters", - queryKey: `ListAssignmentFilters-${tenant}`, - labelField: (filter) => filter.displayName, - valueField: "displayName", - }, - }, - { - type: "radio", - name: "assignmentFilterType", - label: "Assignment Filter Mode", - options: assignmentFilterTypeOptions, - defaultValue: "include", - helperText: "Choose whether to include or exclude devices matching the filter.", - }, - ], - customDataformatter: (row, action, formData) => { - const tenantFilterValue = tenant === "AllTenants" && row?.Tenant ? row.Tenant : tenant; - return { - tenantFilter: tenantFilterValue, - ID: row?.id, - type: row?.URLName, - AssignTo: "AllDevices", - assignmentMode: formData?.assignmentMode || "replace", - AssignmentFilterName: formData?.assignmentFilter?.value || null, - AssignmentFilterType: formData?.assignmentFilter?.value - ? formData?.assignmentFilterType || "include" - : null, - }; - }, - confirmText: 'Are you sure you want to assign "[displayName]" to all devices?', - icon: , - color: "info", + const actions = useCippIntunePolicyActions(tenant, "URLName", { + templateData: { + ID: "id", + URLName: "URLName", }, - { - label: "Assign Globally (All Users / All Devices)", - type: "POST", - url: "/api/ExecAssignPolicy", - data: { - AssignTo: "AllDevicesAndUsers", - ID: "id", - type: "URLName", - }, - fields: [ - { - type: "radio", - name: "assignmentMode", - label: "Assignment mode", - options: assignmentModeOptions, - defaultValue: "replace", - helperText: - "Replace will overwrite existing assignments. Append keeps current assignments and adds/overwrites only for the selected groups.", - }, - { - type: "autoComplete", - name: "assignmentFilter", - label: "Assignment Filter (Optional)", - multiple: false, - creatable: false, - api: { - url: "/api/ListAssignmentFilters", - queryKey: `ListAssignmentFilters-${tenant}`, - labelField: (filter) => filter.displayName, - valueField: "displayName", - }, - }, - { - type: "radio", - name: "assignmentFilterType", - label: "Assignment Filter Mode", - options: assignmentFilterTypeOptions, - defaultValue: "include", - helperText: "Choose whether to include or exclude devices matching the filter.", - }, - ], - customDataformatter: (row, action, formData) => { - const tenantFilterValue = tenant === "AllTenants" && row?.Tenant ? row.Tenant : tenant; - return { - tenantFilter: tenantFilterValue, - ID: row?.id, - type: row?.URLName, - AssignTo: "AllDevicesAndUsers", - assignmentMode: formData?.assignmentMode || "replace", - AssignmentFilterName: formData?.assignmentFilter?.value || null, - AssignmentFilterType: formData?.assignmentFilter?.value - ? formData?.assignmentFilterType || "include" - : null, - }; - }, - confirmText: 'Are you sure you want to assign "[displayName]" to all users and devices?', - icon: , - color: "info", - }, - { - label: "Assign to Custom Group", - type: "POST", - url: "/api/ExecAssignPolicy", - icon: , - color: "info", - confirmText: 'Select the target groups for "[displayName]".', - fields: [ - { - type: "autoComplete", - name: "groupTargets", - label: "Group(s)", - multiple: true, - creatable: false, - allowResubmit: true, - validators: { required: "Please select at least one group" }, - api: { - url: "/api/ListGraphRequest", - dataKey: "Results", - queryKey: `ListPolicyAssignmentGroups-${tenant}`, - labelField: (group) => - group.id ? `${group.displayName} (${group.id})` : group.displayName, - valueField: "id", - addedField: { - description: "description", - }, - data: { - Endpoint: "groups", - manualPagination: true, - $select: "id,displayName,description", - $orderby: "displayName", - $top: 999, - $count: true, - }, - }, - }, - { - type: "radio", - name: "assignmentMode", - label: "Assignment mode", - options: assignmentModeOptions, - defaultValue: "replace", - helperText: - "Replace will overwrite existing assignments. Append keeps current assignments and adds/overwrites only for the selected groups.", - }, - { - type: "autoComplete", - name: "assignmentFilter", - label: "Assignment Filter (Optional)", - multiple: false, - creatable: false, - api: { - url: "/api/ListAssignmentFilters", - queryKey: `ListAssignmentFilters-${tenant}`, - labelField: (filter) => filter.displayName, - valueField: "displayName", - }, - }, - { - type: "radio", - name: "assignmentFilterType", - label: "Assignment Filter Mode", - options: assignmentFilterTypeOptions, - defaultValue: "include", - helperText: "Choose whether to include or exclude devices matching the filter.", - }, - ], - customDataformatter: (row, action, formData) => { - const selectedGroups = Array.isArray(formData?.groupTargets) ? formData.groupTargets : []; - const tenantFilterValue = tenant === "AllTenants" && row?.Tenant ? row.Tenant : tenant; - return { - tenantFilter: tenantFilterValue, - ID: row?.id, - type: row?.URLName, - GroupIds: selectedGroups.map((group) => group.value).filter(Boolean), - GroupNames: selectedGroups.map((group) => group.label).filter(Boolean), - assignmentMode: formData?.assignmentMode || "replace", - AssignmentFilterName: formData?.assignmentFilter?.value || null, - AssignmentFilterType: formData?.assignmentFilter?.value - ? formData?.assignmentFilterType || "include" - : null, - }; - }, - }, - { - label: "Delete Policy", - type: "POST", - url: "/api/RemovePolicy", - data: { - ID: "id", - URLName: "URLName", - }, - confirmText: "Are you sure you want to delete this policy?", - icon: , - color: "danger", - }, - ]; + deleteUrlName: "URLName", + }); const offCanvas = { extendedInfoFields: [ diff --git a/src/pages/identity/administration/users/add.jsx b/src/pages/identity/administration/users/add.jsx index eabb890fa965..28606efc90ba 100644 --- a/src/pages/identity/administration/users/add.jsx +++ b/src/pages/identity/administration/users/add.jsx @@ -37,6 +37,12 @@ const Page = () => { } newFields.tenantFilter = userSettingsDefaults.currentTenant; + // Preserve the currently selected template when copying properties + const currentTemplate = formControl.getValues("userTemplate"); + if (currentTemplate) { + newFields.userTemplate = currentTemplate; + } + formControl.reset(newFields); } }, [formValues]); diff --git a/src/pages/tenant/manage/applied-standards.js b/src/pages/tenant/manage/applied-standards.js index c17dc52e67cb..61bce5fda961 100644 --- a/src/pages/tenant/manage/applied-standards.js +++ b/src/pages/tenant/manage/applied-standards.js @@ -1779,7 +1779,8 @@ const 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. + This data has not yet been collected. Collect the data by selecting + Refresh Data from the Actions dropdown on the top of the page. )} @@ -2187,7 +2188,7 @@ const Page = () => { textTransform: "uppercase", letterSpacing: 0.5, display: "block", - mb: 1, + mb: 2, }} > Current Configuration @@ -2481,13 +2482,7 @@ const Page = () => { standard.currentTenantValue.CurrentValue !== null ? ( { })} ) : ( - + { textTransform: "uppercase", letterSpacing: 0.5, display: "block", - mb: 1, + mb: 2, }} > Current Configuration diff --git a/src/pages/tenant/manage/drift.js b/src/pages/tenant/manage/drift.js index 85ddc2871f88..e2ad9865383b 100644 --- a/src/pages/tenant/manage/drift.js +++ b/src/pages/tenant/manage/drift.js @@ -1316,6 +1316,11 @@ const ManageDriftPage = () => { } }, [templateId]); + // Effect to clear selected items when tenant changes + useEffect(() => { + setSelectedItems([]); + }, [tenantFilter]); + // Add action buttons to each deviation item const deviationItemsWithActions = actualDeviationItems.map((item) => { return { @@ -1959,6 +1964,10 @@ const ManageDriftPage = () => { ? "for this tenant" : "this deviation" }?`, + onSuccess: () => { + // Clear selected items after successful action + setSelectedItems([]); + }, }} row={actionData.data} relatedQueryKeys={[`TenantDrift-${tenantFilter}`]} diff --git a/src/pages/tenant/manage/edit.js b/src/pages/tenant/manage/edit.js index fdc18281a1f8..b96994fe7069 100644 --- a/src/pages/tenant/manage/edit.js +++ b/src/pages/tenant/manage/edit.js @@ -136,6 +136,15 @@ const Page = () => { }; offboardingFormControl.reset({ offboardingDefaults: defaultOffboardingValues }); + + updateOffboardingDefaults.mutate({ + url: "/api/EditTenantOffboardingDefaults", + data: { + customerId: tenantDetails.data?.id || currentTenant, + defaultDomainName: tenantDetails.data?.defaultDomainName || currentTenant, + offboardingDefaults: null, + }, + }); }; const title = "Manage Tenant"; @@ -275,7 +284,8 @@ const Page = () => { onClick={offboardingFormControl.handleSubmit((values) => { const offboardingSettings = values.offboardingDefaults || values; const formattedValues = { - customerId: currentTenant, + customerId: tenantDetails.data?.id || currentTenant, + defaultDomainName: tenantDetails.data?.defaultDomainName || currentTenant, offboardingDefaults: offboardingSettings, }; updateOffboardingDefaults.mutate({ @@ -309,6 +319,7 @@ const Page = () => {