From 6013168c6367799205fa42b2c8fa9575d8aa1065 Mon Sep 17 00:00:00 2001 From: Jan Matufka Date: Tue, 22 Sep 2026 13:37:29 +0200 Subject: [PATCH 1/3] Group Log Detective jobs by trigger on same line The default list display /jobs/log-detective is now using /api/log-detective/groups endpoint (with same page, per_page args). Also shift margins to leave more space for target buttons. Signed-off-by: Jan Matufka --- frontend/src/apiDefinitions.ts | 33 ++++++----- .../logdetective/LogDetectiveGroup.tsx | 3 + .../logdetective/LogDetectiveResultsTable.tsx | 59 +++++++++---------- .../logdetective/logDetectiveResults.ts | 6 +- 4 files changed, 51 insertions(+), 50 deletions(-) diff --git a/frontend/src/apiDefinitions.ts b/frontend/src/apiDefinitions.ts index 477ad46f..4d78a409 100644 --- a/frontend/src/apiDefinitions.ts +++ b/frontend/src/apiDefinitions.ts @@ -496,28 +496,31 @@ export interface OSHScan { url: string | null; } -// /api/log-detective -export interface LogDetectiveResultGroup { - packit_id: number; - analysis_id: string; +export interface LogDetectiveTarget { + id: number; + target_arch: string; status: string; - chroot: string; - commit_sha: string; - log_detective_response: LogDetectiveResponse | null; - error_msg: string | null; - target_build: string | null; - run_ids: number[]; +} + +// /api/log-detective/groups +export interface LogDetectiveQueryGroup { + packit_id: number; submitted_time: number | null; - branch_name: string | null; + run_ids: number[]; + log_detective_targets: LogDetectiveTarget[]; + commit_sha: string | null; pr_id: number | null; issue_id: number | null; + branch_name: string | null; release: string | null; - project_url: string; - repo_name: string; - repo_namespace: string; 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/groups/$id @@ -540,7 +543,6 @@ export interface LogDetectiveGroup { repo_namespace: string; } -// /api/log-detective/$id export interface LogDetectiveExplanation { text: string; } @@ -563,6 +565,7 @@ export interface LogDetectiveResponse { snippets: LogDetectiveSnippet[] | null; } +// /api/log-detective/$id export interface LogDetectiveResult { packit_id: number; analysis_id: string; diff --git a/frontend/src/components/logdetective/LogDetectiveGroup.tsx b/frontend/src/components/logdetective/LogDetectiveGroup.tsx index 87ec5a06..3b2dfa76 100644 --- a/frontend/src/components/logdetective/LogDetectiveGroup.tsx +++ b/frontend/src/components/logdetective/LogDetectiveGroup.tsx @@ -24,6 +24,9 @@ import { Preloader } from "../shared/Preloader"; import { Timestamp } from "../shared/Timestamp"; import { TriggerLink, TriggerSuffix } from "../trigger/TriggerLink"; +// Note: Even though the route is technically reachable, it is not linked from the dashboard FE. +// No one will see this, unless they request /jobs/log-detective/group/{id} directly. + export const LogDetectiveGroup = () => { const { id } = LogDetectiveGroupRoute.useParams(); diff --git a/frontend/src/components/logdetective/LogDetectiveResultsTable.tsx b/frontend/src/components/logdetective/LogDetectiveResultsTable.tsx index cbc73905..2852f77d 100644 --- a/frontend/src/components/logdetective/LogDetectiveResultsTable.tsx +++ b/frontend/src/components/logdetective/LogDetectiveResultsTable.tsx @@ -33,9 +33,8 @@ export const LogDetectiveResultsTable = () => { const columnNames = { forge: "Forge", trigger: "Trigger", - packit_id: "Packit ID", - analysisId: "Analysis ID", - target: "Target", + packitId: "Packit ID", + targets: "Targets", commitSha: "Commit SHA", timeSubmitted: "Time Submitted", }; @@ -45,25 +44,22 @@ export const LogDetectiveResultsTable = () => { ); const TableHeads = [ - + {columnNames.forge} , {columnNames.trigger} , - - {columnNames.packit_id} + + {columnNames.packitId} , - - {columnNames.analysisId} + + {columnNames.targets} , - - {columnNames.target} - , - + {columnNames.commitSha} , - + {columnNames.timeSubmitted} , ]; @@ -90,36 +86,35 @@ export const LogDetectiveResultsTable = () => { {TableHeads} - {data?.map((log_detective_result) => ( - + {data?.map((group) => ( + - + - - + + - - {log_detective_result.packit_id} - - - {log_detective_result.analysis_id} - - - + {group.packit_id} + + {group.log_detective_targets.map((target) => ( + + + + ))} - {log_detective_result.commit_sha} + {group.commit_sha?.slice(0, 7)} - + ))} diff --git a/frontend/src/queries/logdetective/logDetectiveResults.ts b/frontend/src/queries/logdetective/logDetectiveResults.ts index d0ca7bc1..9399db2f 100644 --- a/frontend/src/queries/logdetective/logDetectiveResults.ts +++ b/frontend/src/queries/logdetective/logDetectiveResults.ts @@ -1,7 +1,7 @@ // Copyright Contributors to the Packit project. // SPDX-License-Identifier: MIT -import { LogDetectiveResultGroup } from "../../apiDefinitions"; +import { LogDetectiveQueryGroup } from "../../apiDefinitions"; export interface fetchLogDetectiveResultProps { pageParam: number; @@ -14,9 +14,9 @@ export const fetchLogDetectiveResults = async ({ pageParam = 1, perPage, signal, -}: fetchLogDetectiveResultProps): Promise => { +}: fetchLogDetectiveResultProps): Promise => { const data = await fetch( - `${import.meta.env.VITE_API_URL}/log-detective?page=${pageParam}&per_page=${perPage}`, + `${import.meta.env.VITE_API_URL}/log-detective/groups?page=${pageParam}&per_page=${perPage}`, { signal }, ) .then((response) => response.json()) From 124c6672ef87107ebb568230d6b4e5b51e1dccd3 Mon Sep 17 00:00:00 2001 From: Jan Matufka Date: Tue, 22 Sep 2026 15:40:56 +0200 Subject: [PATCH 2/3] Show more info on Log Detective job overview Things like commit SHA, target arch and trigger link are displayed in list, and probably should also be displayed in the detail view. Signed-off-by: Jan Matufka --- .../logdetective/LogDetectiveResult.tsx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/frontend/src/components/logdetective/LogDetectiveResult.tsx b/frontend/src/components/logdetective/LogDetectiveResult.tsx index edfc3068..7c4cad50 100644 --- a/frontend/src/components/logdetective/LogDetectiveResult.tsx +++ b/frontend/src/components/logdetective/LogDetectiveResult.tsx @@ -23,8 +23,10 @@ import { logDetectiveResultQueryOptions } from "../../queries/logdetective/logDe import { Route as LogDetectiveRoute } from "../../routes/jobs_/log-detective.$id"; import { ErrorConnection } from "../errors/ErrorConnection"; import { Preloader } from "../shared/Preloader"; +import { SHACopy } from "../shared/SHACopy"; import { Timestamp } from "../shared/Timestamp"; import { StatusLabel } from "../statusLabels/StatusLabel"; +import { TriggerLink, TriggerSuffix } from "../trigger/TriggerLink"; // PF CodeBlock has very generous default padding; these overrides keep snippet rows compact. // The `as` cast is needed because PF CSS custom properties aren't in the CSSProperties type. @@ -173,6 +175,20 @@ export const LogDetectiveResult = () => { Log Detective Results + {data ? ( + <> + + + + + + +
+ + ) : null}
@@ -212,6 +228,12 @@ export const LogDetectiveResult = () => { {data.analysis_id} + + Target arch + + {data.chroot} + + Status From 10367598b016dfbe0be3bc4ee97d82929a3a93c6 Mon Sep 17 00:00:00 2001 From: Jan Matufka Date: Wed, 23 Sep 2026 16:51:43 +0200 Subject: [PATCH 3/3] Remove unlinked /log-detective/groups/{id} view Signed-off-by: Jan Matufka --- frontend/src/apiDefinitions.ts | 22 +-- .../logdetective/LogDetectiveGroup.tsx | 131 ------------------ .../queries/logdetective/logDetectiveGroup.ts | 28 ---- .../logdetective/logDetectiveGroupQuery.ts | 17 --- .../logdetective/logDetectiveResults.ts | 4 +- frontend/src/routeTree.gen.ts | 25 ---- .../routes/jobs_/log-detective.group.$id.tsx | 15 -- 7 files changed, 3 insertions(+), 239 deletions(-) delete mode 100644 frontend/src/components/logdetective/LogDetectiveGroup.tsx delete mode 100644 frontend/src/queries/logdetective/logDetectiveGroup.ts delete mode 100644 frontend/src/queries/logdetective/logDetectiveGroupQuery.ts delete mode 100644 frontend/src/routes/jobs_/log-detective.group.$id.tsx diff --git a/frontend/src/apiDefinitions.ts b/frontend/src/apiDefinitions.ts index 4d78a409..6ea0c5c8 100644 --- a/frontend/src/apiDefinitions.ts +++ b/frontend/src/apiDefinitions.ts @@ -503,7 +503,7 @@ export interface LogDetectiveTarget { } // /api/log-detective/groups -export interface LogDetectiveQueryGroup { +export interface LogDetectiveGroup { packit_id: number; submitted_time: number | null; run_ids: number[]; @@ -523,26 +523,6 @@ export interface LogDetectiveQueryGroup { repo_namespace: string; } -// /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; -} - export interface LogDetectiveExplanation { text: string; } diff --git a/frontend/src/components/logdetective/LogDetectiveGroup.tsx b/frontend/src/components/logdetective/LogDetectiveGroup.tsx deleted file mode 100644 index 3b2dfa76..00000000 --- a/frontend/src/components/logdetective/LogDetectiveGroup.tsx +++ /dev/null @@ -1,131 +0,0 @@ -// 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"; - -// Note: Even though the route is technically reachable, it is not linked from the dashboard FE. -// No one will see this, unless they request /jobs/log-detective/group/{id} directly. - -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/queries/logdetective/logDetectiveGroup.ts b/frontend/src/queries/logdetective/logDetectiveGroup.ts deleted file mode 100644 index f6e2660c..00000000 --- a/frontend/src/queries/logdetective/logDetectiveGroup.ts +++ /dev/null @@ -1,28 +0,0 @@ -// 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 deleted file mode 100644 index c55cd147..00000000 --- a/frontend/src/queries/logdetective/logDetectiveGroupQuery.ts +++ /dev/null @@ -1,17 +0,0 @@ -// 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/logDetectiveResults.ts b/frontend/src/queries/logdetective/logDetectiveResults.ts index 9399db2f..dc27b1ec 100644 --- a/frontend/src/queries/logdetective/logDetectiveResults.ts +++ b/frontend/src/queries/logdetective/logDetectiveResults.ts @@ -1,7 +1,7 @@ // Copyright Contributors to the Packit project. // SPDX-License-Identifier: MIT -import { LogDetectiveQueryGroup } from "../../apiDefinitions"; +import { LogDetectiveGroup } from "../../apiDefinitions"; export interface fetchLogDetectiveResultProps { pageParam: number; @@ -14,7 +14,7 @@ export const fetchLogDetectiveResults = async ({ pageParam = 1, perPage, signal, -}: fetchLogDetectiveResultProps): Promise => { +}: fetchLogDetectiveResultProps): Promise => { const data = await fetch( `${import.meta.env.VITE_API_URL}/log-detective/groups?page=${pageParam}&per_page=${perPage}`, { signal }, diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index 41d3adac..56894a9b 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -53,7 +53,6 @@ import { Route as JobsKojiDownstreamIdImport } from './routes/jobs_/koji-downstr 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 @@ -286,11 +285,6 @@ 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' { @@ -582,13 +576,6 @@ 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' @@ -691,7 +678,6 @@ export interface FileRoutesByFullPath { '/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 } @@ -736,7 +722,6 @@ export interface FileRoutesByTo { '/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 } @@ -783,7 +768,6 @@ export interface FileRoutesById { '/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 } @@ -831,7 +815,6 @@ export interface FileRouteTypes { | '/jobs/srpm/$id' | '/jobs/testing-farm/$id' | '/projects/$forge/$namespace' - | '/jobs/log-detective/group/$id' | '/projects/$forge/$namespace/$repo' fileRoutesByTo: FileRoutesByTo to: @@ -875,7 +858,6 @@ export interface FileRouteTypes { | '/jobs/srpm/$id' | '/jobs/testing-farm/$id' | '/projects/$forge/$namespace' - | '/jobs/log-detective/group/$id' | '/projects/$forge/$namespace/$repo' id: | '__root__' @@ -920,7 +902,6 @@ export interface FileRouteTypes { | '/jobs/srpm/$id' | '/jobs/testing-farm/$id' | '/projects/$forge/$namespace' - | '/jobs/log-detective/group/$id' | '/projects/$forge/$namespace/$repo' fileRoutesById: FileRoutesById } @@ -947,7 +928,6 @@ export interface RootRouteChildren { JobsSrpmIdRoute: typeof JobsSrpmIdRoute JobsTestingFarmIdRoute: typeof JobsTestingFarmIdRoute ProjectsForgeNamespaceLazyRoute: typeof ProjectsForgeNamespaceLazyRoute - JobsLogDetectiveGroupIdRoute: typeof JobsLogDetectiveGroupIdRoute ProjectsForgeNamespaceRepoRoute: typeof ProjectsForgeNamespaceRepoRoute } @@ -973,7 +953,6 @@ const rootRouteChildren: RootRouteChildren = { JobsSrpmIdRoute: JobsSrpmIdRoute, JobsTestingFarmIdRoute: JobsTestingFarmIdRoute, ProjectsForgeNamespaceLazyRoute: ProjectsForgeNamespaceLazyRoute, - JobsLogDetectiveGroupIdRoute: JobsLogDetectiveGroupIdRoute, ProjectsForgeNamespaceRepoRoute: ProjectsForgeNamespaceRepoRoute, } @@ -1010,7 +989,6 @@ export const routeTree = rootRoute "/jobs/srpm/$id", "/jobs/testing-farm/$id", "/projects/$forge/$namespace", - "/jobs/log-detective/group/$id", "/projects/$forge/$namespace/$repo" ] }, @@ -1179,9 +1157,6 @@ 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.group.$id.tsx b/frontend/src/routes/jobs_/log-detective.group.$id.tsx deleted file mode 100644 index 1ad13dc9..00000000 --- a/frontend/src/routes/jobs_/log-detective.group.$id.tsx +++ /dev/null @@ -1,15 +0,0 @@ -// 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, -});