diff --git a/src/pages/tools/community-repos/repo.js b/src/pages/tools/community-repos/repo.js index c15b610b5f3d..060915cb2cf5 100644 --- a/src/pages/tools/community-repos/repo.js +++ b/src/pages/tools/community-repos/repo.js @@ -2,7 +2,7 @@ import { useRouter } from "next/router"; import { Layout as DashboardLayout } from "/src/layouts"; import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; import { useState, useEffect } from "react"; -import { ApiPostCall } from "/src/api/ApiCall"; +import { ApiPostCall, ApiGetCall } from "/src/api/ApiCall"; import { Button, Dialog, @@ -11,11 +11,12 @@ import { DialogActions, Box, Skeleton, - Alert, } from "@mui/material"; -import { OpenInNew } from "@mui/icons-material"; +import { Grid } from "@mui/system"; import CippJSONView from "/src/components/CippFormPages/CippJSONView"; import { EyeIcon } from "@heroicons/react/24/outline"; +import { CippAutoComplete } from "/src/components/CippComponents/CippAutocomplete"; +import React from "react"; const Page = () => { const router = useRouter(); @@ -23,55 +24,166 @@ const Page = () => { const [openJsonDialog, setOpenJsonDialog] = useState(false); const [fileResults, setFileResults] = useState([]); const [jsonContent, setJsonContent] = useState({}); + const [branches, setBranches] = useState([]); + const [selectedBranch, setSelectedBranch] = useState(""); + const [selectedRepo, setSelectedRepo] = useState(name); const searchMutation = ApiPostCall({ onResult: (resp) => { + if (resp?.Results === null || resp?.Results?.[0] === null) return; setFileResults(resp?.Results || []); }, }); - const fileMutation = ApiPostCall({ + const fileQuery = ApiPostCall({ onResult: (resp) => { setJsonContent(JSON.parse(resp?.Results?.content || "{}")); }, }); + const branchQuery = ApiGetCall({ + url: "/api/ExecGitHubAction", + data: { + Action: "GetBranches", + FullName: selectedRepo, + }, + onResult: (resp) => { + const branchList = resp?.Results || []; + setBranches(branchList); + if (branchList.length === 1) { + setSelectedBranch(branchList[0].name); + fetchFileTree(branchList[0].name); + } + const mainBranch = branchList.find((branch) => ["main", "master"].includes(branch.name)); + if (mainBranch) { + setSelectedBranch(mainBranch.name); + fetchFileTree(mainBranch.name); + } + }, + queryKey: `${selectedRepo}-branches`, + waiting: selectedRepo !== "", + }); + + const fileTreeQuery = ApiGetCall({ + url: "/api/ExecGitHubAction", + data: { + Action: "GetFileTree", + FullName: selectedRepo, + Branch: selectedBranch, + }, + onResult: (resp) => { + setFileResults(resp?.Results || []); + }, + queryKey: `${selectedRepo}-${selectedBranch}-filetree`, + waiting: selectedRepo !== "" && selectedBranch !== "", + }); + + const fetchFileTree = (branch) => { + if (selectedRepo !== "" && branch !== "") { + if (!fileTreeQuery.waiting) { + fileTreeQuery.waiting = true; + } + fileTreeQuery.refetch(); + } + }; + const handleJsonView = (url) => { - fileMutation.mutate({ + fileQuery.mutate({ url: "/api/ExecGitHubAction", data: { - GetFileContents: { - Url: url, - }, + Action: "GetFileContents", + Url: url, }, }); setOpenJsonDialog(true); }; useEffect(() => { - if (name) { - searchMutation.mutate({ - url: "/api/ExecGitHubAction", - data: { - Search: { - Repository: [name], - Type: "code", - Language: "json", - }, + if (selectedRepo) { + branchQuery.refetch(); + } + }, [selectedRepo]); + + useEffect(() => { + if (selectedBranch) { + fetchFileTree(selectedBranch); + } + }, [selectedBranch]); + + const updateQueryParams = (newRepo) => { + const query = { ...router.query }; + if (query.name !== newRepo) { + query.name = newRepo; + router.replace( + { + pathname: router.pathname, + query: query, }, - }); + undefined, + { shallow: true } + ); } - }, [name]); + }; + + const MemoizedCippAutoComplete = React.memo((props) => { + return ; + }); return ( <> + + { + if (newValue.value === selectedRepo) return; + setSelectedRepo(newValue.value); + updateQueryParams(newValue.value); + }} + api={{ + url: "/api/ListCommunityRepos", + queryKey: "CommunityRepos", + dataKey: "Results", + valueField: "FullName", + labelField: "FullName", + }} + multiple={false} + label="Select Repository" + placeholder="Select Repository" + disableClearable + /> + + + setSelectedBranch(newValue.value)} + options={branches.map((branch) => ({ label: branch.name, value: branch.name }))} + multiple={false} + label="Select Branch" + placeholder="Select Branch" + disableClearable + isFetching={branchQuery.isPending} + /> + + + } data={fileResults} apiDataKey="Results" queryKey="JsonTemplates" - simpleColumns={["name", "html_url"]} + simpleColumns={["path", "html_url"]} actions={[ { label: "View Template", @@ -81,19 +193,8 @@ const Page = () => { hideBulk: true, }, ]} - isFetching={searchMutation.isPending} - refreshFunction={() => - searchMutation.mutate({ - url: "/api/ExecGitHubAction", - data: { - Search: { - Repository: [name], - Type: "code", - Language: "json", - }, - }, - }) - } + isFetching={fileTreeQuery.isFetching} + refreshFunction={() => fetchFileTree(selectedBranch)} /> { > Template Details - {fileMutation.isPending ? ( + {fileQuery.isPending ? ( diff --git a/src/pages/tools/templatelib/index.jsx b/src/pages/tools/templatelib/index.jsx index fbe16255ecc8..a58adc3e0453 100644 --- a/src/pages/tools/templatelib/index.jsx +++ b/src/pages/tools/templatelib/index.jsx @@ -1,6 +1,6 @@ import React from "react"; -import { Grid, Divider, Typography, CircularProgress, Alert, Chip } from "@mui/material"; -import { useForm } from "react-hook-form"; +import { Grid, Divider, Typography, CircularProgress, Alert, Chip, Link } from "@mui/material"; +import { useForm, useWatch } from "react-hook-form"; import { Layout as DashboardLayout } from "/src/layouts/index.js"; import CippFormPage from "/src/components/CippFormPages/CippFormPage"; import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; @@ -8,6 +8,8 @@ import { useSettings } from "/src/hooks/use-settings"; import { CippFormTenantSelector } from "../../../components/CippComponents/CippFormTenantSelector"; import { Box } from "@mui/system"; import { CippFormCondition } from "../../../components/CippComponents/CippFormCondition"; +import { ApiGetCall } from "/src/api/ApiCall"; +import NextLink from "next/link"; const TemplateLibrary = () => { const currentTenant = useSettings().currentTenant; @@ -22,6 +24,13 @@ const TemplateLibrary = () => { }, }); + const integrations = ApiGetCall({ + url: "/api/ListExtensionsConfig", + queryKey: "Integrations", + }); + + const templateRepo = useWatch({ control: formControl.control, name: "templateRepo" }); + const customDataFormatter = (values) => { const startDate = new Date(); startDate.setHours(0, 0, 0, 0); @@ -43,7 +52,7 @@ const TemplateLibrary = () => { { Enabling this feature will overwrite templates with the same name. + + {integrations.isSuccess && !integrations.data?.GitHub?.Enabled && ( + + The community repositories feature requires the GitHub Integration to be enabled. Go + to the{" "} + + GitHub Integration + {" "} + page to enable it. + + )} - + @@ -103,11 +124,37 @@ const TemplateLibrary = () => { }} formControl={formControl} multiple={false} + disabled={!integrations.data?.GitHub?.Enabled} /> - + + {templateRepo?.value && ( + + + Repository Branch + + + + )} { compareValue={"CIPP"} > - Conditional Access + + Conditional Access + { - Intune + + Intune + { compareValue={"CIPP-"} > - Template Repository files + + Template Repository files +