Skip to content
Merged
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
219 changes: 137 additions & 82 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@headlessui/react": "^2.2.9",
"@heroicons/react": "^2.2.0",
"@hookform/resolvers": "^5.2.2",
"@iarna/toml": "^2.2.5",
"@mdxeditor/editor": "^3.52.4",
"@ory/elements-react": "^1.1.0",
"@ory/nextjs": "^1.0.0-rc.1",
Expand Down Expand Up @@ -49,6 +50,7 @@
"@tanstack/react-table": "^8.21.3",
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3",
"@uiw/codemirror-theme-vscode": "^4.25.8",
"@xyflow/react": "^12.6.4",
"autoprefixer": "10.4.27",
"axios": "^1.13.6",
Expand Down Expand Up @@ -99,6 +101,7 @@
"@testing-library/jest-dom": "^6.6.4",
"@testing-library/react": "^16.3.2",
"@types/jest": "^29.5.14",
"@types/js-yaml": "^4.0.9",
"@types/lodash": "^4.17.24",
"@types/node": "^25.5.0",
"@types/node-notifier": "^8.0.5",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2026 L3montree GmbH and the DevGuard Contributors.
// SPDX-License-Identifier: AGPL-3.0-or-later

"use client";

import ConfigFileEditor from "@/components/common/ConfigFileEditor";
import Page from "@/components/Page";
import AssetTitle from "@/components/common/AssetTitle";
import { useActiveAsset } from "@/hooks/useActiveAsset";
import { useActiveOrg } from "@/hooks/useActiveOrg";
import { useActiveProject } from "@/hooks/useActiveProject";
import { useAssetMenu } from "@/hooks/useAssetMenu";

const Config = () => {
const org = useActiveOrg();
const project = useActiveProject();
const asset = useActiveAsset();
const assetMenu = useAssetMenu();

const baseUrl =
org && project && asset
? "/organizations/" +
org.slug +
"/projects/" +
project.slug +
"/assets/" +
asset.slug
: null;

return (
<Page
breadcrumbs={[
{
title: "Settings",
href: "./",
},
{
title: "Config",
href: "",
},
]}
title={asset?.name || ""}
Menu={assetMenu}
Title={<AssetTitle />}
>
<ConfigFileEditor baseUrl={baseUrl} />
</Page>
);
};

export default Config;
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import {
} from "../../../../../../../../components/ui/collapsible";
import DateString from "../../../../../../../../components/common/DateString";
import Section from "@/components/common/Section";
import { Card } from "@/components/ui/card";
import Link from "next/link";

