Skip to content

Commit 2ed8455

Browse files
changes to deploy policy for intune and make catalog clearer.
1 parent dc6235b commit 2ed8455

File tree

11 files changed

+726
-74
lines changed

11 files changed

+726
-74
lines changed

src/components/CippComponents/CippFormTenantSelector.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export const CippFormTenantSelector = ({
7575
name={name}
7676
formControl={formControl}
7777
preselectedValue={preselectedEnabled ?? currentTenant ? currentTenant : null}
78-
placeholder="Select a tenant"
78+
label="Select a tenant"
7979
creatable={false}
8080
multiple={type === "single" ? false : true}
8181
disableClearable={disableClearable}

src/components/CippComponents/CippOffCanvas.jsx

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Drawer, Box, IconButton } from "@mui/material";
1+
import { Drawer, Box, IconButton, Typography, Divider } from "@mui/material";
22
import { CippPropertyListCard } from "../CippCards/CippPropertyListCard";
33
import { getCippTranslation } from "../../utils/get-cipp-translation";
44
import { getCippFormatting } from "../../utils/get-cipp-formatting";
@@ -16,6 +16,7 @@ export const CippOffCanvas = (props) => {
1616
isFetching,
1717
children,
1818
size = "sm",
19+
footer,
1920
} = props;
2021

2122
const mdDown = useMediaQuery((theme) => theme.breakpoints.down("md"));
@@ -79,41 +80,65 @@ export const CippOffCanvas = (props) => {
7980
open={visible}
8081
onClose={onClose}
8182
>
82-
<IconButton
83-
onClick={onClose}
84-
sx={{
85-
position: "absolute",
86-
top: 16,
87-
right: 8,
88-
zIndex: 1,
89-
}}
83+
<Box
84+
sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", p: 1.5 }}
9085
>
91-
<CloseIcon />
92-
</IconButton>
93-
{/* Force vertical stacking in a column layout */}
86+
<Typography variant="h5">{title}</Typography>
87+
<IconButton onClick={onClose}>
88+
<CloseIcon />
89+
</IconButton>
90+
</Box>
91+
<Divider />
92+
{/* Main content area */}
9493
<Box
95-
sx={{ overflowY: "auto", maxHeight: "100%", display: "flex", flexDirection: "column" }}
94+
sx={{
95+
display: "flex",
96+
flexDirection: "column",
97+
height: footer ? "calc(100vh - 120px)" : "100%",
98+
}}
9699
>
97-
<Grid container spacing={1}>
98-
<Grid size={{ xs: 12 }}>
99-
{extendedInfo.length > 0 && (
100-
<CippPropertyListCard
101-
isFetching={isFetching}
102-
align="vertical"
103-
title={title}
104-
propertyItems={extendedInfo}
105-
copyItems={true}
106-
actionItems={actions}
107-
data={extendedData}
108-
/>
109-
)}
100+
<Box
101+
sx={{
102+
overflowY: "auto",
103+
flex: 1,
104+
display: "flex",
105+
flexDirection: "column",
106+
}}
107+
>
108+
<Grid container spacing={1}>
109+
<Grid size={{ xs: 12 }}>
110+
{extendedInfo.length > 0 && (
111+
<CippPropertyListCard
112+
isFetching={isFetching}
113+
align="vertical"
114+
propertyItems={extendedInfo}
115+
copyItems={true}
116+
actionItems={actions}
117+
data={extendedData}
118+
/>
119+
)}
120+
</Grid>
121+
<Grid size={{ xs: 12 }}>
122+
<Box sx={{ m: 2 }}>
123+
{/* Render children if provided, otherwise render default content */}
124+
{typeof children === "function" ? children(extendedData) : children}
125+
</Box>
126+
</Grid>
110127
</Grid>
111-
<Grid size={{ xs: 12 }}>
112-
<Box sx={{ m: 2 }}>
113-
{typeof children === "function" ? children(extendedData) : children}
114-
</Box>
115-
</Grid>
116-
</Grid>
128+
</Box>
129+
130+
{/* Footer section */}
131+
{footer && (
132+
<Box
133+
sx={{
134+
borderTop: 1,
135+
borderColor: "divider",
136+
p: 2,
137+
}}
138+
>
139+
{footer}
140+
</Box>
141+
)}
117142
</Box>
118143
</Drawer>
119144
</>
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import { useEffect, useState } from "react";
2+
import { Button, Stack, Box } from "@mui/material";
3+
import { RocketLaunch } from "@mui/icons-material";
4+
import { useForm, useWatch } from "react-hook-form";
5+
import { CippOffCanvas } from "./CippOffCanvas";
6+
import { CippIntunePolicy } from "../CippWizard/CippIntunePolicy";
7+
import { ApiGetCall, ApiPostCall } from "../../api/ApiCall";
8+
import CippFormComponent from "./CippFormComponent";
9+
import CippJsonView from "../CippFormPages/CippJSONView";
10+
import { Grid } from "@mui/system";
11+
import { CippFormCondition } from "./CippFormCondition";
12+
import { CippApiResults } from "./CippApiResults";
13+
import { useSettings } from "../../hooks/use-settings";
14+
import { CippFormTenantSelector } from "./CippFormTenantSelector";
15+
16+
export const CippPolicyDeployDrawer = ({
17+
buttonText = "Deploy Policy",
18+
requiredPermissions = [],
19+
PermissionButton = Button,
20+
}) => {
21+
const [drawerVisible, setDrawerVisible] = useState(false);
22+
const formControl = useForm();
23+
const tenantFilter = useSettings()?.tenantFilter;
24+
const selectedTenants = useWatch({ control: formControl.control, name: "tenantFilter" }) || [];
25+
const CATemplates = ApiGetCall({ url: "/api/ListIntuneTemplates", queryKey: "IntuneTemplates" });
26+
const [JSONData, setJSONData] = useState();
27+
const watcher = useWatch({ control: formControl.control, name: "TemplateList" });
28+
const jsonWatch = useWatch({ control: formControl.control, name: "RAWJson" });
29+
useEffect(() => {
30+
if (CATemplates.isSuccess && watcher?.value) {
31+
const template = CATemplates.data.find((template) => template.GUID === watcher.value);
32+
if (template) {
33+
const jsonTemplate = template.RAWJson ? JSON.parse(template.RAWJson) : null;
34+
setJSONData(jsonTemplate);
35+
formControl.setValue("RAWJson", template.RAWJson);
36+
formControl.setValue("displayName", template.Displayname);
37+
formControl.setValue("description", template.Description);
38+
formControl.setValue("TemplateType", template.Type);
39+
}
40+
}
41+
}, [watcher]);
42+
const deployPolicy = ApiPostCall({
43+
urlFromData: true,
44+
relatedQueryKeys: [
45+
"IntuneTemplates",
46+
`Configuration Policies - ${tenantFilter}`,
47+
`Compliance Policies - ${tenantFilter}`,
48+
`Protection Policies - ${tenantFilter}`,
49+
],
50+
});
51+
52+
const handleSubmit = () => {
53+
const formData = formControl.getValues();
54+
console.log("Submitting form data:", formData);
55+
deployPolicy.mutate({
56+
url: "/api/AddPolicy",
57+
relatedQueryKeys: [
58+
"IntuneTemplates",
59+
"Configuration Policies",
60+
"Compliance Policies",
61+
"Protection Policies",
62+
],
63+
data: { ...formData },
64+
});
65+
};
66+
67+
const handleCloseDrawer = () => {
68+
setDrawerVisible(false);
69+
formControl.reset();
70+
};
71+
72+
return (
73+
<>
74+
<PermissionButton
75+
requiredPermissions={requiredPermissions}
76+
onClick={() => setDrawerVisible(true)}
77+
startIcon={<RocketLaunch />}
78+
>
79+
{buttonText}
80+
</PermissionButton>
81+
<CippOffCanvas
82+
title="Deploy Policy"
83+
visible={drawerVisible}
84+
onClose={handleCloseDrawer}
85+
size="lg"
86+
footer={
87+
<Stack direction="row" justifyContent="flex-start" spacing={2}>
88+
<Button
89+
variant="contained"
90+
color="primary"
91+
onClick={handleSubmit}
92+
disabled={deployPolicy.isLoading}
93+
>
94+
{deployPolicy.isLoading
95+
? "Deploying..."
96+
: deployPolicy.isSuccess
97+
? "Redeploy Policy"
98+
: "Deploy Policy"}
99+
</Button>
100+
<Button variant="outlined" onClick={handleCloseDrawer}>
101+
Close
102+
</Button>
103+
</Stack>
104+
}
105+
>
106+
<Stack spacing={3}>
107+
<CippFormTenantSelector
108+
formControl={formControl}
109+
name="tenantFilter"
110+
required={true}
111+
disableClearable={false}
112+
allTenants={true}
113+
type="multiple"
114+
/>
115+
<CippFormComponent
116+
type="autoComplete"
117+
name="TemplateList"
118+
label="Please choose a template to apply."
119+
isFetching={CATemplates.isLoading}
120+
multiple={false}
121+
formControl={formControl}
122+
options={
123+
CATemplates.isSuccess
124+
? CATemplates.data.map((template) => ({
125+
label: template.Displayname,
126+
value: template.GUID,
127+
}))
128+
: []
129+
}
130+
/>
131+
132+
<CippFormComponent
133+
type="hidden"
134+
name="RAWJson"
135+
label="Conditional Access Parameters"
136+
placeholder="Enter the JSON information to use as parameters, or select from a template"
137+
formControl={formControl}
138+
/>
139+
<CippJsonView object={JSONData} type="intune" />
140+
141+
<Grid size={{ xs: 12 }}>
142+
<CippFormComponent
143+
type="radio"
144+
name="AssignTo"
145+
options={[
146+
{ label: "Do not assign", value: "On" },
147+
{ label: "Assign to all users", value: "allLicensedUsers" },
148+
{ label: "Assign to all devices", value: "AllDevices" },
149+
{ label: "Assign to all users and devices", value: "AllDevicesAndUsers" },
150+
{ label: "Assign to Custom Group", value: "customGroup" },
151+
]}
152+
formControl={formControl}
153+
/>
154+
</Grid>
155+
<CippFormCondition
156+
formControl={formControl}
157+
field="AssignTo"
158+
compareType="is"
159+
compareValue="customGroup"
160+
>
161+
<Grid size={{ xs: 12 }}>
162+
<CippFormComponent
163+
type="textField"
164+
label="Custom Group Names separated by comma. Wildcards (*) are allowed"
165+
name="customGroup"
166+
formControl={formControl}
167+
validators={{ required: "Please specify custom group names" }}
168+
/>
169+
</Grid>
170+
</CippFormCondition>
171+
<CippFormCondition
172+
formControl={formControl}
173+
field="RAWJson"
174+
compareType="regex"
175+
compareValue={/%(\w+)%/}
176+
>
177+
{(() => {
178+
const rawJson = jsonWatch ? jsonWatch : "";
179+
const placeholderMatches = [...rawJson.matchAll(/%(\w+)%/g)].map((m) => m[1]);
180+
const uniquePlaceholders = Array.from(new Set(placeholderMatches));
181+
if (uniquePlaceholders.length === 0 || selectedTenants.length === 0) {
182+
return null;
183+
}
184+
return uniquePlaceholders.map((placeholder) => (
185+
<Grid key={placeholder} size={{ xs: 12 }}>
186+
{selectedTenants.map((tenant, idx) => (
187+
<CippFormComponent
188+
key={`${tenant.value}-${placeholder}-${idx}`}
189+
type="textField"
190+
defaultValue={
191+
//if the placeholder is tenantid then replace it with tenant.addedFields.customerId, if the placeholder is tenantdomain then replace it with tenant.addedFields.defaultDomainName.
192+
placeholder === "tenantid"
193+
? tenant?.addedFields?.customerId
194+
: placeholder === "tenantdomain"
195+
? tenant?.addedFields?.defaultDomainName
196+
: ""
197+
}
198+
name={`replacemap.${tenant.value}.%${placeholder}%`}
199+
label={`Value for '${placeholder}' in Tenant '${tenant.addedFields.defaultDomainName}'`}
200+
formControl={formControl}
201+
validators={{ required: `Please provide a value for ${placeholder}` }}
202+
sx={{ m: 1 }}
203+
/>
204+
))}
205+
</Grid>
206+
));
207+
})()}
208+
</CippFormCondition>
209+
<CippApiResults apiObject={deployPolicy} />
210+
</Stack>
211+
</CippOffCanvas>
212+
</>
213+
);
214+
};

0 commit comments

Comments
 (0)