diff --git a/package.json b/package.json index 059e1aabea30..58019e25bb41 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "@tiptap/extension-table": "^3.4.1", "@tiptap/pm": "^3.4.1", "@tiptap/react": "^3.4.1", - "@tiptap/starter-kit": "^3.4.1", + "@tiptap/starter-kit": "^3.19.0", "@uiw/react-json-view": "^2.0.0-alpha.30", "@vvo/tzdb": "^6.198.0", "apexcharts": "5.3.5", @@ -62,7 +62,7 @@ "formik": "2.4.6", "gray-matter": "4.0.3", "i18next": "25.5.2", - "javascript-time-ago": "^2.5.11", + "javascript-time-ago": "^2.6.2", "jspdf": "^4.1.0", "jspdf-autotable": "^5.0.2", "leaflet": "^1.9.4", @@ -70,7 +70,7 @@ "leaflet.markercluster": "^1.5.3", "lodash.isequal": "4.5.0", "material-react-table": "^3.0.1", - "monaco-editor": "^0.53.0", + "monaco-editor": "^0.55.1", "mui-tiptap": "^1.14.0", "next": "^16.1.2", "nprogress": "0.2.0", diff --git a/src/components/CippComponents/CippReusableSettingsDeployDrawer.jsx b/src/components/CippComponents/CippReusableSettingsDeployDrawer.jsx new file mode 100644 index 000000000000..06365e32d50c --- /dev/null +++ b/src/components/CippComponents/CippReusableSettingsDeployDrawer.jsx @@ -0,0 +1,165 @@ +import { useEffect, useState } from "react"; +import { Button, Stack } from "@mui/material"; +import { RocketLaunch } from "@mui/icons-material"; +import { useForm, useWatch, useFormState } from "react-hook-form"; +import { CippOffCanvas } from "./CippOffCanvas"; +import { ApiGetCall, ApiPostCall } from "../../api/ApiCall"; +import CippFormComponent from "./CippFormComponent"; +import CippJsonView from "../CippFormPages/CippJSONView"; +import { Grid } from "@mui/system"; +import { CippApiResults } from "./CippApiResults"; +import { useSettings } from "../../hooks/use-settings"; +import { CippFormTenantSelector } from "./CippFormTenantSelector"; +import { PermissionButton as PermissionAwareButton } from "../../utils/permissions"; + +export const CippReusableSettingsDeployDrawer = ({ + buttonText = "Deploy Reusable Settings", + requiredPermissions = [], + PermissionButton = PermissionAwareButton, +}) => { + const [drawerVisible, setDrawerVisible] = useState(false); + const formControl = useForm({ mode: "onChange" }); + const { isValid } = useFormState({ control: formControl.control }); + const tenantFilter = useSettings()?.tenantFilter; + const selectedTemplate = useWatch({ control: formControl.control, name: "TemplateList" }); + const rawJson = useWatch({ control: formControl.control, name: "rawJSON" }); + const selectedTenants = useWatch({ control: formControl.control, name: "tenantFilter" }); + + const templates = ApiGetCall({ url: "/api/ListIntuneReusableSettingTemplates", queryKey: "ListIntuneReusableSettingTemplates" }); + + useEffect(() => { + if (templates.isSuccess && selectedTemplate?.value) { + const match = templates.data?.find((t) => t.GUID === selectedTemplate.value); + if (match) { + formControl.setValue("rawJSON", match.RawJSON || ""); + formControl.setValue("TemplateId", match.GUID); + } + } + }, [templates.isSuccess, templates.data, selectedTemplate, formControl]); + + const effectiveTenants = Array.isArray(selectedTenants) && selectedTenants.length > 0 + ? selectedTenants + : tenantFilter + ? [tenantFilter] + : []; + + const deploy = ApiPostCall({ + urlFromData: true, + relatedQueryKeys: [ + "ListIntuneReusableSettingTemplates", + `ListIntuneReusableSettings-${effectiveTenants.join(",")}`, + ], + }); + + const handleSubmit = async () => { + const isFormValid = await formControl.trigger(); + if (!isFormValid) { + return; + } + const values = formControl.getValues(); + deploy.mutate({ + url: "/api/AddIntuneReusableSetting", + data: { + tenantFilter: effectiveTenants, + TemplateId: values?.TemplateList?.value, + rawJSON: values?.rawJSON, + }, + }); + }; + + const handleCloseDrawer = () => { + setDrawerVisible(false); + formControl.reset(); + deploy.reset(); + }; + + const safeJson = () => { + if (!rawJson) return null; + try { + return JSON.parse(rawJson); + } catch (e) { + return null; + } + }; + + return ( + <> + setDrawerVisible(true)} + startIcon={} + > + {buttonText} + + + + + + } + > + + + ({ + label: + t.displayName || + t.DisplayName || + t.templateName || + t.TemplateName || + t.name || + `Template ${t.GUID}`, + value: t.GUID, + })) + : [] + } + validators={{ required: { value: true, message: "Template selection is required" } }} + /> + + + + + + + + + ); +}; diff --git a/src/components/CippComponents/CippTemplateFieldRenderer.jsx b/src/components/CippComponents/CippTemplateFieldRenderer.jsx index 5f385bfdda64..1757e400acda 100644 --- a/src/components/CippComponents/CippTemplateFieldRenderer.jsx +++ b/src/components/CippComponents/CippTemplateFieldRenderer.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useMemo } from "react"; import { Typography, Divider } from "@mui/material"; import { Grid } from "@mui/system"; import CippFormComponent from "./CippFormComponent"; @@ -10,6 +10,15 @@ const CippTemplateFieldRenderer = ({ formControl, templateType = "conditionalAccess", }) => { + const intuneDefinitionMap = useMemo(() => { + const map = new Map(); + (intuneCollection || []).forEach((def) => { + if (def?.id) { + map.set(def.id, def); + } + }); + return map; + }, []); // Default blacklisted fields with wildcard support const defaultBlacklistedFields = [ "id", @@ -253,6 +262,86 @@ const CippTemplateFieldRenderer = ({ return null; } + // Render Intune group setting collections with child-friendly fields instead of raw [object Object] + if ( + templateType === "intune" && + key.toLowerCase() === "groupsettingcollectionvalue" && + Array.isArray(value) + ) { + return ( + + + {getCippTranslation(key)} + + + + {value.map((groupEntry, groupIndex) => ( + + + {`Entry ${groupIndex + 1}`} + + + {(groupEntry?.children || []).map((child, childIndex) => { + const childPath = `${fieldPath}.${groupIndex}.children.${childIndex}`; + const intuneDefinition = intuneDefinitionMap.get(child?.settingDefinitionId); + const childLabel = + intuneDefinition?.displayName || child?.settingDefinitionId || `Child ${ + childIndex + 1 + }`; + + if (child?.simpleSettingValue) { + return ( + + + + ); + } + + if (child?.choiceSettingValue) { + const options = + intuneDefinition?.options?.map((option) => ({ + label: option.displayName || option.id, + value: option.id, + })) || []; + + return ( + + + + ); + } + + return ( + + + Unsupported group entry type — edit in JSON if needed. + + + ); + })} + + + ))} + + + ); + } + // Check for custom schema handling const schemaField = schemaFields[key.toLowerCase()]; if (schemaField) { @@ -299,9 +388,7 @@ const CippTemplateFieldRenderer = ({ // Handle different setting types if (settingInstance.choiceSettingValue) { // Find the setting definition in the intune collection - const intuneObj = intuneCollection.find( - (item) => item.id === settingInstance.settingDefinitionId - ); + const intuneObj = intuneDefinitionMap.get(settingInstance.settingDefinitionId); const label = intuneObj?.displayName || `Setting ${index + 1}`; const options = @@ -327,9 +414,7 @@ const CippTemplateFieldRenderer = ({ if (settingInstance.simpleSettingValue) { // Find the setting definition in the intune collection - const intuneObj = intuneCollection.find( - (item) => item.id === settingInstance.settingDefinitionId - ); + const intuneObj = intuneDefinitionMap.get(settingInstance.settingDefinitionId); const label = intuneObj?.displayName || `Setting ${index + 1}`; diff --git a/src/components/CippFormPages/CippAddIntuneReusableSettingTemplateForm.jsx b/src/components/CippFormPages/CippAddIntuneReusableSettingTemplateForm.jsx new file mode 100644 index 000000000000..6fdf41b42d40 --- /dev/null +++ b/src/components/CippFormPages/CippAddIntuneReusableSettingTemplateForm.jsx @@ -0,0 +1,54 @@ +import { Grid } from "@mui/system"; +import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; + +const CippAddIntuneReusableSettingTemplateForm = ({ formControl }) => { + return ( + + + + + + + + + + + + + + + + + + + ); +}; + +export default CippAddIntuneReusableSettingTemplateForm; diff --git a/src/data/standards.json b/src/data/standards.json index 9864918bdf39..4e89eadb5656 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -5344,6 +5344,46 @@ } ] }, + { + "name": "standards.ReusableSettingsTemplate", + "cat": "Templates", + "label": "Reusable Settings Template", + "multiple": true, + "disabledFeatures": { + "report": false, + "warn": false, + "remediate": false + }, + "impact": "High Impact", + "impactColour": "info", + "addedDate": "2026-01-02", + "helpText": "Deploy and maintain Intune reusable settings templates that can be referenced by multiple policies.", + "executiveText": "Creates and keeps reusable Intune settings templates consistent so common firewall and configuration blocks can be reused across many policies.", + "addedComponent": [ + { + "type": "autoComplete", + "multiple": true, + "creatable": false, + "required": true, + "name": "TemplateList", + "label": "Select Reusable Settings Template", + "api": { + "queryKey": "ListIntuneReusableSettingTemplates", + "url": "/api/ListIntuneReusableSettingTemplates", + "labelField": "displayName", + "valueField": "GUID", + "showRefresh": true, + "templateView": { + "title": "Reusable Settings", + "property": "RawJSON", + "type": "intune" + } + } + } + ], + "powershellEquivalent": "", + "recommendedBy": [] + }, { "name": "standards.TransportRuleTemplate", "label": "Transport Rule Template", diff --git a/src/layouts/config.js b/src/layouts/config.js index 07ca170abc5b..3bfc34eeb508 100644 --- a/src/layouts/config.js +++ b/src/layouts/config.js @@ -447,6 +447,16 @@ export const nativeMenuItems = [ path: "/endpoint/MEM/list-templates", permissions: ["Endpoint.MEM.*"], }, + { + title: "Reusable Settings", + path: "/endpoint/MEM/reusable-settings", + permissions: ["Endpoint.MEM.*"], + }, + { + title: "Reusable Settings Templates", + path: "/endpoint/MEM/reusable-settings-templates", + permissions: ["Endpoint.MEM.*"], + }, { title: "Assignment Filters", path: "/endpoint/MEM/assignment-filters", diff --git a/src/pages/cipp/settings/features.js b/src/pages/cipp/settings/features.js index 930636e3205c..15b6fd3a111e 100644 --- a/src/pages/cipp/settings/features.js +++ b/src/pages/cipp/settings/features.js @@ -49,7 +49,7 @@ const Page = () => { actions: actions, }; - const simpleColumns = ["Name", "Enabled"]; + const simpleColumns = ["Name", "Enabled", "Description"]; return ( { + const userSettingsDefaults = useSettings(); + + const formControl = useForm({ + mode: "onChange", + defaultValues: { + tenantFilter: userSettingsDefaults.currentTenant, + }, + }); + + return ( + + + + + + ); +}; + +Page.getLayout = (page) => {page}; + +export default Page; diff --git a/src/pages/endpoint/MEM/reusable-settings-templates/edit.jsx b/src/pages/endpoint/MEM/reusable-settings-templates/edit.jsx new file mode 100644 index 000000000000..d1ca5e5f444b --- /dev/null +++ b/src/pages/endpoint/MEM/reusable-settings-templates/edit.jsx @@ -0,0 +1,374 @@ +import { Alert, Box, Button, Stack, Table, TableBody, TableCell, TableHead, TableRow, Typography, Divider } from "@mui/material"; +import { useForm, useFieldArray } from "react-hook-form"; +import { useRouter } from "next/router"; +import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "../../../../components/CippFormPages/CippFormPage"; +import CippFormSkeleton from "../../../../components/CippFormPages/CippFormSkeleton"; +import CippFormComponent from "../../../../components/CippComponents/CippFormComponent"; +import { ApiGetCall } from "../../../../api/ApiCall"; +import { useSettings } from "../../../../hooks/use-settings"; +import { useEffect, useMemo } from "react"; + +// Structured clone helper for older runtimes +const deepClone = (obj) => JSON.parse(JSON.stringify(obj)); + +const generateGuid = () => { + const wrap = (val) => `{${val}}`; + if (typeof crypto !== "undefined" && crypto.randomUUID) return wrap(crypto.randomUUID()); + const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); + return wrap(`${s4()}${s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`); +}; + +const EditReusableSettingsTemplate = () => { + const router = useRouter(); + const { id: rawId } = router.query; + const { currentTenant } = useSettings(); + + const normalizedId = useMemo(() => { + if (typeof rawId === "string") return rawId; + if (Array.isArray(rawId) && rawId.length > 0) return rawId[0]; + return undefined; + }, [rawId]); + + const formControl = useForm({ + mode: "onChange", + defaultValues: { + tenantFilter: currentTenant, + GUID: normalizedId, + }, + }); + + const templateQuery = ApiGetCall({ + url: "/api/ListIntuneReusableSettingTemplates", + data: normalizedId ? { id: normalizedId } : undefined, + queryKey: `ReusableSettingTemplate-${normalizedId}`, + waiting: !!normalizedId, + }); + + const templateData = Array.isArray(templateQuery.data) + ? templateQuery.data[0] + : templateQuery.data; + + const normalizedTemplate = useMemo(() => { + if (!templateData) return null; + return { + ...templateData, + // Normalize all known casing variants to the canonical RawJSON property + RawJSON: templateData.RawJSON ?? templateData.RAWJson ?? templateData.RAWJSON, + }; + }, [templateData]); + + const parsedRaw = useMemo(() => { + if (!normalizedTemplate?.RawJSON) return null; + try { + return JSON.parse(normalizedTemplate.RawJSON); + } catch (e) { + return null; + } + }, [normalizedTemplate]); + + // Strip the group collection out of the parsed RAW for cleaner form state + const sanitizedParsedRaw = useMemo(() => { + if (!parsedRaw) return null; + const clone = deepClone(parsedRaw); + if (clone?.settingInstance?.groupSettingCollectionValue) { + delete clone.settingInstance.groupSettingCollectionValue; + } + return clone; + }, [parsedRaw]); + + const groupCollection = useMemo(() => { + return ( + parsedRaw?.settingInstance?.groupSettingCollectionValue || + templateData?.settingInstance?.groupSettingCollectionValue || + [] + ); + }, [parsedRaw, templateData]); + + const groupChildDefinitions = useMemo(() => { + const first = groupCollection?.[0]?.children || []; + return { + idDef: first.find((c) => c.settingDefinitionId?.toLowerCase().includes("_id"))?.settingDefinitionId, + autoresolveDef: first.find((c) => c.settingDefinitionId?.toLowerCase().includes("_autoresolve"))?.settingDefinitionId, + keywordDef: first.find((c) => c.settingDefinitionId?.toLowerCase().includes("_keyword"))?.settingDefinitionId, + }; + }, [groupCollection]); + + useEffect(() => { + if (groupCollection) { + formControl.setValue("groupSettingCollectionValue", groupCollection); + if (sanitizedParsedRaw) { + formControl.setValue("parsedRAWJson", sanitizedParsedRaw); + } + } + }, [groupCollection, sanitizedParsedRaw, formControl]); + + useEffect(() => { + if (normalizedTemplate) { + formControl.setValue("displayName", normalizedTemplate.displayName || normalizedTemplate.name); + formControl.setValue("description", normalizedTemplate.description || normalizedTemplate.Description); + } + }, [normalizedTemplate, formControl]); + + /** + * Convert RHF form values into the API payload while preserving Graph @odata fields. + * - Flattens react-hook-form autocomplete objects to their .value. + * - Restores @odata.* keys from the original template to avoid dot-notation loss from RHF. + * - Syncs displayName/description into parsed RAW JSON and reinserts the edited groupSettingCollectionValue. + * - Builds the final payload expected by /api/AddIntuneReusableSettingTemplate, including tenant fallback. + */ + const customDataFormatter = useMemo(() => { + const getOriginalValueByPath = (obj, path) => { + if (!obj) return undefined; + const keys = path.split("."); + let current = obj; + for (const key of keys) { + if (current && typeof current === "object" && key in current) { + current = current[key]; + } else { + return undefined; + } + } + return current; + }; + + const extractValues = (obj) => { + if (obj === null || obj === undefined) return obj; + + if ( + obj && + typeof obj === "object" && + obj.hasOwnProperty("value") && + obj.hasOwnProperty("label") + ) { + return obj.value; + } + + if (Array.isArray(obj)) { + return obj.map((item) => extractValues(item)); + } + + if (typeof obj === "object") { + const result = {}; + Object.keys(obj).forEach((key) => { + const value = extractValues(obj[key]); + + if (key.endsWith("@odata") && value && typeof value === "object") { + // Restore @odata.* keys from the original template to avoid RHF dot-notation artifacts + Object.keys(value).forEach((odataKey) => { + const baseKey = key.replace("@odata", ""); + const originalKey = `${baseKey}@odata.${odataKey}`; + const originalValue = getOriginalValueByPath(normalizedTemplate, originalKey); + if (originalValue !== undefined) { + result[originalKey] = originalValue; + } + }); + } else { + result[key] = value; + } + }); + return result; + } + + return obj; + }; + + return (values) => { + const processedValues = extractValues(values) || {}; + + // Sync template/policy name & description into parsed RAW JSON, and merge edited group collection + if (processedValues.parsedRAWJson) { + if (processedValues.displayName) { + processedValues.parsedRAWJson.displayName = processedValues.displayName; + } + if (processedValues.description) { + processedValues.parsedRAWJson.description = processedValues.description; + } + + if (processedValues.groupSettingCollectionValue && processedValues.parsedRAWJson.settingInstance) { + processedValues.parsedRAWJson.settingInstance.groupSettingCollectionValue = + processedValues.groupSettingCollectionValue; + } + } + + return { + GUID: processedValues.GUID || normalizedId, + displayName: processedValues.displayName, + description: processedValues.description, + package: processedValues.package, + rawJSON: JSON.stringify(processedValues.parsedRAWJson || processedValues, null, 2), + tenantFilter: processedValues.tenantFilter || currentTenant, + }; + }; + }, [currentTenant, normalizedId, normalizedTemplate]); + + const { fields, append, remove } = useFieldArray({ + control: formControl.control, + name: "groupSettingCollectionValue", + }); + + const createEmptyEntry = () => { + const { idDef, autoresolveDef, keywordDef } = groupChildDefinitions; + const children = []; + + if (idDef) { + children.push({ + "@odata.type": "#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance", + settingDefinitionId: idDef, + simpleSettingValue: { + "@odata.type": "#microsoft.graph.deviceManagementConfigurationStringSettingValue", + value: generateGuid(), + }, + }); + } + + if (autoresolveDef) { + children.push({ + "@odata.type": "#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance", + settingDefinitionId: autoresolveDef, + choiceSettingValue: { value: "", children: [] }, + }); + } + + if (keywordDef) { + children.push({ + "@odata.type": "#microsoft.graph.deviceManagementConfigurationSimpleSettingInstance", + settingDefinitionId: keywordDef, + simpleSettingValue: { + "@odata.type": "#microsoft.graph.deviceManagementConfigurationStringSettingValue", + value: "", + }, + }); + } + + return { children }; + }; + + return ( + + + {templateQuery.isLoading ? ( + + ) : templateQuery.isError || !normalizedTemplate ? ( + Error loading reusable settings template. + ) : ( + <> + + Template + + + + + + + {fields?.length > 0 && ( + + + Group Setting Collection (Policy) + + + + + ID + Autoresolve + Keyword + Actions + + + + {fields.map((field, index) => { + const idPath = `groupSettingCollectionValue.${index}.children.0.simpleSettingValue.value`; + const autoresolvePath = `groupSettingCollectionValue.${index}.children.1.choiceSettingValue.value`; + const keywordPath = `groupSettingCollectionValue.${index}.children.2.simpleSettingValue.value`; + + const autoresolveBase = groupChildDefinitions.autoresolveDef || "autoresolve"; + const autoresolveTrue = `${autoresolveBase}_true`; + const autoresolveFalse = `${autoresolveBase}_false`; + + return ( + + + + + + + + + + + + + + + ); + })} + +
+ + + +
+ )} + + )} +
+
+ ); +}; + +EditReusableSettingsTemplate.getLayout = (page) => {page}; + +export default EditReusableSettingsTemplate; diff --git a/src/pages/endpoint/MEM/reusable-settings-templates/index.js b/src/pages/endpoint/MEM/reusable-settings-templates/index.js new file mode 100644 index 000000000000..230f214e3fca --- /dev/null +++ b/src/pages/endpoint/MEM/reusable-settings-templates/index.js @@ -0,0 +1,108 @@ +import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; +import CippJsonView from "../../../../components/CippFormPages/CippJSONView"; +import { Button } from "@mui/material"; +import Link from "next/link"; +import { AddBox, GitHub, Delete, Edit } from "@mui/icons-material"; +import { ApiGetCall } from "/src/api/ApiCall"; + +const Page = () => { + const pageTitle = "Reusable Settings Templates"; + + const integrations = ApiGetCall({ + url: "/api/ListExtensionsConfig", + queryKey: "Integrations", + refetchOnMount: false, + refetchOnReconnect: false, + }); + + const actions = [ + { + label: "Edit Template", + icon: , + link: "/endpoint/MEM/reusable-settings-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: "This field is required" }, + }, + }, + { + label: "Commit Message", + placeholder: "Enter a commit message for adding this file to GitHub", + name: "Message", + type: "textField", + multiline: true, + required: true, + rows: 4, + }, + ], + confirmText: "Are you sure you want to save this template to the selected repository?", + condition: () => integrations.isSuccess && integrations?.data?.GitHub?.Enabled, + }, + { + label: "Delete Template", + type: "POST", + url: "/api/RemoveIntuneReusableSettingTemplate", + icon: , + data: { + ID: "GUID", + }, + confirmText: "Do you want to delete the template?", + multiPost: false, + }, + ]; + + const offCanvas = { + children: (row) => , + size: "lg", + }; + + const simpleColumns = ["displayName", "package", "description", "isSynced"]; + + return ( + }> + Add Reusable Settings Template + + } + apiUrl="/api/ListIntuneReusableSettingTemplates" + tenantInTitle={false} + actions={actions} + offCanvas={offCanvas} + simpleColumns={simpleColumns} + queryKey="ListIntuneReusableSettingTemplates-table" + /> + ); +}; + +Page.getLayout = (page) => {page}; +export default Page; diff --git a/src/pages/endpoint/MEM/reusable-settings/edit.jsx b/src/pages/endpoint/MEM/reusable-settings/edit.jsx new file mode 100644 index 000000000000..b6dcd1825ffa --- /dev/null +++ b/src/pages/endpoint/MEM/reusable-settings/edit.jsx @@ -0,0 +1,136 @@ +import { useEffect } from "react"; +import { Alert, Box, Stack } from "@mui/material"; +import { Grid } from "@mui/system"; +import { useForm } from "react-hook-form"; +import { useRouter } from "next/router"; +import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "/src/components/CippFormPages/CippFormPage"; +import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton"; +import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import CippJsonView from "/src/components/CippFormPages/CippJSONView"; +import { ApiGetCall } from "/src/api/ApiCall"; +import { useSettings } from "/src/hooks/use-settings"; + +const EditReusableSetting = () => { + const router = useRouter(); + const { id, tenant } = router.query; + const { currentTenant } = useSettings(); + + const effectiveTenant = tenant || currentTenant; + + const formControl = useForm({ + mode: "onChange", + defaultValues: { + tenantFilter: effectiveTenant, + }, + }); + + const { reset } = formControl; + + const settingQuery = ApiGetCall({ + url: "/api/ListIntuneReusableSettings", + queryKey: ["ListIntuneReusableSettings", effectiveTenant, id], + enabled: !!id && !!effectiveTenant, + data: { tenantFilter: effectiveTenant, ID: id }, + }); + + const record = Array.isArray(settingQuery.data) ? settingQuery.data[0] : settingQuery.data; + + useEffect(() => { + if (record) { + reset({ + tenantFilter: effectiveTenant, + ID: record.id, + displayName: record.displayName, + description: record.description, + rawJSON: record.RawJSON, + }); + } + }, [record, effectiveTenant, reset]); + + const safeJson = () => { + if (!record?.RawJSON) return null; + try { + return JSON.parse(record.RawJSON); + } catch (e) { + console.error("Failed to parse RawJSON for reusable setting preview", { + error: e, + recordId: record?.id, + }); + return null; + } + }; + + const customDataformatter = (values) => ({ + tenantFilter: values.tenantFilter || effectiveTenant, + ID: values.ID, // forward the existing setting id so the API updates the same record + TemplateId: values.ID, // keep legacy TemplateId for API compatibility + displayName: values.displayName, + description: values.description, + rawJSON: values.rawJSON, + }); + + return ( + + + {settingQuery.isLoading ? ( + + ) : settingQuery.isError || !record ? ( + Error loading reusable setting or setting not found. + ) : ( + + + + + + + + + + + + + + + + + + + )} + + + ); +}; + +EditReusableSetting.getLayout = (page) => {page}; + +export default EditReusableSetting; diff --git a/src/pages/endpoint/MEM/reusable-settings/index.js b/src/pages/endpoint/MEM/reusable-settings/index.js new file mode 100644 index 000000000000..2ac91d5d9955 --- /dev/null +++ b/src/pages/endpoint/MEM/reusable-settings/index.js @@ -0,0 +1,65 @@ +import { Book, DeleteForever } from "@mui/icons-material"; +import { CippReusableSettingsDeployDrawer } from "/src/components/CippComponents/CippReusableSettingsDeployDrawer.jsx"; +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"; + +const Page = () => { + const { currentTenant } = useSettings(); + const pageTitle = "Reusable Settings"; + + const actions = [ + { + label: "Edit Reusable Setting", + link: `/endpoint/MEM/reusable-settings/edit?id=[id]&tenant=${currentTenant}&tenantFilter=${currentTenant}`, + }, + { + label: "Delete Reusable Setting", + type: "POST", + url: "/api/RemoveIntuneReusableSetting", + icon: , + color: "error", + data: { + ID: "id", + DisplayName: "displayName", + }, + confirmText: "Delete this reusable setting from the tenant?", + multiPost: false, + }, + { + label: "Create Template from Setting", + type: "POST", + url: "/api/AddIntuneReusableSettingTemplate", + icon: , + data: { + displayName: "displayName", + description: "description", + rawJSON: "RawJSON", + }, + confirmText: "Create a reusable settings template from this entry?", + multiPost: false, + }, + ]; + + const offCanvas = { + children: (row) => , + size: "lg", + }; + + return ( + } + apiUrl="/api/ListIntuneReusableSettings" + queryKey={`ListIntuneReusableSettings-${currentTenant}`} + actions={actions} + offCanvas={offCanvas} + simpleColumns={["displayName", "description", "id", "version"]} + /> + ); +}; + +Page.getLayout = (page) => {page}; + +export default Page; diff --git a/src/pages/tenant/administration/applications/app-registrations.js b/src/pages/tenant/administration/applications/app-registrations.js index 74dcf8b4e072..1adcf038947c 100644 --- a/src/pages/tenant/administration/applications/app-registrations.js +++ b/src/pages/tenant/administration/applications/app-registrations.js @@ -47,6 +47,13 @@ const Page = () => { DisplayName: "displayName", Type: "application", }, + fields: [ + { + type: "switch", + name: "Overwrite", + label: "Overwrite Existing Template", + }, + ], confirmText: "Create a deployment template from '[displayName]'? This will copy all permissions and create a reusable template. If you run this from a customer tenant, the App Registration will first be copied to the partner tenant as a multi-tenant app.", condition: (row) => canWriteApplication && !row?.applicationTemplateId, @@ -130,7 +137,7 @@ const Page = () => { options={ row?.passwordCredentials?.map((cred) => ({ label: `${cred.displayName || "Unnamed"} (Expiration: ${new Date( - cred.endDateTime + cred.endDateTime, ).toLocaleDateString()})`, value: cred.keyId, })) || [] diff --git a/src/pages/tenant/administration/applications/enterprise-apps.js b/src/pages/tenant/administration/applications/enterprise-apps.js index 39aac9021b60..6a408ca37021 100644 --- a/src/pages/tenant/administration/applications/enterprise-apps.js +++ b/src/pages/tenant/administration/applications/enterprise-apps.js @@ -49,6 +49,13 @@ const Page = () => { DisplayName: "displayName", Type: "servicePrincipal", }, + fields: [ + { + type: "switch", + name: "Overwrite", + label: "Overwrite Existing Template", + }, + ], confirmText: "Create a deployment template from '[displayName]'? This will copy all permissions and create a reusable template.", condition: (row) => canWriteApplication && row?.signInAudience === "AzureADMultipleOrgs", @@ -78,7 +85,7 @@ const Page = () => { options={ row?.passwordCredentials?.map((cred) => ({ label: `${cred.displayName || "Unnamed"} (Expiration: ${new Date( - cred.endDateTime + cred.endDateTime, ).toLocaleDateString()})`, value: cred.keyId, })) || [] diff --git a/src/pages/tenant/standards/domains-analyser/index.js b/src/pages/tenant/standards/domains-analyser/index.js index 58ee11a13fb4..aef98643e21c 100644 --- a/src/pages/tenant/standards/domains-analyser/index.js +++ b/src/pages/tenant/standards/domains-analyser/index.js @@ -6,7 +6,7 @@ import { ApiGetCall } from "../../../../api/ApiCall"; import { useSettings } from "../../../../hooks/use-settings"; import { CippApiResults } from "../../../../components/CippComponents/CippApiResults"; import { CippDomainCards } from "../../../../components/CippCards/CippDomainCards"; -import { DeleteForever, TravelExplore, Refresh } from "@mui/icons-material"; +import { DeleteForever, TravelExplore, Refresh, Settings } from "@mui/icons-material"; import { DomainAnalyserDialog } from "../../../../components/CippComponents/DomainAnalyserDialog"; import { useDialog } from "../../../../hooks/use-dialog"; @@ -19,9 +19,27 @@ const Page = () => { waiting: false, }); const actions = [ + { + label: "Add/Modify DKIM Selectors", + type: "POST", + icon: , + url: "/api/ExecDnsConfig", + data: { Action: "!SetDkimConfig", Domain: "Domain" }, + confirmText: "Enter the DKIM selectors for [Domain] (comma-separated)", + fields: [ + { + type: "textField", + name: "Selector", + label: "DKIM Selectors", + placeholder: "selector1, selector2, selector3", + required: true, + }, + ], + multiPost: false, + }, { label: "Delete from analyser", - type: "GET", + type: "POST", icon: , url: "/api/ExecDnsConfig", data: { Action: "!RemoveDomain", Domain: "Domain" }, diff --git a/yarn.lock b/yarn.lock index 309852f41632..ca973629395e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2134,20 +2134,20 @@ resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.11.2.tgz#00409e743ac4eea9afe5b7708594d5fcebb00212" integrity sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw== -"@tiptap/core@^3.13.0", "@tiptap/core@^3.4.1": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-3.13.0.tgz#ae3fe6fe7732f36b6ea8a2198e1fc53a4ad0d0d2" - integrity sha512-iUelgiTMgPVMpY5ZqASUpk8mC8HuR9FWKaDzK27w9oWip9tuB54Z8mePTxNcQaSPb6ErzEaC8x8egrRt7OsdGQ== +"@tiptap/core@^3.19.0", "@tiptap/core@^3.4.1": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-3.19.0.tgz#dca483b50e1b8a596f695aecde387a79fe7da717" + integrity sha512-bpqELwPW+DG8gWiD8iiFtSl4vIBooG5uVJod92Qxn3rA9nFatyXRr4kNbMJmOZ66ezUvmCjXVe/5/G4i5cyzKA== -"@tiptap/extension-blockquote@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-3.13.0.tgz#33508ad7f0bd4d74d5065f11d6e33c50ef8835a2" - integrity sha512-K1z/PAIIwEmiWbzrP//4cC7iG1TZknDlF1yb42G7qkx2S2X4P0NiqX7sKOej3yqrPjKjGwPujLMSuDnCF87QkQ== +"@tiptap/extension-blockquote@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-3.19.0.tgz#86c52e8e3b6d1e072ae0d9c895723034a1e37096" + integrity sha512-y3UfqY9KD5XwWz3ndiiJ089Ij2QKeiXy/g1/tlAN/F1AaWsnkHEHMLxCP1BIqmMpwsX7rZjMLN7G5Lp7c9682A== -"@tiptap/extension-bold@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-3.13.0.tgz#1fbff35b20da292172fc5a1886576c3410e1e3ca" - integrity sha512-VYiDN9EEwR6ShaDLclG8mphkb/wlIzqfk7hxaKboq1G+NSDj8PcaSI9hldKKtTCLeaSNu6UR5nkdu/YHdzYWTw== +"@tiptap/extension-bold@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-3.19.0.tgz#ef0ddfd9b242ef9c25e3348aef9bf2dc681cdc19" + integrity sha512-UZgb1d0XK4J/JRIZ7jW+s4S6KjuEDT2z1PPM6ugcgofgJkWQvRZelCPbmtSFd3kwsD+zr9UPVgTh9YIuGQ8t+Q== "@tiptap/extension-bubble-menu@^3.13.0": version "3.13.0" @@ -2156,127 +2156,127 @@ dependencies: "@floating-ui/dom" "^1.0.0" -"@tiptap/extension-bullet-list@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-3.13.0.tgz#277c9380704f618d71b63278da7bba45ed2b7905" - integrity sha512-fFQmmEUoPzRGiQJ/KKutG35ZX21GE+1UCDo8Q6PoWH7Al9lex47nvyeU1BiDYOhcTKgIaJRtEH5lInsOsRJcSA== +"@tiptap/extension-bullet-list@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-3.19.0.tgz#acf12e952b6a5873dc20b58530f2f524807bbd6f" + integrity sha512-F9uNnqd0xkJbMmRxVI5RuVxwB9JaCH/xtRqOUNQZnRBt7IdAElCY+Dvb4hMCtiNv+enGM/RFGJuFHR9TxmI7rw== -"@tiptap/extension-code-block@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-3.13.0.tgz#ded41a224db15e938c6a871462a0c33c00acd657" - integrity sha512-kIwfQ4iqootsWg9e74iYJK54/YMIj6ahUxEltjZRML5z/h4gTDcQt2eTpnEC8yjDjHeUVOR94zH9auCySyk9CQ== +"@tiptap/extension-code-block@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-3.19.0.tgz#71a7a362b3fa68c1789c8b9ac224ca89eb410630" + integrity sha512-b/2qR+tMn8MQb+eaFYgVk4qXnLNkkRYmwELQ8LEtEDQPxa5Vl7J3eu8+4OyoIFhZrNDZvvoEp80kHMCP8sI6rg== -"@tiptap/extension-code@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-3.13.0.tgz#7c05fb8477356aebe8b1f00117a32d3abbf24357" - integrity sha512-sF5raBni6iSVpXWvwJCAcOXw5/kZ+djDHx1YSGWhopm4+fsj0xW7GvVO+VTwiFjZGKSw+K5NeAxzcQTJZd3Vhw== +"@tiptap/extension-code@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-3.19.0.tgz#15d53c139ad64d1debcc08c7ca5afbcc8e531f0b" + integrity sha512-2kqqQIXBXj2Or+4qeY3WoE7msK+XaHKL6EKOcKlOP2BW8eYqNTPzNSL+PfBDQ3snA7ljZQkTs/j4GYDj90vR1A== -"@tiptap/extension-document@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-3.13.0.tgz#74757e23bf92bba82226a91580ce738bc68bd3af" - integrity sha512-RjU7hTJwjKXIdY57o/Pc+Yr8swLkrwT7PBQ/m+LCX5oO/V2wYoWCjoBYnK5KSHrWlNy/aLzC33BvLeqZZ9nzlQ== +"@tiptap/extension-document@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-3.19.0.tgz#dfa6889cff748d489e0bc1028918bf4571372ba5" + integrity sha512-AOf0kHKSFO0ymjVgYSYDncRXTITdTcrj1tqxVazrmO60KNl1Rc2dAggDvIVTEBy5NvceF0scc7q3sE/5ZtVV7A== -"@tiptap/extension-dropcursor@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-3.13.0.tgz#04f7659c86558ebeb068fd7d5c2474d8bd28b430" - integrity sha512-m7GPT3c/83ni+bbU8c+3dpNa8ug+aQ4phNB1Q52VQG3oTonDJnZS7WCtn3lB/Hi1LqoqMtEHwhepU2eD+JeXqQ== +"@tiptap/extension-dropcursor@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-3.19.0.tgz#fbef441944842f23fe0a35154b519103166a4848" + integrity sha512-sf3dEZXiLvsGqVK2maUIzXY6qtYYCvBumag7+VPTMGQ0D4hiZ1X/4ukt4+6VXDg5R2WP1CoIt/QvUetUjWNhbQ== "@tiptap/extension-floating-menu@^3.13.0": version "3.13.0" resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-3.13.0.tgz#03d03292add49d1b380cdb1ff3890b2956d4e3f5" integrity sha512-OsezV2cMofZM4c13gvgi93IEYBUzZgnu8BXTYZQiQYekz4bX4uulBmLa1KOA9EN71FzS+SoLkXHU0YzlbLjlxA== -"@tiptap/extension-gapcursor@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-3.13.0.tgz#5e4fbd3b066fa10656314bbbff2e329709be5d2c" - integrity sha512-KVxjQKkd964nin+1IdM2Dvej/Jy4JTMcMgq5seusUhJ9T9P8F9s2D5Iefwgkps3OCzub/aF+eAsZe+1P5KSIgA== +"@tiptap/extension-gapcursor@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-3.19.0.tgz#64e5462a4ab2f0bd110738410dcbf3597d76349f" + integrity sha512-w7DACS4oSZaDWjz7gropZHPc9oXqC9yERZTcjWxyORuuIh1JFf0TRYspleK+OK28plK/IftojD/yUDn1MTRhvA== -"@tiptap/extension-hard-break@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-3.13.0.tgz#b1444339c544f27fe8cff8dcbdb99007e0cdc3e1" - integrity sha512-nH1OBaO+/pakhu+P1jF208mPgB70IKlrR/9d46RMYoYbqJTNf4KVLx5lHAOHytIhjcNg+MjyTfJWfkK+dyCCyg== +"@tiptap/extension-hard-break@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-3.19.0.tgz#7120524cec9ed4b957963693cb4c57cbecbaecf8" + integrity sha512-lAmQraYhPS5hafvCl74xDB5+bLuNwBKIEsVoim35I0sDJj5nTrfhaZgMJ91VamMvT+6FF5f1dvBlxBxAWa8jew== -"@tiptap/extension-heading@^3.13.0", "@tiptap/extension-heading@^3.4.1": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-3.13.0.tgz#ead7f224de24ac66bb198cabe6b2af9617967583" - integrity sha512-8VKWX8waYPtUWN97J89em9fOtxNteh6pvUEd0htcOAtoxjt2uZjbW5N4lKyWhNKifZBrVhH2Cc2NUPuftCVgxw== +"@tiptap/extension-heading@^3.19.0", "@tiptap/extension-heading@^3.4.1": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-3.19.0.tgz#d0bc93426c01a2ed36b9124c1a8205ab3945e77a" + integrity sha512-uLpLlfyp086WYNOc0ekm1gIZNlEDfmzOhKzB0Hbyi6jDagTS+p9mxUNYeYOn9jPUxpFov43+Wm/4E24oY6B+TQ== -"@tiptap/extension-horizontal-rule@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.13.0.tgz#c51eb35f3b3bf6308ab6b354a06c0e96c19dbff6" - integrity sha512-ZUFyORtjj22ib8ykbxRhWFQOTZjNKqOsMQjaAGof30cuD2DN5J5pMz7Haj2fFRtLpugWYH+f0Mi+WumQXC3hCw== +"@tiptap/extension-horizontal-rule@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.19.0.tgz#0e77078fcd53beca786277ce83d259e2103cc361" + integrity sha512-iqUHmgMGhMgYGwG6L/4JdelVQ5Mstb4qHcgTGd/4dkcUOepILvhdxajPle7OEdf9sRgjQO6uoAU5BVZVC26+ng== "@tiptap/extension-image@^3.4.1": version "3.13.0" resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-3.13.0.tgz#55edb952e86c2ebed436cd53def8b2e743d71d7e" integrity sha512-223uzLUkIa1rkK7aQK3AcIXe6LbCtmnpVb7sY5OEp+LpSaSPyXwyrZ4A0EO1o98qXG68/0B2OqMntFtA9c5Fbw== -"@tiptap/extension-italic@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-3.13.0.tgz#c855521360c8079574f7b0855148e4f561ba396a" - integrity sha512-XbVTgmzk1kgUMTirA6AGdLTcKHUvEJoh3R4qMdPtwwygEOe7sBuvKuLtF6AwUtpnOM+Y3tfWUTNEDWv9AcEdww== +"@tiptap/extension-italic@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-3.19.0.tgz#af2a9c095ec846e379041f3e17e1dd101a5a4bf8" + integrity sha512-6GffxOnS/tWyCbDkirWNZITiXRta9wrCmrfa4rh+v32wfaOL1RRQNyqo9qN6Wjyl1R42Js+yXTzTTzZsOaLMYA== -"@tiptap/extension-link@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-3.13.0.tgz#c6b087a39860068b93d1fb8fcbebbd360f0188b4" - integrity sha512-LuFPJ5GoL12GHW4A+USsj60O90pLcwUPdvEUSWewl9USyG6gnLnY/j5ZOXPYH7LiwYW8+lhq7ABwrDF2PKyBbA== +"@tiptap/extension-link@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-3.19.0.tgz#e8e656735bda6ca1d4b6577821e06274ab0ff6c8" + integrity sha512-HEGDJnnCPfr7KWu7Dsq+eRRe/mBCsv6DuI+7fhOCLDJjjKzNgrX2abbo/zG3D/4lCVFaVb+qawgJubgqXR/Smw== dependencies: linkifyjs "^4.3.2" -"@tiptap/extension-list-item@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-3.13.0.tgz#03f17af7ed2d0643638e07ce96ad0f9c044ff69b" - integrity sha512-63NbcS/XeQP2jcdDEnEAE3rjJICDj8y1SN1h/MsJmSt1LusnEo8WQ2ub86QELO6XnD3M04V03cY6Knf6I5mTkw== +"@tiptap/extension-list-item@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-3.19.0.tgz#b2218ff6be694b581fd7d817810a33ee1c218311" + integrity sha512-VsSKuJz4/Tb6ZmFkXqWpDYkRzmaLTyE6dNSEpNmUpmZ32sMqo58mt11/huADNwfBFB0Ve7siH/VnFNIJYY3xvg== -"@tiptap/extension-list-keymap@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-list-keymap/-/extension-list-keymap-3.13.0.tgz#75ee2c28f5d944c407309ce987d07ae23c4cd45a" - integrity sha512-P+HtIa1iwosb1feFc8B/9MN5EAwzS+/dZ0UH0CTF2E4wnp5Z9OMxKl1IYjfiCwHzZrU5Let+S/maOvJR/EmV0g== +"@tiptap/extension-list-keymap@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-list-keymap/-/extension-list-keymap-3.19.0.tgz#41b87b154560aad92e779bff5c6e32e125b792ea" + integrity sha512-bxgmAgA3RzBGA0GyTwS2CC1c+QjkJJq9hC+S6PSOWELGRiTbwDN3MANksFXLjntkTa0N5fOnL27vBHtMStURqw== -"@tiptap/extension-list@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-list/-/extension-list-3.13.0.tgz#6981a395f2fbe46d9ad20deb75cf65aa9e33feba" - integrity sha512-MMFH0jQ4LeCPkJJFyZ77kt6eM/vcKujvTbMzW1xSHCIEA6s4lEcx9QdZMPpfmnOvTzeoVKR4nsu2t2qT9ZXzAw== +"@tiptap/extension-list@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-list/-/extension-list-3.19.0.tgz#737dcb56ba9838a4431c1afb035bd622fab46d21" + integrity sha512-N6nKbFB2VwMsPlCw67RlAtYSK48TAsAUgjnD+vd3ieSlIufdQnLXDFUP6hFKx9mwoUVUgZGz02RA6bkxOdYyTw== -"@tiptap/extension-ordered-list@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-3.13.0.tgz#552d4a57e9116fd7d32e49c5cdc346baf0bbfd74" - integrity sha512-QuDyLzuK/3vCvx9GeKhgvHWrGECBzmJyAx6gli2HY+Iil7XicbfltV4nvhIxgxzpx3LDHLKzJN9pBi+2MzX60g== +"@tiptap/extension-ordered-list@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-3.19.0.tgz#f6f8bfe41d3429c505b44764b473b6dfd7bcd2a1" + integrity sha512-cxGsINquwHYE1kmhAcLNLHAofmoDEG6jbesR5ybl7tU5JwtKVO7S/xZatll2DU1dsDAXWPWEeeMl4e/9svYjCg== -"@tiptap/extension-paragraph@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-3.13.0.tgz#01881b5954136de5059e7882be5b210eca0dac46" - integrity sha512-9csQde1i0yeZI5oQQ9e1GYNtGL2JcC2d8Fwtw9FsGC8yz2W0h+Fmk+3bc2kobbtO5LGqupSc1fKM8fAg5rSRDg== +"@tiptap/extension-paragraph@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-3.19.0.tgz#91adde189aabf13a2bfbb2d961833d3bc2bc055f" + integrity sha512-xWa6gj82l5+AzdYyrSk9P4ynySaDzg/SlR1FarXE5yPXibYzpS95IWaVR0m2Qaz7Rrk+IiYOTGxGRxcHLOelNg== -"@tiptap/extension-strike@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-3.13.0.tgz#f753bae727549fb32ee9251036890ed5f39bc443" - integrity sha512-VHhWNqTAMOfrC48m2FcPIZB0nhl6XHQviAV16SBc+EFznKNv9tQUsqQrnuQ2y6ZVfqq5UxvZ3hKF/JlN/Ff7xw== +"@tiptap/extension-strike@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-3.19.0.tgz#eac7712cc791488f4c1c48baf3aed1a8d95f398c" + integrity sha512-xYpabHsv7PccLUBQaP8AYiFCnYbx6P93RHPd0lgNwhdOjYFd931Zy38RyoxPHAgbYVmhf1iyx7lpuLtBnhS5dA== "@tiptap/extension-table@^3.4.1": version "3.13.0" resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-3.13.0.tgz#83283bc818582e621cefabf173beeb37fe6f30ba" integrity sha512-LcH9KE4QBUJ6IPwt1Uo5iU7zatFjUUvXbctIu2fKQ9nqJ7nNSFxRhkNyporVFkTWYH7/rb0qMoF1VxSUGefG5w== -"@tiptap/extension-text@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-3.13.0.tgz#90d38438eb99135b1221d7f2944a06545f21d39d" - integrity sha512-VcZIna93rixw7hRkHGCxDbL3kvJWi80vIT25a2pXg0WP1e7Pi3nBYvZIL4SQtkbBCji9EHrbZx3p8nNPzfazYw== +"@tiptap/extension-text@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-3.19.0.tgz#353278c97bd8f5bdc29f06942fbd1e856bdb5b18" + integrity sha512-K95+SnbZy0h6hNFtfy23n8t/nOcTFEf69In9TSFVVmwn/Nwlke+IfiESAkqbt1/7sKJeegRXYO7WzFEmFl9Q/g== -"@tiptap/extension-underline@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-3.13.0.tgz#7fc969c3b7adc7d7cc7def498f85c4cb30cf3aba" - integrity sha512-VDQi+UYw0tFnfghpthJTFmtJ3yx90kXeDwFvhmT8G+O+si5VmP05xYDBYBmYCix5jqKigJxEASiBL0gYOgMDEg== +"@tiptap/extension-underline@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-3.19.0.tgz#bbc81d085725981d256127ab416f91d0802ec2a4" + integrity sha512-800MGEWfG49j10wQzAFiW/ele1HT04MamcL8iyuPNu7ZbjbGN2yknvdrJlRy7hZlzIrVkZMr/1tz62KN33VHIw== -"@tiptap/extensions@^3.13.0": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/extensions/-/extensions-3.13.0.tgz#542ee8a97575ae32090302b7f09522e025715297" - integrity sha512-i7O0ptSibEtTy+2PIPsNKEvhTvMaFJg1W4Oxfnbuxvaigs7cJV9Q0lwDUcc7CPsNw2T1+44wcxg431CzTvdYoA== +"@tiptap/extensions@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/extensions/-/extensions-3.19.0.tgz#5747c0ebf460b9669e8b4362561872448f66abfe" + integrity sha512-ZmGUhLbMWaGqnJh2Bry+6V4M6gMpUDYo4D1xNux5Gng/E/eYtc+PMxMZ/6F7tNTAuujLBOQKj6D+4SsSm457jw== -"@tiptap/pm@^3.13.0", "@tiptap/pm@^3.4.1": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-3.13.0.tgz#d01e9f08e2be3e6bfa69ed4457a1c2fee87157b3" - integrity sha512-WKR4ucALq+lwx0WJZW17CspeTpXorbIOpvKv5mulZica6QxqfMhn8n1IXCkDws/mCoLRx4Drk5d377tIjFNsvQ== +"@tiptap/pm@^3.19.0", "@tiptap/pm@^3.4.1": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-3.19.0.tgz#5cb499c7b2603ec6550d0c7a70b924f27fdb7692" + integrity sha512-789zcnM4a8OWzvbD2DL31d0wbSm9BVeO/R7PLQwLIGysDI3qzrcclyZ8yhqOEVuvPitRRwYLq+mY14jz7kY4cw== dependencies: prosemirror-changeset "^2.3.0" prosemirror-collab "^1.3.1" @@ -2309,35 +2309,35 @@ "@tiptap/extension-bubble-menu" "^3.13.0" "@tiptap/extension-floating-menu" "^3.13.0" -"@tiptap/starter-kit@^3.4.1": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-3.13.0.tgz#7f803f0e089a7c2cbd016ad79b257c4cbe910208" - integrity sha512-Ojn6sRub04CRuyQ+9wqN62JUOMv+rG1vXhc2s6DCBCpu28lkCMMW+vTe7kXJcEdbot82+5swPbERw9vohswFzg== - dependencies: - "@tiptap/core" "^3.13.0" - "@tiptap/extension-blockquote" "^3.13.0" - "@tiptap/extension-bold" "^3.13.0" - "@tiptap/extension-bullet-list" "^3.13.0" - "@tiptap/extension-code" "^3.13.0" - "@tiptap/extension-code-block" "^3.13.0" - "@tiptap/extension-document" "^3.13.0" - "@tiptap/extension-dropcursor" "^3.13.0" - "@tiptap/extension-gapcursor" "^3.13.0" - "@tiptap/extension-hard-break" "^3.13.0" - "@tiptap/extension-heading" "^3.13.0" - "@tiptap/extension-horizontal-rule" "^3.13.0" - "@tiptap/extension-italic" "^3.13.0" - "@tiptap/extension-link" "^3.13.0" - "@tiptap/extension-list" "^3.13.0" - "@tiptap/extension-list-item" "^3.13.0" - "@tiptap/extension-list-keymap" "^3.13.0" - "@tiptap/extension-ordered-list" "^3.13.0" - "@tiptap/extension-paragraph" "^3.13.0" - "@tiptap/extension-strike" "^3.13.0" - "@tiptap/extension-text" "^3.13.0" - "@tiptap/extension-underline" "^3.13.0" - "@tiptap/extensions" "^3.13.0" - "@tiptap/pm" "^3.13.0" +"@tiptap/starter-kit@^3.19.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-3.19.0.tgz#312440bd18c3cce379ea8eab3fe174b8141dd313" + integrity sha512-dTCkHEz+Y8ADxX7h+xvl6caAj+3nII/wMB1rTQchSuNKqJTOrzyUsCWm094+IoZmLT738wANE0fRIgziNHs/ug== + dependencies: + "@tiptap/core" "^3.19.0" + "@tiptap/extension-blockquote" "^3.19.0" + "@tiptap/extension-bold" "^3.19.0" + "@tiptap/extension-bullet-list" "^3.19.0" + "@tiptap/extension-code" "^3.19.0" + "@tiptap/extension-code-block" "^3.19.0" + "@tiptap/extension-document" "^3.19.0" + "@tiptap/extension-dropcursor" "^3.19.0" + "@tiptap/extension-gapcursor" "^3.19.0" + "@tiptap/extension-hard-break" "^3.19.0" + "@tiptap/extension-heading" "^3.19.0" + "@tiptap/extension-horizontal-rule" "^3.19.0" + "@tiptap/extension-italic" "^3.19.0" + "@tiptap/extension-link" "^3.19.0" + "@tiptap/extension-list" "^3.19.0" + "@tiptap/extension-list-item" "^3.19.0" + "@tiptap/extension-list-keymap" "^3.19.0" + "@tiptap/extension-ordered-list" "^3.19.0" + "@tiptap/extension-paragraph" "^3.19.0" + "@tiptap/extension-strike" "^3.19.0" + "@tiptap/extension-text" "^3.19.0" + "@tiptap/extension-underline" "^3.19.0" + "@tiptap/extensions" "^3.19.0" + "@tiptap/pm" "^3.19.0" "@trysound/sax@0.2.0": version "0.2.0" @@ -2567,11 +2567,6 @@ dependencies: csstype "^3.2.2" -"@types/trusted-types@^1.0.6": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-1.0.6.tgz#569b8a08121d3203398290d602d84d73c8dcf5da" - integrity sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw== - "@types/trusted-types@^2.0.7": version "2.0.7" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" @@ -3728,6 +3723,13 @@ domhandler@^5.0.2, domhandler@^5.0.3: dependencies: domelementtype "^2.3.0" +dompurify@3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.2.7.tgz#721d63913db5111dd6dfda8d3a748cfd7982d44a" + integrity sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw== + optionalDependencies: + "@types/trusted-types" "^2.0.7" + dompurify@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.3.1.tgz#c7e1ddebfe3301eacd6c0c12a4af284936dbbb86" @@ -5100,12 +5102,12 @@ iterator.prototype@^1.1.5: has-symbols "^1.1.0" set-function-name "^2.0.2" -javascript-time-ago@^2.5.11: - version "2.5.12" - resolved "https://registry.yarnpkg.com/javascript-time-ago/-/javascript-time-ago-2.5.12.tgz#b789f5c84b0518b38700722627c404a09299c5f9" - integrity sha512-s8PPq2HQ3HIbSU0SjhNvTitf5VoXbQWof9q6k3gIX7F2il0ptjD5lONTDccpuKt/2U7RjbCp/TCHPK7eDwO7zQ== +javascript-time-ago@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/javascript-time-ago/-/javascript-time-ago-2.6.2.tgz#b66ada9080440c472e53845d7912e9d2e7088045" + integrity sha512-gagMB4fetS1M1ZHaxQ9kX2amORXa5Los5PGh8NZzWSKrytz43KnpJlaPFTXbg/R7iPMN7U/6MoevgxqJ0QQ5lA== dependencies: - relative-time-format "^1.1.7" + relative-time-format "^1.1.11" jay-peg@^1.1.1: version "1.1.1" @@ -5360,6 +5362,11 @@ markdown-table@^3.0.0: resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.4.tgz#fe44d6d410ff9d6f2ea1797a3f60aa4d2b631c2a" integrity sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw== +marked@14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/marked/-/marked-14.0.0.tgz#79a1477358a59e0660276f8fec76de2c33f35d83" + integrity sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ== + material-react-table@^3.0.1: version "3.2.1" resolved "https://registry.yarnpkg.com/material-react-table/-/material-react-table-3.2.1.tgz#56f595755cab3b669b399999fed9eb305fbb6dd7" @@ -5902,12 +5909,13 @@ minimist@^1.2.0, minimist@^1.2.6, minimist@~1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -monaco-editor@^0.53.0: - version "0.53.0" - resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.53.0.tgz#2f485492e0ee822be13b1b45e3092922963737ae" - integrity sha512-0WNThgC6CMWNXXBxTbaYYcunj08iB5rnx4/G56UOPeL9UVIUGGHA1GR0EWIh9Ebabj7NpCRawQ5b0hfN1jQmYQ== +monaco-editor@^0.55.1: + version "0.55.1" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.55.1.tgz#e74c6fe5a6bf985b817d2de3eb88d56afc494a1b" + integrity sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A== dependencies: - "@types/trusted-types" "^1.0.6" + dompurify "3.2.7" + marked "14.0.0" ms@^2.1.1, ms@^2.1.3: version "2.1.3" @@ -6918,7 +6926,7 @@ rehype-raw@^7.0.0: hast-util-raw "^9.0.0" vfile "^6.0.0" -relative-time-format@^1.1.7: +relative-time-format@^1.1.11: version "1.1.11" resolved "https://registry.yarnpkg.com/relative-time-format/-/relative-time-format-1.1.11.tgz#b193d5192434e7c1c6a53e362f811c68a4f18c45" integrity sha512-TH+oV/w77hjaB9xCzoFYJ/Icmr/12+02IAoCI/YGS2UBTbjCbBjHGEBxGnVy4EJvOR1qadGzyFRI6hGaJJG93Q==