diff --git a/frontend/src/apiDefinitions.ts b/frontend/src/apiDefinitions.ts index c9dbc19d..612e6c9a 100644 --- a/frontend/src/apiDefinitions.ts +++ b/frontend/src/apiDefinitions.ts @@ -495,3 +495,71 @@ export interface OSHScan { task_id: number | null; url: string | null; } + +// /api/log-detective +export interface LogDetectiveResultGroup { + packit_id: number; + analysis_id: string; + status: string; + chroot: string; + commit_sha: string; + log_detective_response: LogDetectiveResponse | null; + target_build: string | null; + run_ids: number[]; + submitted_time: number | null; +} + +// /api/log-detective/groups/$id +export interface LogDetectiveGroup { + packit_id: number; + submitted_time: number | null; + run_ids: number[]; + log_detective_target_ids: number[]; + pr_id: number | null; + issue_id: number | null; + branch_name: string | null; + release: string | null; + anitya_version: string | null; + anitya_project_id: number | null; + anitya_project_name: string | null; + anitya_package: string | null; + non_git_upstream: boolean; + project_url: string; + repo_name: string; + repo_namespace: string; +} + +// /api/log-detective/$id +export interface LogDetectiveExplanation { + logprobs: unknown | null; + text: string; +} + +export interface LogDetectiveResponse { + explanation: LogDetectiveExplanation; + response_certainty: number; +} + +export interface LogDetectiveResult { + packit_id: number; + analysis_id: string; + branch_name: string | null; + chroot: string; + commit_sha: string; + run_ids: number[]; + status: string; + log_detective_response: LogDetectiveResponse | null; + target_build: string | null; + submitted_time: number | null; + project_url: string; + pr_id: number | null; + issue_id: number | null; + release: string | null; + anitya_version: string | null; + anitya_project_id: number | null; + anitya_project_name: string | null; + anitya_package: string | null; + non_git_upstream: boolean; + repo_name: string; + repo_namespace: string; +} diff --git a/frontend/src/components/jobs/Jobs.tsx b/frontend/src/components/jobs/Jobs.tsx index 46525a8a..5e94cb30 100644 --- a/frontend/src/components/jobs/Jobs.tsx +++ b/frontend/src/components/jobs/Jobs.tsx @@ -69,6 +69,9 @@ const Jobs = () => { Koji Tagging Requests + + Log Detective + diff --git a/frontend/src/components/logdetective/LogDetectiveGroup.tsx b/frontend/src/components/logdetective/LogDetectiveGroup.tsx new file mode 100644 index 00000000..87ec5a06 --- /dev/null +++ b/frontend/src/components/logdetective/LogDetectiveGroup.tsx @@ -0,0 +1,128 @@ +// Copyright Contributors to the Packit project. +// SPDX-License-Identifier: MIT + +import { + Card, + CardBody, + Content, + DescriptionList, + DescriptionListDescription, + DescriptionListGroup, + DescriptionListTerm, + List, + ListItem, + PageSection, + Title, +} from "@patternfly/react-core"; + +import { useQuery } from "@tanstack/react-query"; +import { Link } from "@tanstack/react-router"; +import { logDetectiveGroupQueryOptions } from "../../queries/logdetective/logDetectiveGroupQuery"; +import { Route as LogDetectiveGroupRoute } from "../../routes/jobs_/log-detective.group.$id"; +import { ErrorConnection } from "../errors/ErrorConnection"; +import { Preloader } from "../shared/Preloader"; +import { Timestamp } from "../shared/Timestamp"; +import { TriggerLink, TriggerSuffix } from "../trigger/TriggerLink"; + +export const LogDetectiveGroup = () => { + const { id } = LogDetectiveGroupRoute.useParams(); + + const { data, isError, isLoading } = useQuery( + logDetectiveGroupQueryOptions({ id }), + ); + + // If backend API is down + if (isError) { + return ; + } + + if (data && "error" in data) { + return ( + + + + + Not Found. + + + + + ); + } + + return ( + <> + + + Log Detective Group + + {data ? ( + + + + ) : ( + <>> + )} + + + + + + {!data ? ( + isLoading || data === undefined ? ( + + ) : ( + + + + + Not Found. + + + + + ) + ) : ( + + + + Submitted Time + + + + + + + Log Detective Targets + + + + {data.log_detective_target_ids.map((targetId) => ( + + + #{targetId} + + + ))} + + + + + Run IDs + + {data.run_ids.join(", ")} + + + + + )} + + + > + ); +}; diff --git a/frontend/src/components/logdetective/LogDetectiveResult.tsx b/frontend/src/components/logdetective/LogDetectiveResult.tsx new file mode 100644 index 00000000..f45fc9df --- /dev/null +++ b/frontend/src/components/logdetective/LogDetectiveResult.tsx @@ -0,0 +1,132 @@ +// Copyright Contributors to the Packit project. +// SPDX-License-Identifier: MIT + +import { + Card, + CardBody, + CodeBlock, + CodeBlockCode, + Content, + DescriptionList, + DescriptionListDescription, + DescriptionListGroup, + DescriptionListTerm, + PageSection, + Title, +} from "@patternfly/react-core"; + +import { useQuery } from "@tanstack/react-query"; +import { logDetectiveResultQueryOptions } from "../../queries/logdetective/logDetectiveResultQuery"; +import { Route as LogDetectiveRoute } from "../../routes/jobs_/log-detective.$id"; +import { ErrorConnection } from "../errors/ErrorConnection"; +import { Preloader } from "../shared/Preloader"; +import { Timestamp } from "../shared/Timestamp"; +import { StatusLabel } from "../statusLabels/StatusLabel"; + +export const LogDetectiveResult = () => { + const { id } = LogDetectiveRoute.useParams(); + + const { data, isError, isLoading } = useQuery( + logDetectiveResultQueryOptions({ id }), + ); + + // If backend API is down + if (isError) { + return ; + } + + if (data && "error" in data) { + return ( + + + + + Not Found. + + + + + ); + } + + return ( + <> + + + Log Detective Results + + + + + {!data ? ( + isLoading || data === undefined ? ( + + ) : ( + + + + + Not Found. + + + + + ) + ) : ( + <> + + + + Packit ID + + {data.packit_id} + + + + Analysis ID + + {data.analysis_id} + + + + Status + + + + + + Submitted Time + + + + + + + {data.log_detective_response?.explanation ? ( + + + + Explanation + + + + {data.log_detective_response.explanation.text} + + + + + + + ) : null} + > + )} + + + > + ); +}; diff --git a/frontend/src/components/logdetective/LogDetectiveResultsTable.tsx b/frontend/src/components/logdetective/LogDetectiveResultsTable.tsx new file mode 100644 index 00000000..962b7c3e --- /dev/null +++ b/frontend/src/components/logdetective/LogDetectiveResultsTable.tsx @@ -0,0 +1,111 @@ +// Copyright Contributors to the Packit project. +// SPDX-License-Identifier: MIT + +import { useState } from "react"; + +import { + Table, + TableVariant, + Tbody, + Td, + Th, + Thead, + Tr, +} from "@patternfly/react-table"; + +import { SkeletonTable } from "@patternfly/react-component-groups"; +import { useQuery } from "@tanstack/react-query"; +import { logDetectiveResultsQueryOptions } from "../../queries/logdetective/logDetectiveResultsQuery"; +import { ErrorConnection } from "../errors/ErrorConnection"; +import { PackitPagination } from "../shared/PackitPagination"; +import { PackitPaginationContext } from "../shared/PackitPaginationContext"; +import { Timestamp } from "../shared/Timestamp"; +import { StatusLabel } from "../statusLabels/StatusLabel"; + +export const LogDetectiveResultsTable = () => { + const [page, setPage] = useState(1); + const [perPage, setPerPage] = useState(10); + const value = { page, setPage, perPage, setPerPage }; + + // Headings + const columnNames = { + packit_id: "Packit ID", + analysisId: "Analysis ID", + target: "Target", + commitSha: "Commit SHA", + timeSubmitted: "Time Submitted", + }; + + const { isLoading, isError, data } = useQuery( + logDetectiveResultsQueryOptions(page, perPage), + ); + + const TableHeads = [ + + {columnNames.packit_id} + , + + {columnNames.analysisId} + , + + {columnNames.target} + , + + {columnNames.commitSha} + , + + {columnNames.timeSubmitted} + , + ]; + // If backend API is down + if (isError) { + return ; + } + + return ( + + + {isLoading ? ( + + ) : ( + + + {TableHeads} + + + {data?.map((log_detective_result) => ( + + + {log_detective_result.packit_id} + + + {log_detective_result.analysis_id} + + + + + + {log_detective_result.commit_sha} + + + + + + ))} + + + )} + + ); +}; diff --git a/frontend/src/components/statusLabels/StatusLabel.tsx b/frontend/src/components/statusLabels/StatusLabel.tsx index 6462491d..44bf2d19 100644 --- a/frontend/src/components/statusLabels/StatusLabel.tsx +++ b/frontend/src/components/statusLabels/StatusLabel.tsx @@ -56,6 +56,10 @@ export const StatusLabel: React.FC = (props) => { setColor("grey"); setIcon(); break; + case "complete": + setColor("blue"); + setIcon(); + break; case "unknown": setColor("grey"); setIcon(); diff --git a/frontend/src/queries/logdetective/logDetectiveGroup.ts b/frontend/src/queries/logdetective/logDetectiveGroup.ts new file mode 100644 index 00000000..f6e2660c --- /dev/null +++ b/frontend/src/queries/logdetective/logDetectiveGroup.ts @@ -0,0 +1,28 @@ +// Copyright Contributors to the Packit project. +// SPDX-License-Identifier: MIT + +import { LogDetectiveGroup } from "../../apiDefinitions"; + +export interface fetchLogDetectiveGroupProps { + id: string; + signal?: AbortSignal; +} + +// Fetch data from dashboard backend (or if we want, directly from the API) +export const fetchLogDetectiveGroup = async ({ + id, + signal, +}: fetchLogDetectiveGroupProps): Promise => { + const data = await fetch( + `${import.meta.env.VITE_API_URL}/log-detective/groups/${id}`, + { signal }, + ) + .then((response) => response.json()) + .catch((err) => { + if (err.status === 404) { + throw new Error(`Log Detective group ${id} not found!`); + } + throw err; + }); + return data; +}; diff --git a/frontend/src/queries/logdetective/logDetectiveGroupQuery.ts b/frontend/src/queries/logdetective/logDetectiveGroupQuery.ts new file mode 100644 index 00000000..c55cd147 --- /dev/null +++ b/frontend/src/queries/logdetective/logDetectiveGroupQuery.ts @@ -0,0 +1,17 @@ +// Copyright Contributors to the Packit project. +// SPDX-License-Identifier: MIT + +import { queryOptions } from "@tanstack/react-query"; +import { + fetchLogDetectiveGroup, + fetchLogDetectiveGroupProps, +} from "./logDetectiveGroup"; + +type QueryParameters = Omit; + +export const logDetectiveGroupQueryOptions = (params: QueryParameters) => + queryOptions({ + queryKey: ["log-detective-group", params], + queryFn: async ({ signal }) => + await fetchLogDetectiveGroup({ signal, ...params }), + }); diff --git a/frontend/src/queries/logdetective/logDetectiveResult.ts b/frontend/src/queries/logdetective/logDetectiveResult.ts new file mode 100644 index 00000000..3adbff29 --- /dev/null +++ b/frontend/src/queries/logdetective/logDetectiveResult.ts @@ -0,0 +1,28 @@ +// Copyright Contributors to the Packit project. +// SPDX-License-Identifier: MIT + +import { LogDetectiveResult } from "../../apiDefinitions"; + +export interface fetchLogDetectiveResultProps { + id: string; + signal?: AbortSignal; +} + +// Fetch data from dashboard backend (or if we want, directly from the API) +export const fetchLogDetectiveResult = async ({ + id, + signal, +}: fetchLogDetectiveResultProps): Promise => { + const data = await fetch( + `${import.meta.env.VITE_API_URL}/log-detective/${id}`, + { signal }, + ) + .then((response) => response.json()) + .catch((err) => { + if (err.status === 404) { + throw new Error(`Log Detective run ${id} not found!`); + } + throw err; + }); + return data; +}; diff --git a/frontend/src/queries/logdetective/logDetectiveResultQuery.ts b/frontend/src/queries/logdetective/logDetectiveResultQuery.ts new file mode 100644 index 00000000..4903f51b --- /dev/null +++ b/frontend/src/queries/logdetective/logDetectiveResultQuery.ts @@ -0,0 +1,17 @@ +// Copyright Contributors to the Packit project. +// SPDX-License-Identifier: MIT + +import { queryOptions } from "@tanstack/react-query"; +import { + fetchLogDetectiveResult, + fetchLogDetectiveResultProps, +} from "./logDetectiveResult"; + +type QueryParameters = Omit; + +export const logDetectiveResultQueryOptions = (params: QueryParameters) => + queryOptions({ + queryKey: ["log-detective-results", params], + queryFn: async ({ signal }) => + await fetchLogDetectiveResult({ signal, ...params }), + }); diff --git a/frontend/src/queries/logdetective/logDetectiveResults.ts b/frontend/src/queries/logdetective/logDetectiveResults.ts new file mode 100644 index 00000000..d0ca7bc1 --- /dev/null +++ b/frontend/src/queries/logdetective/logDetectiveResults.ts @@ -0,0 +1,30 @@ +// Copyright Contributors to the Packit project. +// SPDX-License-Identifier: MIT + +import { LogDetectiveResultGroup } from "../../apiDefinitions"; + +export interface fetchLogDetectiveResultProps { + pageParam: number; + perPage: number; + signal?: AbortSignal; +} + +// Fetch data from dashboard backend (or if we want, directly from the API) +export const fetchLogDetectiveResults = async ({ + pageParam = 1, + perPage, + signal, +}: fetchLogDetectiveResultProps): Promise => { + const data = await fetch( + `${import.meta.env.VITE_API_URL}/log-detective?page=${pageParam}&per_page=${perPage}`, + { signal }, + ) + .then((response) => response.json()) + .catch((err) => { + if (err.status === 404) { + throw new Error(`Log Detective results not found!`); + } + throw err; + }); + return data; +}; diff --git a/frontend/src/queries/logdetective/logDetectiveResultsQuery.ts b/frontend/src/queries/logdetective/logDetectiveResultsQuery.ts new file mode 100644 index 00000000..58a7d4ec --- /dev/null +++ b/frontend/src/queries/logdetective/logDetectiveResultsQuery.ts @@ -0,0 +1,15 @@ +// Copyright Contributors to the Packit project. +// SPDX-License-Identifier: MIT + +import { queryOptions } from "@tanstack/react-query"; +import { fetchLogDetectiveResults } from "./logDetectiveResults"; + +export const logDetectiveResultsQueryOptions = ( + pageParam: number, + perPage: number = 20, +) => + queryOptions({ + queryKey: ["log-detective-results", { pageParam, perPage }], + queryFn: async ({ signal }) => + await fetchLogDetectiveResults({ pageParam, perPage, signal }), + }); diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index 4bc66c47..41d3adac 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -31,6 +31,7 @@ import { Route as JobsPullFromUpstreamImport } from './routes/jobs/pull-from-ups import { Route as JobsProposeDownstreamsImport } from './routes/jobs/propose-downstreams' import { Route as JobsProposeDownstreamImport } from './routes/jobs/propose-downstream' import { Route as JobsOpenscanhubImport } from './routes/jobs/openscanhub' +import { Route as JobsLogDetectiveImport } from './routes/jobs/log-detective' import { Route as JobsKojiTagRequestsImport } from './routes/jobs/koji-tag-requests' import { Route as JobsKojiDownstreamImport } from './routes/jobs/koji-downstream' import { Route as JobsKojiBuildsImport } from './routes/jobs/koji-builds' @@ -45,12 +46,14 @@ import { Route as JobsSrpmIdImport } from './routes/jobs_/srpm.$id' import { Route as JobsPullFromUpstreamIdImport } from './routes/jobs_/pull-from-upstream.$id' import { Route as JobsProposeDownstreamIdImport } from './routes/jobs_/propose-downstream.$id' import { Route as JobsOpenscanhubIdImport } from './routes/jobs_/openscanhub.$id' +import { Route as JobsLogDetectiveIdImport } from './routes/jobs_/log-detective.$id' import { Route as JobsKojiIdImport } from './routes/jobs_/koji.$id' import { Route as JobsKojiTagRequestIdImport } from './routes/jobs_/koji-tag-request.$id' import { Route as JobsKojiDownstreamIdImport } from './routes/jobs_/koji-downstream.$id' import { Route as JobsCoprIdImport } from './routes/jobs_/copr.$id' import { Route as JobsBodhiIdImport } from './routes/jobs_/bodhi.$id' import { Route as ProjectsForgeNamespaceRepoImport } from './routes/projects/$forge.$namespace.$repo' +import { Route as JobsLogDetectiveGroupIdImport } from './routes/jobs_/log-detective.group.$id' // Create Virtual Routes @@ -160,6 +163,11 @@ const JobsOpenscanhubRoute = JobsOpenscanhubImport.update({ getParentRoute: () => JobsRoute, } as any) +const JobsLogDetectiveRoute = JobsLogDetectiveImport.update({ + path: '/log-detective', + getParentRoute: () => JobsRoute, +} as any) + const JobsKojiTagRequestsRoute = JobsKojiTagRequestsImport.update({ path: '/koji-tag-requests', getParentRoute: () => JobsRoute, @@ -239,6 +247,11 @@ const JobsOpenscanhubIdRoute = JobsOpenscanhubIdImport.update({ getParentRoute: () => rootRoute, } as any) +const JobsLogDetectiveIdRoute = JobsLogDetectiveIdImport.update({ + path: '/jobs/log-detective/$id', + getParentRoute: () => rootRoute, +} as any) + const JobsKojiIdRoute = JobsKojiIdImport.update({ path: '/jobs/koji/$id', getParentRoute: () => rootRoute, @@ -273,6 +286,11 @@ const ProjectsForgeNamespaceRepoRoute = ProjectsForgeNamespaceRepoImport.update( import('./routes/projects/$forge.$namespace.$repo.lazy').then((d) => d.Route), ) +const JobsLogDetectiveGroupIdRoute = JobsLogDetectiveGroupIdImport.update({ + path: '/jobs/log-detective/group/$id', + getParentRoute: () => rootRoute, +} as any) + // Populate the FileRoutesByPath interface declare module '@tanstack/react-router' { @@ -382,6 +400,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof JobsKojiTagRequestsImport parentRoute: typeof JobsImport } + '/jobs/log-detective': { + id: '/jobs/log-detective' + path: '/log-detective' + fullPath: '/jobs/log-detective' + preLoaderRoute: typeof JobsLogDetectiveImport + parentRoute: typeof JobsImport + } '/jobs/openscanhub': { id: '/jobs/openscanhub' path: '/openscanhub' @@ -508,6 +533,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof JobsKojiIdImport parentRoute: typeof rootRoute } + '/jobs/log-detective/$id': { + id: '/jobs/log-detective/$id' + path: '/jobs/log-detective/$id' + fullPath: '/jobs/log-detective/$id' + preLoaderRoute: typeof JobsLogDetectiveIdImport + parentRoute: typeof rootRoute + } '/jobs/openscanhub/$id': { id: '/jobs/openscanhub/$id' path: '/jobs/openscanhub/$id' @@ -550,6 +582,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ProjectsForgeNamespaceLazyImport parentRoute: typeof rootRoute } + '/jobs/log-detective/group/$id': { + id: '/jobs/log-detective/group/$id' + path: '/jobs/log-detective/group/$id' + fullPath: '/jobs/log-detective/group/$id' + preLoaderRoute: typeof JobsLogDetectiveGroupIdImport + parentRoute: typeof rootRoute + } '/projects/$forge/$namespace/$repo': { id: '/projects/$forge/$namespace/$repo' path: '/projects/$forge/$namespace/$repo' @@ -572,6 +611,7 @@ interface JobsRouteChildren { JobsKojiBuildsRoute: typeof JobsKojiBuildsRoute JobsKojiDownstreamRoute: typeof JobsKojiDownstreamRoute JobsKojiTagRequestsRoute: typeof JobsKojiTagRequestsRoute + JobsLogDetectiveRoute: typeof JobsLogDetectiveRoute JobsOpenscanhubRoute: typeof JobsOpenscanhubRoute JobsProposeDownstreamRoute: typeof JobsProposeDownstreamRoute JobsProposeDownstreamsRoute: typeof JobsProposeDownstreamsRoute @@ -594,6 +634,7 @@ const JobsRouteChildren: JobsRouteChildren = { JobsKojiBuildsRoute: JobsKojiBuildsRoute, JobsKojiDownstreamRoute: JobsKojiDownstreamRoute, JobsKojiTagRequestsRoute: JobsKojiTagRequestsRoute, + JobsLogDetectiveRoute: JobsLogDetectiveRoute, JobsOpenscanhubRoute: JobsOpenscanhubRoute, JobsProposeDownstreamRoute: JobsProposeDownstreamRoute, JobsProposeDownstreamsRoute: JobsProposeDownstreamsRoute, @@ -624,6 +665,7 @@ export interface FileRoutesByFullPath { '/jobs/koji-builds': typeof JobsKojiBuildsRoute '/jobs/koji-downstream': typeof JobsKojiDownstreamRoute '/jobs/koji-tag-requests': typeof JobsKojiTagRequestsRoute + '/jobs/log-detective': typeof JobsLogDetectiveRoute '/jobs/openscanhub': typeof JobsOpenscanhubRoute '/jobs/propose-downstream': typeof JobsProposeDownstreamRoute '/jobs/propose-downstreams': typeof JobsProposeDownstreamsRoute @@ -642,12 +684,14 @@ export interface FileRoutesByFullPath { '/jobs/koji-downstream/$id': typeof JobsKojiDownstreamIdRoute '/jobs/koji-tag-request/$id': typeof JobsKojiTagRequestIdRoute '/jobs/koji/$id': typeof JobsKojiIdRoute + '/jobs/log-detective/$id': typeof JobsLogDetectiveIdRoute '/jobs/openscanhub/$id': typeof JobsOpenscanhubIdRoute '/jobs/propose-downstream/$id': typeof JobsProposeDownstreamIdRoute '/jobs/pull-from-upstream/$id': typeof JobsPullFromUpstreamIdRoute '/jobs/srpm/$id': typeof JobsSrpmIdRoute '/jobs/testing-farm/$id': typeof JobsTestingFarmIdRoute '/projects/$forge/$namespace': typeof ProjectsForgeNamespaceLazyRoute + '/jobs/log-detective/group/$id': typeof JobsLogDetectiveGroupIdRoute '/projects/$forge/$namespace/$repo': typeof ProjectsForgeNamespaceRepoRoute } @@ -666,6 +710,7 @@ export interface FileRoutesByTo { '/jobs/koji-builds': typeof JobsKojiBuildsRoute '/jobs/koji-downstream': typeof JobsKojiDownstreamRoute '/jobs/koji-tag-requests': typeof JobsKojiTagRequestsRoute + '/jobs/log-detective': typeof JobsLogDetectiveRoute '/jobs/openscanhub': typeof JobsOpenscanhubRoute '/jobs/propose-downstream': typeof JobsProposeDownstreamRoute '/jobs/propose-downstreams': typeof JobsProposeDownstreamsRoute @@ -684,12 +729,14 @@ export interface FileRoutesByTo { '/jobs/koji-downstream/$id': typeof JobsKojiDownstreamIdRoute '/jobs/koji-tag-request/$id': typeof JobsKojiTagRequestIdRoute '/jobs/koji/$id': typeof JobsKojiIdRoute + '/jobs/log-detective/$id': typeof JobsLogDetectiveIdRoute '/jobs/openscanhub/$id': typeof JobsOpenscanhubIdRoute '/jobs/propose-downstream/$id': typeof JobsProposeDownstreamIdRoute '/jobs/pull-from-upstream/$id': typeof JobsPullFromUpstreamIdRoute '/jobs/srpm/$id': typeof JobsSrpmIdRoute '/jobs/testing-farm/$id': typeof JobsTestingFarmIdRoute '/projects/$forge/$namespace': typeof ProjectsForgeNamespaceLazyRoute + '/jobs/log-detective/group/$id': typeof JobsLogDetectiveGroupIdRoute '/projects/$forge/$namespace/$repo': typeof ProjectsForgeNamespaceRepoRoute } @@ -710,6 +757,7 @@ export interface FileRoutesById { '/jobs/koji-builds': typeof JobsKojiBuildsRoute '/jobs/koji-downstream': typeof JobsKojiDownstreamRoute '/jobs/koji-tag-requests': typeof JobsKojiTagRequestsRoute + '/jobs/log-detective': typeof JobsLogDetectiveRoute '/jobs/openscanhub': typeof JobsOpenscanhubRoute '/jobs/propose-downstream': typeof JobsProposeDownstreamRoute '/jobs/propose-downstreams': typeof JobsProposeDownstreamsRoute @@ -728,12 +776,14 @@ export interface FileRoutesById { '/jobs/koji-downstream/$id': typeof JobsKojiDownstreamIdRoute '/jobs/koji-tag-request/$id': typeof JobsKojiTagRequestIdRoute '/jobs/koji/$id': typeof JobsKojiIdRoute + '/jobs/log-detective/$id': typeof JobsLogDetectiveIdRoute '/jobs/openscanhub/$id': typeof JobsOpenscanhubIdRoute '/jobs/propose-downstream/$id': typeof JobsProposeDownstreamIdRoute '/jobs/pull-from-upstream/$id': typeof JobsPullFromUpstreamIdRoute '/jobs/srpm/$id': typeof JobsSrpmIdRoute '/jobs/testing-farm/$id': typeof JobsTestingFarmIdRoute '/projects/$forge/$namespace': typeof ProjectsForgeNamespaceLazyRoute + '/jobs/log-detective/group/$id': typeof JobsLogDetectiveGroupIdRoute '/projects/$forge/$namespace/$repo': typeof ProjectsForgeNamespaceRepoRoute } @@ -755,6 +805,7 @@ export interface FileRouteTypes { | '/jobs/koji-builds' | '/jobs/koji-downstream' | '/jobs/koji-tag-requests' + | '/jobs/log-detective' | '/jobs/openscanhub' | '/jobs/propose-downstream' | '/jobs/propose-downstreams' @@ -773,12 +824,14 @@ export interface FileRouteTypes { | '/jobs/koji-downstream/$id' | '/jobs/koji-tag-request/$id' | '/jobs/koji/$id' + | '/jobs/log-detective/$id' | '/jobs/openscanhub/$id' | '/jobs/propose-downstream/$id' | '/jobs/pull-from-upstream/$id' | '/jobs/srpm/$id' | '/jobs/testing-farm/$id' | '/projects/$forge/$namespace' + | '/jobs/log-detective/group/$id' | '/projects/$forge/$namespace/$repo' fileRoutesByTo: FileRoutesByTo to: @@ -796,6 +849,7 @@ export interface FileRouteTypes { | '/jobs/koji-builds' | '/jobs/koji-downstream' | '/jobs/koji-tag-requests' + | '/jobs/log-detective' | '/jobs/openscanhub' | '/jobs/propose-downstream' | '/jobs/propose-downstreams' @@ -814,12 +868,14 @@ export interface FileRouteTypes { | '/jobs/koji-downstream/$id' | '/jobs/koji-tag-request/$id' | '/jobs/koji/$id' + | '/jobs/log-detective/$id' | '/jobs/openscanhub/$id' | '/jobs/propose-downstream/$id' | '/jobs/pull-from-upstream/$id' | '/jobs/srpm/$id' | '/jobs/testing-farm/$id' | '/projects/$forge/$namespace' + | '/jobs/log-detective/group/$id' | '/projects/$forge/$namespace/$repo' id: | '__root__' @@ -838,6 +894,7 @@ export interface FileRouteTypes { | '/jobs/koji-builds' | '/jobs/koji-downstream' | '/jobs/koji-tag-requests' + | '/jobs/log-detective' | '/jobs/openscanhub' | '/jobs/propose-downstream' | '/jobs/propose-downstreams' @@ -856,12 +913,14 @@ export interface FileRouteTypes { | '/jobs/koji-downstream/$id' | '/jobs/koji-tag-request/$id' | '/jobs/koji/$id' + | '/jobs/log-detective/$id' | '/jobs/openscanhub/$id' | '/jobs/propose-downstream/$id' | '/jobs/pull-from-upstream/$id' | '/jobs/srpm/$id' | '/jobs/testing-farm/$id' | '/projects/$forge/$namespace' + | '/jobs/log-detective/group/$id' | '/projects/$forge/$namespace/$repo' fileRoutesById: FileRoutesById } @@ -881,12 +940,14 @@ export interface RootRouteChildren { JobsKojiDownstreamIdRoute: typeof JobsKojiDownstreamIdRoute JobsKojiTagRequestIdRoute: typeof JobsKojiTagRequestIdRoute JobsKojiIdRoute: typeof JobsKojiIdRoute + JobsLogDetectiveIdRoute: typeof JobsLogDetectiveIdRoute JobsOpenscanhubIdRoute: typeof JobsOpenscanhubIdRoute JobsProposeDownstreamIdRoute: typeof JobsProposeDownstreamIdRoute JobsPullFromUpstreamIdRoute: typeof JobsPullFromUpstreamIdRoute JobsSrpmIdRoute: typeof JobsSrpmIdRoute JobsTestingFarmIdRoute: typeof JobsTestingFarmIdRoute ProjectsForgeNamespaceLazyRoute: typeof ProjectsForgeNamespaceLazyRoute + JobsLogDetectiveGroupIdRoute: typeof JobsLogDetectiveGroupIdRoute ProjectsForgeNamespaceRepoRoute: typeof ProjectsForgeNamespaceRepoRoute } @@ -905,12 +966,14 @@ const rootRouteChildren: RootRouteChildren = { JobsKojiDownstreamIdRoute: JobsKojiDownstreamIdRoute, JobsKojiTagRequestIdRoute: JobsKojiTagRequestIdRoute, JobsKojiIdRoute: JobsKojiIdRoute, + JobsLogDetectiveIdRoute: JobsLogDetectiveIdRoute, JobsOpenscanhubIdRoute: JobsOpenscanhubIdRoute, JobsProposeDownstreamIdRoute: JobsProposeDownstreamIdRoute, JobsPullFromUpstreamIdRoute: JobsPullFromUpstreamIdRoute, JobsSrpmIdRoute: JobsSrpmIdRoute, JobsTestingFarmIdRoute: JobsTestingFarmIdRoute, ProjectsForgeNamespaceLazyRoute: ProjectsForgeNamespaceLazyRoute, + JobsLogDetectiveGroupIdRoute: JobsLogDetectiveGroupIdRoute, ProjectsForgeNamespaceRepoRoute: ProjectsForgeNamespaceRepoRoute, } @@ -940,12 +1003,14 @@ export const routeTree = rootRoute "/jobs/koji-downstream/$id", "/jobs/koji-tag-request/$id", "/jobs/koji/$id", + "/jobs/log-detective/$id", "/jobs/openscanhub/$id", "/jobs/propose-downstream/$id", "/jobs/pull-from-upstream/$id", "/jobs/srpm/$id", "/jobs/testing-farm/$id", "/projects/$forge/$namespace", + "/jobs/log-detective/group/$id", "/projects/$forge/$namespace/$repo" ] }, @@ -964,6 +1029,7 @@ export const routeTree = rootRoute "/jobs/koji-builds", "/jobs/koji-downstream", "/jobs/koji-tag-requests", + "/jobs/log-detective", "/jobs/openscanhub", "/jobs/propose-downstream", "/jobs/propose-downstreams", @@ -1024,6 +1090,10 @@ export const routeTree = rootRoute "filePath": "jobs/koji-tag-requests.tsx", "parent": "/jobs" }, + "/jobs/log-detective": { + "filePath": "jobs/log-detective.tsx", + "parent": "/jobs" + }, "/jobs/openscanhub": { "filePath": "jobs/openscanhub.tsx", "parent": "/jobs" @@ -1088,6 +1158,9 @@ export const routeTree = rootRoute "/jobs/koji/$id": { "filePath": "jobs_/koji.$id.tsx" }, + "/jobs/log-detective/$id": { + "filePath": "jobs_/log-detective.$id.tsx" + }, "/jobs/openscanhub/$id": { "filePath": "jobs_/openscanhub.$id.tsx" }, @@ -1106,6 +1179,9 @@ export const routeTree = rootRoute "/projects/$forge/$namespace": { "filePath": "projects/$forge.$namespace_.lazy.tsx" }, + "/jobs/log-detective/group/$id": { + "filePath": "jobs_/log-detective.group.$id.tsx" + }, "/projects/$forge/$namespace/$repo": { "filePath": "projects/$forge.$namespace.$repo.tsx" } diff --git a/frontend/src/routes/jobs/log-detective.tsx b/frontend/src/routes/jobs/log-detective.tsx new file mode 100644 index 00000000..c01fd5ea --- /dev/null +++ b/frontend/src/routes/jobs/log-detective.tsx @@ -0,0 +1,12 @@ +// Copyright Contributors to the Packit project. +// SPDX-License-Identifier: MIT + +import { createFileRoute } from "@tanstack/react-router"; +import { LogDetectiveResultsTable } from "../../components/logdetective/LogDetectiveResultsTable"; + +export const Route = createFileRoute("/jobs/log-detective")({ + staticData: { + title: "Log Detective results", + }, + component: () => LogDetectiveResultsTable(), +}); diff --git a/frontend/src/routes/jobs_/log-detective.$id.tsx b/frontend/src/routes/jobs_/log-detective.$id.tsx new file mode 100644 index 00000000..85a8e86a --- /dev/null +++ b/frontend/src/routes/jobs_/log-detective.$id.tsx @@ -0,0 +1,15 @@ +// Copyright Contributors to the Packit project. +// SPDX-License-Identifier: MIT + +import { createFileRoute } from "@tanstack/react-router"; +import { LogDetectiveResult } from "../../components/logdetective/LogDetectiveResult"; +import { logDetectiveResultQueryOptions } from "../../queries/logdetective/logDetectiveResultQuery"; + +export const Route = createFileRoute("/jobs/log-detective/$id")({ + staticData: { + title: "Log Detective result detail", + }, + loader: ({ context: { queryClient }, params: { id } }) => + queryClient.ensureQueryData(logDetectiveResultQueryOptions({ id })), + component: LogDetectiveResult, +}); diff --git a/frontend/src/routes/jobs_/log-detective.group.$id.tsx b/frontend/src/routes/jobs_/log-detective.group.$id.tsx new file mode 100644 index 00000000..1ad13dc9 --- /dev/null +++ b/frontend/src/routes/jobs_/log-detective.group.$id.tsx @@ -0,0 +1,15 @@ +// Copyright Contributors to the Packit project. +// SPDX-License-Identifier: MIT + +import { createFileRoute } from "@tanstack/react-router"; +import { LogDetectiveGroup } from "../../components/logdetective/LogDetectiveGroup"; +import { logDetectiveGroupQueryOptions } from "../../queries/logdetective/logDetectiveGroupQuery"; + +export const Route = createFileRoute("/jobs/log-detective/group/$id")({ + staticData: { + title: "Log Detective group detail", + }, + loader: ({ context: { queryClient }, params: { id } }) => + queryClient.ensureQueryData(logDetectiveGroupQueryOptions({ id })), + component: LogDetectiveGroup, +});