Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions src/components/CippFormPages/CippReusableSettingForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useEffect } from "react";
import { Grid } from "@mui/system";
import CippFormComponent from "/src/components/CippComponents/CippFormComponent";
import { useWatch } from "react-hook-form";
import { getCippValidator } from "/src/utils/get-cipp-validator";

const CippReusableSettingForm = (props) => {
const { formControl, isEdit = false } = props;

const selectedDefinition = useWatch({ control: formControl.control, name: "settingDefinition" });

useEffect(() => {
if (selectedDefinition && selectedDefinition.value) {
formControl.setValue("settingDefinitionId", selectedDefinition.value);
}
}, [selectedDefinition, formControl]);

return (
<Grid container spacing={2}>
<CippFormComponent type="hidden" name="settingDefinitionId" formControl={formControl} />
<CippFormComponent type="hidden" name="settingId" formControl={formControl} />

<Grid size={{ md: 6, xs: 12 }}>
<CippFormComponent
type="textField"
label="Display Name"
name="displayName"
required
formControl={formControl}
fullWidth
/>
</Grid>
<Grid size={{ md: 6, xs: 12 }}>
<CippFormComponent
type="textField"
label="Description"
name="description"
formControl={formControl}
fullWidth
/>
</Grid>

<Grid size={{ xs: 12 }}>
<CippFormComponent
type="autoComplete"
name="settingDefinition"
label="Setting Definition"
required
creatable={false}
formControl={formControl}
disabled={isEdit}
helperText={
isEdit
? "Setting definition cannot be changed after creation"
: "Choose the reusable setting definition this instance is based on"
}
api={{
url: "/api/ListReusableSettingDefinitions",
queryKey: "ListReusableSettingDefinitions",
labelField: "displayName",
valueField: "id",
}}
/>
</Grid>

<Grid size={{ xs: 12 }}>
<CippFormComponent
type="textField"
name="settingInstanceJson"
label="Setting Instance JSON"
formControl={formControl}
required
multiline
rows={12}
validators={{
validate: (value) => getCippValidator(value, "json"),
}}
helperText="Paste the JSON payload returned by Graph for the setting instance."
/>
</Grid>
</Grid>
);
};

export default CippReusableSettingForm;
79 changes: 79 additions & 0 deletions src/components/CippFormPages/CippReusableSettingTemplateForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { useEffect } from "react";
import { Grid } from "@mui/system";
import CippFormComponent from "/src/components/CippComponents/CippFormComponent";
import { useWatch } from "react-hook-form";
import { getCippValidator } from "/src/utils/get-cipp-validator";

const CippReusableSettingTemplateForm = (props) => {
const { formControl } = props;

const selectedDefinition = useWatch({ control: formControl.control, name: "settingDefinition" });

useEffect(() => {
if (selectedDefinition && selectedDefinition.value) {
formControl.setValue("settingDefinitionId", selectedDefinition.value);
}
}, [selectedDefinition, formControl]);

return (
<Grid container spacing={2}>
<CippFormComponent type="hidden" name="GUID" formControl={formControl} />
<CippFormComponent type="hidden" name="settingDefinitionId" formControl={formControl} />

<Grid size={{ md: 6, xs: 12 }}>
<CippFormComponent
type="textField"
label="Template Name"
name="displayName"
required
formControl={formControl}
fullWidth
/>
</Grid>
<Grid size={{ md: 6, xs: 12 }}>
<CippFormComponent
type="textField"
label="Description"
name="description"
formControl={formControl}
fullWidth
/>
</Grid>

<Grid size={{ xs: 12 }}>
<CippFormComponent
type="autoComplete"
name="settingDefinition"
label="Setting Definition"
required
creatable={false}
formControl={formControl}
api={{
url: "/api/ListReusableSettingDefinitions",
queryKey: "ListReusableSettingDefinitions",
labelField: "displayName",
valueField: "id",
}}
/>
</Grid>

<Grid size={{ xs: 12 }}>
<CippFormComponent
type="textField"
name="settingInstanceJson"
label="Setting Instance JSON"
formControl={formControl}
required
multiline
rows={12}
validators={{
validate: (value) => getCippValidator(value, "json"),
}}
helperText="Provide the JSON payload that should be stored with this template."
/>
</Grid>
</Grid>
);
};

export default CippReusableSettingTemplateForm;
153 changes: 153 additions & 0 deletions src/components/CippWizard/CippWizardReusableSettingTemplates.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { useEffect } from "react";
import { Stack } from "@mui/material";
import { Grid } from "@mui/system";
import { useWatch } from "react-hook-form";
import CippFormComponent from "../CippComponents/CippFormComponent";
import CippWizardStepButtons from "./CippWizardStepButtons";
import { getCippValidator } from "/src/utils/get-cipp-validator";

