diff --git a/src/app/(loading-group)/[organizationSlug]/org-dependency-search/page.tsx b/src/app/(loading-group)/[organizationSlug]/org-dependency-search/page.tsx new file mode 100644 index 00000000..cefaa084 --- /dev/null +++ b/src/app/(loading-group)/[organizationSlug]/org-dependency-search/page.tsx @@ -0,0 +1,165 @@ +"use client"; + +import Section from "@/components/common/Section"; +import Page from "@/components/Page"; +import { Policy } from "@/types/api/api"; +import { FunctionComponent, useState } from "react"; +import { toast } from "sonner"; +import useSWR from "swr"; +import EmptyParty from "../../../../components/common/EmptyParty"; +import ListRenderer from "../../../../components/common/ListRenderer"; +import PolicyListItem from "../../../../components/common/PolicyListItem"; +import PolicyDialog from "../../../../components/PolicyDialog"; +import { Button } from "../../../../components/ui/button"; +import { fetcher } from "../../../../data-fetcher/fetcher"; +import useDecodedParams from "../../../../hooks/useDecodedParams"; +import { useOrganizationMenu } from "../../../../hooks/useOrganizationMenu"; +import { browserApiClient } from "../../../../services/devGuardApi"; + +const ComplianceIndex: FunctionComponent = () => { + const menu = useOrganizationMenu(); + const [open, setOpen] = useState(false); + const { organizationSlug } = useDecodedParams() as { + organizationSlug: string; + }; + const { + data: policies, + isLoading, + error, + mutate, + } = useSWR>( + "/organizations/" + organizationSlug + "/policies/", + fetcher, + ); + + const handleCreatePolicy = async (policy: Policy) => { + mutate( + async (prev) => { + let url = "/organizations/" + organizationSlug + "/policies/"; + + const resp = await browserApiClient(url, { + method: "POST", + body: JSON.stringify(policy), + }); + + if (!resp.ok) { + toast.error("Failed to create policy"); + return; + } + // update the policies + const newPolicy = await resp.json(); + return [newPolicy, ...(prev || [])]; + }, + { + optimisticData: (prev) => [ + { ...policy, id: Math.random().toString() }, + ...(prev || []), + ], + }, + ); + + toast.success("Policy created successfully"); + setOpen(false); + }; + + const handlePolicyUpdate = async (policy: Policy) => { + mutate( + async (prev) => { + let url = + "/organizations/" + organizationSlug + "/policies/" + policy.id; + + const resp = await browserApiClient(url, { + method: "PUT", + body: JSON.stringify(policy), + }); + + if (!resp.ok) { + toast.error("Failed to update policy"); + return; + } + + // update the policies + const newPolicy = await resp.json(); + toast.success("Policy updated successfully"); + return prev?.map((p) => (p.id === newPolicy.id ? newPolicy : p)); + }, + { + optimisticData: (prev) => + prev?.map((p) => (p.id === policy.id ? policy : p)) || [], + }, + ); + }; + + const handlePolicyDelete = async (policy: Policy) => { + mutate( + async (prev) => { + let url = + "/organizations/" + organizationSlug + "/policies/" + policy.id; + + const resp = await browserApiClient(url, { + method: "DELETE", + }); + + if (!resp.ok) { + toast.error("Failed to delete policy"); + return; + } + + // update the policies + toast.success("Policy deleted successfully"); + return prev?.filter((p) => p.id !== policy.id); + }, + { + optimisticData: (prev) => prev?.filter((p) => p.id !== policy.id) || [], + }, + ); + }; + + return ( + +
+
+
setOpen(true)}>Upload new Policy + } + > + + } + renderItem={(policy) => ( + + )} + /> +
+
+
+ +
+ ); +}; + +export default ComplianceIndex; diff --git a/src/config.ts b/src/config.ts index a3afa1f2..a467f798 100644 --- a/src/config.ts +++ b/src/config.ts @@ -47,3 +47,5 @@ export const config = { | false, errorTrackingDsn: process.env.ERROR_TRACKING_DSN || "", }; + +//Important Commit diff --git a/src/hooks/useOrganizationMenu.ts b/src/hooks/useOrganizationMenu.ts index 7f55ecb1..c5a648b0 100644 --- a/src/hooks/useOrganizationMenu.ts +++ b/src/hooks/useOrganizationMenu.ts @@ -21,6 +21,7 @@ import { useActiveOrg } from "./useActiveOrg"; import { useCurrentUser } from "./useCurrentUser"; import useDecodedParams from "./useDecodedParams"; import { useCurrentUserRole } from "./useUserRole"; +import { FolderSearch } from "lucide-react"; export const useOrganizationMenu = () => { const pathName = usePathname() || "/"; @@ -57,6 +58,13 @@ export const useOrganizationMenu = () => { currentUserRole === UserRole.Owner || currentUserRole === UserRole.Admin ) { + menu.push({ + title: "Package Search", + href: "/" + decodedOrgSlug + "/org-dependency-search", + Icon: FolderSearch, + isActive: + decodedPathName === "/" + decodedOrgSlug + "/org-dependency-search", + }); menu.push({ title: "Settings", href: "/" + decodedOrgSlug + "/settings",