From 525f33e62a609ecae01d4a9c549a91356d13de5c Mon Sep 17 00:00:00 2001
From: Logan Cook <2997336+MWGMorningwood@users.noreply.github.com>
Date: Mon, 20 Oct 2025 18:17:12 -0400
Subject: [PATCH] feat: add reusable settings and templates management pages
---
.../CippFormPages/CippReusableSettingForm.jsx | 85 ++++++++++
.../CippReusableSettingTemplateForm.jsx | 79 +++++++++
.../CippWizardReusableSettingTemplates.jsx | 153 ++++++++++++++++++
src/layouts/config.js | 10 ++
.../MEM/list-reusable-settings/add.jsx | 75 +++++++++
.../MEM/list-reusable-settings/edit.jsx | 112 +++++++++++++
.../MEM/list-reusable-settings/index.js | 82 ++++++++++
.../MEM/reusable-setting-templates/add.jsx | 54 +++++++
.../MEM/reusable-setting-templates/deploy.js | 43 +++++
.../MEM/reusable-setting-templates/edit.jsx | 96 +++++++++++
.../MEM/reusable-setting-templates/index.js | 143 ++++++++++++++++
11 files changed, 932 insertions(+)
create mode 100644 src/components/CippFormPages/CippReusableSettingForm.jsx
create mode 100644 src/components/CippFormPages/CippReusableSettingTemplateForm.jsx
create mode 100644 src/components/CippWizard/CippWizardReusableSettingTemplates.jsx
create mode 100644 src/pages/endpoint/MEM/list-reusable-settings/add.jsx
create mode 100644 src/pages/endpoint/MEM/list-reusable-settings/edit.jsx
create mode 100644 src/pages/endpoint/MEM/list-reusable-settings/index.js
create mode 100644 src/pages/endpoint/MEM/reusable-setting-templates/add.jsx
create mode 100644 src/pages/endpoint/MEM/reusable-setting-templates/deploy.js
create mode 100644 src/pages/endpoint/MEM/reusable-setting-templates/edit.jsx
create mode 100644 src/pages/endpoint/MEM/reusable-setting-templates/index.js
diff --git a/src/components/CippFormPages/CippReusableSettingForm.jsx b/src/components/CippFormPages/CippReusableSettingForm.jsx
new file mode 100644
index 000000000000..a8d5e371be64
--- /dev/null
+++ b/src/components/CippFormPages/CippReusableSettingForm.jsx
@@ -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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getCippValidator(value, "json"),
+ }}
+ helperText="Paste the JSON payload returned by Graph for the setting instance."
+ />
+
+
+ );
+};
+
+export default CippReusableSettingForm;
diff --git a/src/components/CippFormPages/CippReusableSettingTemplateForm.jsx b/src/components/CippFormPages/CippReusableSettingTemplateForm.jsx
new file mode 100644
index 000000000000..9afce6dad5d3
--- /dev/null
+++ b/src/components/CippFormPages/CippReusableSettingTemplateForm.jsx
@@ -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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getCippValidator(value, "json"),
+ }}
+ helperText="Provide the JSON payload that should be stored with this template."
+ />
+
+
+ );
+};
+
+export default CippReusableSettingTemplateForm;
diff --git a/src/components/CippWizard/CippWizardReusableSettingTemplates.jsx b/src/components/CippWizard/CippWizardReusableSettingTemplates.jsx
new file mode 100644
index 000000000000..b5744d2ed8c0
--- /dev/null
+++ b/src/components/CippWizard/CippWizardReusableSettingTemplates.jsx
@@ -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 (
+
+
+
+
+
+ option.DisplayName || option.displayName || option.GUID,
+ valueField: "GUID",
+ addedField: {
+ displayName: "DisplayName",
+ description: "Description",
+ settingDefinitionId: "SettingDefinitionId",
+ settingInstance: "SettingInstance",
+ },
+ showRefresh: true,
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getCippValidator(value, "json"),
+ }}
+ />
+
+
+
+
+
+ );
+};
diff --git a/src/layouts/config.js b/src/layouts/config.js
index 115de1795d46..6c7bcd6f4691 100644
--- a/src/layouts/config.js
+++ b/src/layouts/config.js
@@ -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",
diff --git a/src/pages/endpoint/MEM/list-reusable-settings/add.jsx b/src/pages/endpoint/MEM/list-reusable-settings/add.jsx
new file mode 100644
index 000000000000..f037eb3e7546
--- /dev/null
+++ b/src/pages/endpoint/MEM/list-reusable-settings/add.jsx
@@ -0,0 +1,75 @@
+import { useEffect } from "react";
+import { useForm } from "react-hook-form";
+import { Box } from "@mui/material";
+import CippFormPage from "../../../../components/CippFormPages/CippFormPage";
+import { Layout as DashboardLayout } from "/src/layouts/index.js";
+import { useSettings } from "../../../../hooks/use-settings";
+import CippReusableSettingForm from "../../../../components/CippFormPages/CippReusableSettingForm";
+
+const Page = () => {
+ const userSettingsDefaults = useSettings();
+
+ const formControl = useForm({
+ mode: "onChange",
+ defaultValues: {
+ tenantFilter: userSettingsDefaults.currentTenant,
+ settingInstanceJson: "{\n \"@odata.type\": \"#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance\"\n}",
+ },
+ });
+
+ useEffect(() => {
+ formControl.setValue("tenantFilter", userSettingsDefaults?.currentTenant || "");
+ }, [userSettingsDefaults, formControl]);
+
+ const formatData = (values) => {
+ const {
+ settingDefinition,
+ settingInstanceJson,
+ settingDefinitionId,
+ settingId,
+ ...rest
+ } = values;
+
+ const payload = { ...rest };
+
+ const definitionId = settingDefinitionId || settingDefinition?.value;
+ if (definitionId) {
+ payload.settingDefinitionId = definitionId;
+ }
+
+ if (settingId) {
+ payload.id = settingId;
+ }
+
+ if (settingInstanceJson) {
+ try {
+ payload.settingInstance = JSON.parse(settingInstanceJson);
+ } catch (error) {
+ throw new Error("Setting instance JSON is invalid");
+ }
+ }
+
+ return payload;
+ };
+
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+};
+
+Page.getLayout = (page) => {page};
+
+export default Page;
diff --git a/src/pages/endpoint/MEM/list-reusable-settings/edit.jsx b/src/pages/endpoint/MEM/list-reusable-settings/edit.jsx
new file mode 100644
index 000000000000..cc728d8e1a29
--- /dev/null
+++ b/src/pages/endpoint/MEM/list-reusable-settings/edit.jsx
@@ -0,0 +1,112 @@
+import { useEffect, useState } from "react";
+import { useRouter } from "next/router";
+import { useForm } from "react-hook-form";
+import { Box } from "@mui/material";
+import { Layout as DashboardLayout } from "/src/layouts/index.js";
+import CippFormPage from "../../../../components/CippFormPages/CippFormPage";
+import { ApiGetCall } from "../../../../api/ApiCall";
+import { useSettings } from "../../../../hooks/use-settings";
+import CippReusableSettingForm from "../../../../components/CippFormPages/CippReusableSettingForm";
+
+const Page = () => {
+ const router = useRouter();
+ const { settingId } = router.query;
+ const [ready, setReady] = useState(false);
+ const { currentTenant } = useSettings();
+
+ const getInfo = ApiGetCall({
+ url: `/api/ListReusableSettings?settingId=${settingId}&tenantFilter=${currentTenant}`,
+ queryKey: `ListReusableSettings-${settingId}`,
+ waiting: ready,
+ });
+
+ useEffect(() => {
+ if (settingId) {
+ setReady(true);
+ getInfo.refetch();
+ }
+ }, [settingId]);
+
+ const formControl = useForm({
+ mode: "onChange",
+ defaultValues: {
+ tenantFilter: currentTenant,
+ },
+ });
+
+ useEffect(() => {
+ if (getInfo.isSuccess && getInfo.data) {
+ const record = Array.isArray(getInfo.data) ? getInfo.data[0] : getInfo.data;
+ if (record) {
+ const instanceJson = record.settingInstance
+ ? JSON.stringify(record.settingInstance, null, 2)
+ : "";
+ formControl.reset({
+ tenantFilter: currentTenant,
+ settingId: record.id,
+ displayName: record.displayName || "",
+ description: record.description || "",
+ settingDefinitionId: record.settingDefinitionId,
+ settingDefinition: record.settingDefinitionId
+ ? { label: record.settingDefinitionId, value: record.settingDefinitionId }
+ : null,
+ settingInstanceJson: instanceJson,
+ });
+ }
+ }
+ }, [getInfo.isSuccess, getInfo.data, currentTenant]);
+
+ const formatData = (values) => {
+ const {
+ settingDefinition,
+ settingInstanceJson,
+ settingDefinitionId,
+ settingId,
+ ...rest
+ } = values;
+
+ const payload = { ...rest };
+
+ const definitionId = settingDefinitionId || settingDefinition?.value;
+ if (definitionId) {
+ payload.settingDefinitionId = definitionId;
+ }
+
+ if (settingId) {
+ payload.settingId = settingId;
+ payload.id = settingId;
+ }
+
+ if (settingInstanceJson) {
+ try {
+ payload.settingInstance = JSON.parse(settingInstanceJson);
+ } catch (error) {
+ throw new Error("Setting instance JSON is invalid");
+ }
+ }
+
+ return payload;
+ };
+
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+};
+
+Page.getLayout = (page) => {page};
+
+export default Page;
diff --git a/src/pages/endpoint/MEM/list-reusable-settings/index.js b/src/pages/endpoint/MEM/list-reusable-settings/index.js
new file mode 100644
index 000000000000..6e0e1cfe3dcd
--- /dev/null
+++ b/src/pages/endpoint/MEM/list-reusable-settings/index.js
@@ -0,0 +1,82 @@
+import { Button } from "@mui/material";
+import { Stack } from "@mui/system";
+import { Add, DeleteOutline, Edit, SaveAlt } from "@mui/icons-material";
+import Link from "next/link";
+import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx";
+import { Layout as DashboardLayout } from "/src/layouts/index.js";
+import { useSettings } from "../../../../hooks/use-settings";
+import CippJsonView from "../../../../components/CippFormPages/CippJSONView.jsx";
+
+const Page = () => {
+ const { currentTenant } = useSettings();
+ const pageTitle = "Reusable Settings";
+
+ const actions = [
+ {
+ label: "Edit Reusable Setting",
+ link: "/endpoint/MEM/list-reusable-settings/edit?settingId=[id]",
+ multiPost: false,
+ icon: ,
+ color: "success",
+ },
+ {
+ label: "Create template from setting",
+ type: "POST",
+ url: "/api/AddReusableSettingTemplate",
+ icon: ,
+ data: {
+ displayName: "displayName",
+ description: "description",
+ settingDefinitionId: "settingDefinitionId",
+ settingInstance: "settingInstance",
+ },
+ confirmText: "Create a reusable setting template based on this setting?",
+ multiPost: false,
+ },
+ {
+ label: "Delete Reusable Setting",
+ type: "POST",
+ url: "/api/ExecReusableSetting",
+ icon: ,
+ data: {
+ Action: "Delete",
+ ID: "id",
+ },
+ confirmText: "Are you sure you want to delete this reusable setting?",
+ multiPost: false,
+ },
+ ];
+
+ const offCanvas = {
+ children: (data) => (
+
+ ),
+ actions,
+ };
+
+ return (
+
+ }>
+ Add Reusable Setting
+
+
+ }
+ apiUrl="/api/ListReusableSettings"
+ queryKey={`reusable-settings-${currentTenant}`}
+ actions={actions}
+ offCanvas={offCanvas}
+ simpleColumns={["displayName", "description", "settingDefinitionId"]}
+ />
+ );
+};
+
+Page.getLayout = (page) => {page};
+
+export default Page;
diff --git a/src/pages/endpoint/MEM/reusable-setting-templates/add.jsx b/src/pages/endpoint/MEM/reusable-setting-templates/add.jsx
new file mode 100644
index 000000000000..7e6a3062d415
--- /dev/null
+++ b/src/pages/endpoint/MEM/reusable-setting-templates/add.jsx
@@ -0,0 +1,54 @@
+import { Box } from "@mui/material";
+import { useForm } from "react-hook-form";
+import { Layout as DashboardLayout } from "/src/layouts/index.js";
+import CippFormPage from "../../../../components/CippFormPages/CippFormPage";
+import CippReusableSettingTemplateForm from "../../../../components/CippFormPages/CippReusableSettingTemplateForm";
+
+const Page = () => {
+ const formControl = useForm({
+ mode: "onChange",
+ defaultValues: {},
+ });
+
+ const formatData = (values) => {
+ const { settingDefinition, settingInstanceJson, settingDefinitionId, ...rest } = values;
+ const payload = { ...rest };
+
+ const definitionId = settingDefinitionId || settingDefinition?.value;
+ if (definitionId) {
+ payload.settingDefinitionId = definitionId;
+ }
+
+ if (settingInstanceJson) {
+ try {
+ payload.settingInstance = JSON.parse(settingInstanceJson);
+ } catch (error) {
+ throw new Error("Setting instance JSON is invalid");
+ }
+ }
+
+ return payload;
+ };
+
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+};
+
+Page.getLayout = (page) => {page};
+
+export default Page;
diff --git a/src/pages/endpoint/MEM/reusable-setting-templates/deploy.js b/src/pages/endpoint/MEM/reusable-setting-templates/deploy.js
new file mode 100644
index 000000000000..41f20608eade
--- /dev/null
+++ b/src/pages/endpoint/MEM/reusable-setting-templates/deploy.js
@@ -0,0 +1,43 @@
+import { Layout as DashboardLayout } from "/src/layouts/index.js";
+import CippWizardPage from "/src/components/CippWizard/CippWizardPage.jsx";
+import { CippTenantStep } from "/src/components/CippWizard/CippTenantStep.jsx";
+import { CippWizardConfirmation } from "/src/components/CippWizard/CippWizardConfirmation";
+import { CippWizardReusableSettingTemplates } from "../../../../components/CippWizard/CippWizardReusableSettingTemplates";
+
+const Page = () => {
+ const steps = [
+ {
+ title: "Step 1",
+ description: "Tenant Selection",
+ component: CippTenantStep,
+ componentProps: {
+ allTenants: false,
+ type: "multiple",
+ },
+ },
+ {
+ title: "Step 2",
+ description: "Choose Template",
+ component: CippWizardReusableSettingTemplates,
+ },
+ {
+ title: "Step 3",
+ description: "Confirmation",
+ component: CippWizardConfirmation,
+ },
+ ];
+
+ return (
+ <>
+
+ >
+ );
+};
+
+Page.getLayout = (page) => {page};
+
+export default Page;
diff --git a/src/pages/endpoint/MEM/reusable-setting-templates/edit.jsx b/src/pages/endpoint/MEM/reusable-setting-templates/edit.jsx
new file mode 100644
index 000000000000..a035a36ccaff
--- /dev/null
+++ b/src/pages/endpoint/MEM/reusable-setting-templates/edit.jsx
@@ -0,0 +1,96 @@
+import { useEffect } from "react";
+import { useRouter } from "next/router";
+import { useForm } from "react-hook-form";
+import { Box } from "@mui/material";
+import { Layout as DashboardLayout } from "/src/layouts/index.js";
+import CippFormPage from "../../../../components/CippFormPages/CippFormPage";
+import CippReusableSettingTemplateForm from "../../../../components/CippFormPages/CippReusableSettingTemplateForm";
+import { ApiGetCall } from "../../../../api/ApiCall";
+
+const Page = () => {
+ const router = useRouter();
+ const { id } = router.query;
+ const formControl = useForm({
+ mode: "onChange",
+ defaultValues: {},
+ });
+
+ const templateInfo = ApiGetCall({
+ url: `/api/ListReusableSettingTemplates?id=${id}`,
+ queryKey: `ListReusableSettingTemplates-${id}`,
+ waiting: Boolean(id),
+ });
+
+ useEffect(() => {
+ if (templateInfo.isSuccess && templateInfo.data) {
+ const record = Array.isArray(templateInfo.data) ? templateInfo.data[0] : templateInfo.data;
+ if (record) {
+ const instanceJson = record.SettingInstance
+ ? JSON.stringify(record.SettingInstance, null, 2)
+ : record.settingInstance
+ ? JSON.stringify(record.settingInstance, null, 2)
+ : "";
+
+ const definitionId = record.SettingDefinitionId || record.settingDefinitionId;
+
+ formControl.reset({
+ GUID: record.GUID || record.guid || id,
+ displayName: record.DisplayName || record.displayName || "",
+ description: record.Description || record.description || "",
+ settingDefinitionId: definitionId,
+ settingDefinition: definitionId
+ ? {
+ label: definitionId,
+ value: definitionId,
+ }
+ : null,
+ settingInstanceJson: instanceJson,
+ });
+ }
+ }
+ }, [templateInfo.isSuccess, templateInfo.data, id]);
+
+ const formatData = (values) => {
+ const { settingDefinition, settingInstanceJson, settingDefinitionId, ...rest } = values;
+ const payload = { ...rest };
+
+ const definitionId = settingDefinitionId || settingDefinition?.value;
+ if (definitionId) {
+ payload.settingDefinitionId = definitionId;
+ }
+
+ if (settingInstanceJson) {
+ try {
+ payload.settingInstance = JSON.parse(settingInstanceJson);
+ } catch (error) {
+ throw new Error("Setting instance JSON is invalid");
+ }
+ }
+
+ return payload;
+ };
+
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+};
+
+Page.getLayout = (page) => {page};
+
+export default Page;
diff --git a/src/pages/endpoint/MEM/reusable-setting-templates/index.js b/src/pages/endpoint/MEM/reusable-setting-templates/index.js
new file mode 100644
index 000000000000..9deebc5aadd4
--- /dev/null
+++ b/src/pages/endpoint/MEM/reusable-setting-templates/index.js
@@ -0,0 +1,143 @@
+import { Button } from "@mui/material";
+import Link from "next/link";
+import { AddBox, Delete, Edit, GitHub, RocketLaunch } from "@mui/icons-material";
+import { Layout as DashboardLayout } from "/src/layouts/index.js";
+import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx";
+import { ApiGetCall } from "/src/api/ApiCall";
+import { CippPropertyListCard } from "../../../../components/CippCards/CippPropertyListCard";
+import { getCippTranslation } from "../../../../utils/get-cipp-translation";
+import { getCippFormatting } from "../../../../utils/get-cipp-formatting";
+
+const Page = () => {
+ const pageTitle = "Reusable Setting Templates";
+ const integrations = ApiGetCall({
+ url: "/api/ListExtensionsConfig",
+ queryKey: "Integrations",
+ refetchOnMount: false,
+ refetchOnReconnect: false,
+ });
+
+ const actions = [
+ {
+ label: "Edit Template",
+ icon: ,
+ link: "/endpoint/MEM/reusable-setting-templates/edit?id=[GUID]",
+ },
+ {
+ label: "Save to GitHub",
+ type: "POST",
+ url: "/api/ExecCommunityRepo",
+ icon: ,
+ data: {
+ Action: "UploadTemplate",
+ GUID: "GUID",
+ },
+ fields: [
+ {
+ label: "Repository",
+ name: "FullName",
+ type: "select",
+ api: {
+ url: "/api/ListCommunityRepos",
+ data: {
+ WriteAccess: true,
+ },
+ queryKey: "CommunityRepos-Write",
+ dataKey: "Results",
+ valueField: "FullName",
+ labelField: "FullName",
+ },
+ multiple: false,
+ creatable: false,
+ required: true,
+ validators: {
+ required: { value: true, message: "Repository is required" },
+ },
+ },
+ {
+ label: "Commit Message",
+ placeholder: "Enter a commit message",
+ name: "Message",
+ type: "textField",
+ multiline: true,
+ required: true,
+ rows: 4,
+ },
+ ],
+ confirmText: "Save this reusable setting template to the selected repository?",
+ condition: () => integrations.isSuccess && integrations?.data?.GitHub?.Enabled,
+ },
+ {
+ label: "Delete Template",
+ type: "POST",
+ url: "/api/RemoveReusableSettingTemplate",
+ icon: ,
+ data: {
+ ID: "GUID",
+ },
+ confirmText: "Delete this reusable setting template?",
+ multiPost: false,
+ },
+ ];
+
+ const offCanvas = {
+ children: (data) => {
+ const keys = Object.keys(data).filter(
+ (key) => !key.includes("@odata") && !key.includes("@data")
+ );
+ const properties = [];
+ keys.forEach((key) => {
+ if (data[key] && data[key].length !== 0) {
+ properties.push({
+ label: getCippTranslation(key),
+ value: getCippFormatting(data[key], key),
+ });
+ }
+ });
+
+ return (
+
+ );
+ },
+ };
+
+ return (
+
+ }
+ >
+ Add Reusable Setting Template
+
+ }
+ >
+ Deploy Reusable Setting Template
+
+ >
+ }
+ offCanvas={offCanvas}
+ simpleColumns={["DisplayName", "Description", "SettingDefinitionId", "GUID"]}
+ />
+ );
+};
+
+Page.getLayout = (page) => {page};
+
+export default Page;