export const CippWizardReusableSettingTemplates = (props) => {
const { postUrl, formControl, onPreviousStep, onNextStep, currentStep } = props;
const templateSelection = useWatch({ control: formControl.control, name: "TemplateList" });
const definitionSelection = useWatch({ control: formControl.control, name: "settingDefinition" });
const settingInstanceJson = useWatch({ control: formControl.control, name: "settingInstanceJson" });

useEffect(() => {
if (templateSelection?.addedFields) {
const addedFields = templateSelection.addedFields;
const templateDisplayName =
addedFields.displayName || addedFields.DisplayName || templateSelection.label;
const templateDescription = addedFields.description || addedFields.Description || "";
const templateDefinitionId =
addedFields.settingDefinitionId || addedFields.SettingDefinitionId || "";
const templateInstance = addedFields.settingInstance || addedFields.SettingInstance;

formControl.setValue("displayName", templateDisplayName || "");
formControl.setValue("description", templateDescription || "");
if (templateDefinitionId) {
formControl.setValue("settingDefinition", {
label: templateDefinitionId,
value: templateDefinitionId,
});
formControl.setValue("settingDefinitionId", templateDefinitionId);
}
if (templateInstance) {
const rawJson = typeof templateInstance === "string" ? templateInstance : JSON.stringify(templateInstance, null, 2);
formControl.setValue("settingInstanceJson", rawJson);
}
}
}, [templateSelection, formControl]);

useEffect(() => {
if (definitionSelection?.value) {
formControl.setValue("settingDefinitionId", definitionSelection.value);
}
}, [definitionSelection, formControl]);

useEffect(() => {
if (!settingInstanceJson) {
formControl.setValue("settingInstance", undefined);
return;
}

try {
const parsed = JSON.parse(settingInstanceJson);
formControl.setValue("settingInstance", parsed);
} catch (error) {
// Keep previous parsed value, validation will surface through the JSON field validator
}
}, [settingInstanceJson, formControl]);

return (
<Stack spacing={3}>
<CippFormComponent type="hidden" name="settingDefinitionId" formControl={formControl} />
<CippFormComponent type="hidden" name="settingInstance" formControl={formControl} />
<Grid container spacing={3}>
<Grid size={12}>
<CippFormComponent
type="autoComplete"
name="TemplateList"
label="Choose a Template"
formControl={formControl}
creatable={false}
multiple={false}
api={{
excludeTenantFilter: true,
url: "/api/ListReusableSettingTemplates",
queryKey: "ListReusableSettingTemplates",
labelField: (option) => option.DisplayName || option.displayName || option.GUID,
valueField: "GUID",
addedField: {
displayName: "DisplayName",
description: "Description",
settingDefinitionId: "SettingDefinitionId",
settingInstance: "SettingInstance",
},
showRefresh: true,
}}
/>
</Grid>

<Grid size={12}>
<CippFormComponent
type="autoComplete"
name="settingDefinition"
label="Setting Definition"
formControl={formControl}
creatable={false}
required
api={{
url: "/api/ListReusableSettingDefinitions",
queryKey: "ListReusableSettingDefinitions",
labelField: "displayName",
valueField: "id",
}}
/>
</Grid>

<Grid size={12}>
<CippFormComponent
type="textField"
name="displayName"
label="Reusable Setting Name"
formControl={formControl}
validators={{ required: "A display name is required" }}
/>
</Grid>

<Grid size={12}>
<CippFormComponent
type="textField"
name="description"
label="Description"
formControl={formControl}
multiline
rows={2}
/>
</Grid>

<Grid size={12}>
<CippFormComponent
type="textField"
name="settingInstanceJson"
label="Setting Instance JSON"
formControl={formControl}
multiline
rows={12}
validators={{
validate: (value) => getCippValidator(value, "json"),
}}
/>
</Grid>
</Grid>

<CippWizardStepButtons
postUrl={postUrl}
currentStep={currentStep}
onPreviousStep={onPreviousStep}
onNextStep={onNextStep}
formControl={formControl}
/>
</Stack>
);
};
10 changes: 10 additions & 0 deletions src/layouts/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,16 @@ export const nativeMenuItems = [
path: "/endpoint/MEM/assignment-filter-templates",
permissions: ["Endpoint.MEM.*"],
},
{
title: "Reusable Settings",
path: "/endpoint/MEM/list-reusable-settings",
permissions: ["Endpoint.MEM.*"],
},
{
title: "Reusable Setting Templates",
path: "/endpoint/MEM/reusable-setting-templates",
permissions: ["Endpoint.MEM.*"],
},
{
title: "Scripts",
path: "/endpoint/MEM/list-scripts",
Expand Down
Loading