Add measure-document linking

Introduce the ability to link measures to documents, following the
existing pattern used by controls and risks. This includes:

- Database migration for measures_documents join table
- Coredata MeasureDocument struct with insert/delete operations
- Document service methods for listing/counting by measure ID
- Measure service CreateDocumentMapping/DeleteDocumentMapping methods
- Cleanup of measure-document mappings on document archive
- GraphQL mutations, inputs, payloads, and Measure.documents field
- DocumentConnection.TotalCount support for measure resolver
- MCP linkMeasure/unlinkMeasure updated to support documents
- MCP listMeasureDocuments tool
- Frontend MeasureDocumentsTab with LinkedDocumentsCard integration
- Authorization actions for measure document mapping
- E2e tests for measure document mapping

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-04-03 09:38:38 +02:00
parent 8de7add203
commit ef2e99d86c
16 changed files with 903 additions and 50 deletions

View File

@@ -22,11 +22,11 @@ import { Fragment } from "react";
import { loadQuery } from "react-relay";
import { redirect } from "react-router";
import type { MeasureGraphNodeQuery } from "#/__generated__/core/MeasureGraphNodeQuery.graphql";
import type { MeasureDetailPageNodeQuery } from "#/__generated__/core/MeasureDetailPageNodeQuery.graphql";
import { LinkCardSkeleton } from "#/components/skeletons/LinkCardSkeleton";
import { PageSkeleton } from "#/components/skeletons/PageSkeleton";
import { coreEnvironment } from "#/environments";
import { measureNodeQuery } from "#/hooks/graph/MeasureGraph";
import { measureNodeQuery } from "#/pages/organizations/measures/MeasureDetailPage";
export const measureRoutes = [
{
@@ -41,7 +41,7 @@ export const measureRoutes = [
path: "measures/:measureId",
Fallback: PageSkeleton,
loader: loaderFromQueryLoader(({ measureId }) =>
loadQuery<MeasureGraphNodeQuery>(coreEnvironment, measureNodeQuery, {
loadQuery<MeasureDetailPageNodeQuery>(coreEnvironment, measureNodeQuery, {
measureId: measureId,
}),
),
@@ -89,6 +89,14 @@ export const measureRoutes = [
import("#/pages/organizations/measures/tabs/MeasureEvidencesTab"),
),
},
{
path: "documents",
Fallback: LinkCardSkeleton,
Component: lazy(
() =>
import("#/pages/organizations/measures/tabs/MeasureDocumentsTab"),
),
},
],
},
] satisfies AppRoute[];