const firstOrUndefined = (el?: number[]): number | undefined => {
if (!el) {
Expand Down Expand Up @@ -367,6 +369,30 @@ const Index: FunctionComponent = () => {
</div>
</Section>
<hr />
<Section
id="config-files"
title="Configuration Files"
description="View and edit configuration files for this repository, including scanner tool settings. These configurations override project-level settings for this specific repository."
>
<Card className="p-6">
<div className="flex justify-end">
<Link
href={
"/" +
activeOrg.slug +
"/projects/" +
project!.slug +
"/assets/" +
asset.slug +
"/settings/config"
}
>
<Button variant={"outline"}>Go to Configuration Files</Button>
</Link>
</div>
</Card>
</Section>
<hr />
</div>

<DangerZone>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2026 L3montree GmbH and the DevGuard Contributors.
// SPDX-License-Identifier: AGPL-3.0-or-later

"use client";

import ConfigFileEditor from "@/components/common/ConfigFileEditor";
import Page from "@/components/Page";
import ProjectTitle from "@/components/common/ProjectTitle";
import { useActiveOrg } from "@/hooks/useActiveOrg";
import { useActiveProject } from "@/hooks/useActiveProject";
import { useProjectMenu } from "@/hooks/useProjectMenu";

const Config = () => {
const org = useActiveOrg();
const project = useActiveProject();
const projectMenu = useProjectMenu();

const baseUrl =
org && project
? "/organizations/" + org.slug + "/projects/" + project.slug
: null;

return (
<Page
breadcrumbs={[
{
title: "Settings",
href: "./",
},
{
title: "Config",
href: "",
},
]}
title={""}
Menu={projectMenu}
Title={<ProjectTitle />}
>
<ConfigFileEditor baseUrl={baseUrl} />
</Page>
);
};

export default Config;
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import {

import { ProjectForm } from "@/components/project/ProjectForm";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { useActiveProject } from "@/hooks/useActiveProject";
import Link from "next/link";

import ListItem from "@/components/common/ListItem";
import { WebhookIntegrationDialog } from "@/components/common/WebhookIntegrationDialog";
Expand Down Expand Up @@ -281,6 +283,34 @@ const Index: FunctionComponent = () => {
<hr />
</>
)}
<hr />
<Section
id="config-files"
title="Configuration Files"
description="View and edit configuration files for this project, including scanner tool settings. These configurations override organization-level settings and are inherited by all repositories in this project."
>
<Card className="p-6">
<div className="flex justify-end">
{project?.slug ? (
<Link
href={
"/" +
activeOrg.slug +
"/projects/" +
project.slug +
"/settings/config"
}
>
<Button variant={"outline"}>Go to Configuration Files</Button>
</Link>
) : (
<Button variant={"outline"} disabled>
Go to Configuration Files
</Button>
)}
</div>
</Card>
</Section>
<FormProvider {...form}>
<form onSubmit={form.handleSubmit(handleUpdate)}>
<ProjectForm
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2026 L3montree GmbH and the DevGuard Contributors.
// SPDX-License-Identifier: AGPL-3.0-or-later

"use client";

import ConfigFileEditor from "@/components/common/ConfigFileEditor";
import Page from "@/components/Page";
import { useActiveOrg } from "@/hooks/useActiveOrg";
import { useOrganizationMenu } from "@/hooks/useOrganizationMenu";

const Config = () => {
const org = useActiveOrg();
const orgMenu = useOrganizationMenu();

const baseUrl = org ? "/organizations/" + org.slug : null;

return (
<Page
breadcrumbs={[
{
title: "Settings",
href: "./",
},
{
title: "Config",
href: "",
},
]}
Title={null}
title={""}
Menu={orgMenu}
>
<ConfigFileEditor baseUrl={baseUrl} />
</Page>
);
};

export default Config;
14 changes: 14 additions & 0 deletions src/app/(loading-group)/[organizationSlug]/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,20 @@ const Home = () => {
</FormProvider>
</div>
<hr />
<Section
id="config-files"
title="Configuration Files"
description="View and edit configuration files for your organization, including scanner tool settings. These configurations are inherited by all projects and repositories in your organization and can be overridden at the project or repository level."
>
<Card className="p-6">
<div className="flex justify-end">
<Link href={"/" + activeOrg.slug + "/settings/config"}>
<Button variant={"outline"}>Go to Configuration Files</Button>
</Link>
</div>
</Card>
</Section>
<hr />
<FormProvider {...form}>
<form onSubmit={form.handleSubmit(handleUpdate)}>
<Section
Expand Down
43 changes: 42 additions & 1 deletion src/components/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ import { documentationLinks } from "@/const/documentationLinks";

import { useConfig } from "../context/ConfigContext";
import EntityProviderBanner from "./common/EntityProviderBanner";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "./ui/breadcrumb";

interface Props {
title: string;
Expand All @@ -34,9 +42,17 @@ interface Props {
isActive?: boolean;
}>;
fullscreen?: boolean;
breadcrumbs?: Array<{
title: string;
href: string;
}>;
}

const Main: FunctionComponent<Props> = ({ children, fullscreen }) => {
const Main: FunctionComponent<Props> = ({
children,
fullscreen,
breadcrumbs,
}) => {
const dimensions = useDimensions();
const themeConfig = useConfig();

Expand All @@ -49,6 +65,31 @@ const Main: FunctionComponent<Props> = ({ children, fullscreen }) => {
!fullscreen && "mx-auto max-w-screen-xl gap-4 px-6 pb-8 pt-6 lg:px-8",
)}
>
{breadcrumbs && (
<Breadcrumb className="mb-4">
<BreadcrumbList>
{breadcrumbs.map((breadcrumb, index) => (
<React.Fragment key={index}>
<BreadcrumbItem>
{index + 1 == breadcrumbs.length ? (
<BreadcrumbPage className="font-medium">
{breadcrumb.title}
</BreadcrumbPage>
) : (
<BreadcrumbLink
className="text-muted-foreground! font-medium"
href={breadcrumb.href}
>
{breadcrumb.title}
</BreadcrumbLink>
)}
</BreadcrumbItem>
{index < breadcrumbs.length - 1 && <BreadcrumbSeparator />}
</React.Fragment>
))}
</BreadcrumbList>
</Breadcrumb>
)}
{children}
</div>
<div className="bg-footer">
Expand Down
5 changes: 5 additions & 0 deletions src/components/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ type PageProps = {
Icon: any;
}>;
fullscreen?: boolean;
breadcrumbs?: Array<{
title: string;
href: string;
}>;
};

// Add that the navigation is a prop
Expand All @@ -45,6 +49,7 @@ const Page = (props: PropsWithChildren<PageProps>) => {
<div>
<div className={classNames(props.Sidebar ? "lg:pr-80" : "")}>
<Main
breadcrumbs={props.breadcrumbs}
fullscreen={props.fullscreen}
Menu={props.Menu}
Button={props.Button}
Expand Down
Loading
Loading