Fix measure Id missing for evidence dialog

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-04-06 12:32:57 +04:00
parent acf5913d6c
commit 08847d9dc5
5 changed files with 40 additions and 26 deletions

View File

@@ -13,7 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE. // PERFORMANCE OF THIS SOFTWARE.
import { graphql, useRefetchableFragment } from "react-relay"; import { graphql, useRefetchableFragment } from "react-relay";
import { useOutletContext } from "react-router"; import { useOutletContext, useParams } from "react-router";
import type { MeasureControlsTabFragment$key } from "#/__generated__/core/MeasureControlsTabFragment.graphql"; import type { MeasureControlsTabFragment$key } from "#/__generated__/core/MeasureControlsTabFragment.graphql";
import { LinkedControlsCard } from "#/components/controls/LinkedControlsCard"; import { LinkedControlsCard } from "#/components/controls/LinkedControlsCard";
@@ -30,7 +30,6 @@ export const controlsFragment = graphql`
filter: { type: "ControlFilter", defaultValue: null } filter: { type: "ControlFilter", defaultValue: null }
) )
@refetchable(queryName: "MeasureControlsTabControlsQuery") { @refetchable(queryName: "MeasureControlsTabControlsQuery") {
id
canCreateControlMeasureMapping: permission( canCreateControlMeasureMapping: permission(
action: "core:control:create-measure-mapping" action: "core:control:create-measure-mapping"
) )
@@ -85,8 +84,12 @@ export const attachControlMutation = graphql`
export default function MeasureControlsTab() { export default function MeasureControlsTab() {
const { measure } = useOutletContext<{ const { measure } = useOutletContext<{
measure: MeasureControlsTabFragment$key & { id: string }; measure: MeasureControlsTabFragment$key;
}>(); }>();
const { measureId } = useParams<{ measureId: string }>();
if (!measureId) {
throw new Error("Missing :measureId param in route");
}
// eslint-disable-next-line relay/generated-typescript-types // eslint-disable-next-line relay/generated-typescript-types
const [data, refetch] = useRefetchableFragment(controlsFragment, measure); const [data, refetch] = useRefetchableFragment(controlsFragment, measure);
const connectionId = data.controls.__id; const connectionId = data.controls.__id;
@@ -97,7 +100,7 @@ export default function MeasureControlsTab() {
const readOnly = !canLinkControl && !canUnlinkControl; const readOnly = !canLinkControl && !canUnlinkControl;
const incrementOptions = { const incrementOptions = {
id: data.id, id: measureId,
node: "controls(first:0)", node: "controls(first:0)",
}; };
const [detachControl, isDetaching] = useMutationWithIncrement( const [detachControl, isDetaching] = useMutationWithIncrement(
@@ -122,7 +125,7 @@ export default function MeasureControlsTab() {
controls={controls} controls={controls}
onDetach={detachControl} onDetach={detachControl}
onAttach={attachControl} onAttach={attachControl}
params={{ measureId: data.id }} params={{ measureId }}
connectionId={connectionId} connectionId={connectionId}
refetch={refetch} refetch={refetch}
readOnly={readOnly} readOnly={readOnly}

View File

@@ -13,7 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE. // PERFORMANCE OF THIS SOFTWARE.
import { graphql, useFragment } from "react-relay"; import { graphql, useFragment } from "react-relay";
import { useOutletContext } from "react-router"; import { useOutletContext, useParams } from "react-router";
import type { MeasureDocumentsTabFragment$key } from "#/__generated__/core/MeasureDocumentsTabFragment.graphql"; import type { MeasureDocumentsTabFragment$key } from "#/__generated__/core/MeasureDocumentsTabFragment.graphql";
import { LinkedDocumentsCard } from "#/components/documents/LinkedDocumentsCard"; import { LinkedDocumentsCard } from "#/components/documents/LinkedDocumentsCard";
@@ -68,6 +68,10 @@ export const detachDocumentMutation = graphql`
`; `;
export default function MeasureDocumentsTab() { export default function MeasureDocumentsTab() {
const { measureId } = useParams<{ measureId: string }>();
if (!measureId) {
throw new Error("Missing :measureId param in route");
}
const { measure } = useOutletContext<{ const { measure } = useOutletContext<{
measure: MeasureDocumentsTabFragment$key; measure: MeasureDocumentsTabFragment$key;
}>(); }>();
@@ -108,7 +112,7 @@ export default function MeasureDocumentsTab() {
documents={documents} documents={documents}
onAttach={attachDocument} onAttach={attachDocument}
onDetach={detachDocument} onDetach={detachDocument}
params={{ measureId: data.id }} params={{ measureId }}
connectionId={connectionId} connectionId={connectionId}
readOnly={readOnly} readOnly={readOnly}
/> />

View File

@@ -60,7 +60,7 @@ export const evidencesFragment = graphql`
before: { type: "CursorKey", defaultValue: null } before: { type: "CursorKey", defaultValue: null }
last: { type: "Int", defaultValue: null } last: { type: "Int", defaultValue: null }
) { ) {
id name
canUploadEvidence: permission(action: "core:measure:upload-evidence") canUploadEvidence: permission(action: "core:measure:upload-evidence")
evidences( evidences(
first: $first first: $first
@@ -112,12 +112,16 @@ const deleteEvidenceMutation = graphql`
export default function MeasureEvidencesTab() { export default function MeasureEvidencesTab() {
const { measure } = useOutletContext<{ const { measure } = useOutletContext<{
measure: MeasureEvidencesTabFragment$key & { id: string; name: string }; measure: MeasureEvidencesTabFragment$key;
}>(); }>();
const { evidenceId, snapshotId } = useParams<{ const { measureId, evidenceId, snapshotId } = useParams<{
measureId: string;
evidenceId: string; evidenceId: string;
snapshotId?: string; snapshotId?: string;
}>(); }>();
if (!measureId) {
throw new Error("Missing :measureId param in route");
}
// eslint-disable-next-line relay/generated-typescript-types // eslint-disable-next-line relay/generated-typescript-types
const pagination = usePaginationFragment(evidencesFragment, measure); const pagination = usePaginationFragment(evidencesFragment, measure);
const connectionId = pagination.data.evidences.__id; const connectionId = pagination.data.evidences.__id;
@@ -130,7 +134,7 @@ export default function MeasureEvidencesTab() {
const dialogRef = useDialogRef(); const dialogRef = useDialogRef();
const isSnapshotMode = Boolean(snapshotId); const isSnapshotMode = Boolean(snapshotId);
usePageTitle(measure.name + " - " + __("Evidences")); usePageTitle(pagination.data.name + " - " + __("Evidences"));
return ( return (
<div className="space-y-6"> <div className="space-y-6">
@@ -149,7 +153,7 @@ export default function MeasureEvidencesTab() {
<EvidenceRow <EvidenceRow
key={evidence.id} key={evidence.id}
evidenceKey={evidence} evidenceKey={evidence}
measureId={measure.id} measureId={measureId}
organizationId={organizationId} organizationId={organizationId}
connectionId={connectionId} connectionId={connectionId}
hideActions={isSnapshotMode} hideActions={isSnapshotMode}
@@ -169,11 +173,11 @@ export default function MeasureEvidencesTab() {
</SortableTable> </SortableTable>
{evidence && ( {evidence && (
<EvidencePreviewDialog <EvidencePreviewDialog
key={evidence?.id} key={evidence.id}
onClose={() => { onClose={() => {
const baseUrl = isSnapshotMode const baseUrl = isSnapshotMode
? `/organizations/${organizationId}/snapshots/${snapshotId}/risks/measures/${measure.id}/evidences` ? `/organizations/${organizationId}/snapshots/${snapshotId}/risks/measures/${measureId}/evidences`
: `/organizations/${organizationId}/measures/${measure.id}/evidences`; : `/organizations/${organizationId}/measures/${measureId}/evidences`;
void navigate(baseUrl); void navigate(baseUrl);
}} }}
evidenceId={evidence.id} evidenceId={evidence.id}
@@ -183,7 +187,7 @@ export default function MeasureEvidencesTab() {
{!isSnapshotMode && pagination.data.canUploadEvidence && ( {!isSnapshotMode && pagination.data.canUploadEvidence && (
<CreateEvidenceDialog <CreateEvidenceDialog
ref={dialogRef} ref={dialogRef}
measureId={measure.id} measureId={measureId}
connectionId={connectionId} connectionId={connectionId}
/> />
)} )}

View File

@@ -13,7 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE. // PERFORMANCE OF THIS SOFTWARE.
import { graphql, useFragment } from "react-relay"; import { graphql, useFragment } from "react-relay";
import { useOutletContext } from "react-router"; import { useOutletContext, useParams } from "react-router";
import type { MeasureRisksTabFragment$key } from "#/__generated__/core/MeasureRisksTabFragment.graphql"; import type { MeasureRisksTabFragment$key } from "#/__generated__/core/MeasureRisksTabFragment.graphql";
import { LinkedRisksCard } from "#/components/risks/LinkedRisksCard"; import { LinkedRisksCard } from "#/components/risks/LinkedRisksCard";
@@ -68,8 +68,12 @@ export const detachRiskMutation = graphql`
`; `;
export default function MeasureRisksTab() { export default function MeasureRisksTab() {
const { measureId } = useParams<{ measureId: string }>();
if (!measureId) {
throw new Error("Missing :measureId param in route");
}
const { measure } = useOutletContext<{ const { measure } = useOutletContext<{
measure: MeasureRisksTabFragment$key & { id: string }; measure: MeasureRisksTabFragment$key;
}>(); }>();
const data = useFragment(risksFragment, measure); const data = useFragment(risksFragment, measure);
const connectionId = data.risks.__id; const connectionId = data.risks.__id;

View File

@@ -15,7 +15,7 @@
import { useTranslate } from "@probo/i18n"; import { useTranslate } from "@probo/i18n";
import { Button, IconPlusLarge } from "@probo/ui"; import { Button, IconPlusLarge } from "@probo/ui";
import { useLazyLoadQuery } from "react-relay"; import { useLazyLoadQuery } from "react-relay";
import { useOutletContext } from "react-router"; import { useParams } from "react-router";
import { graphql } from "relay-runtime"; import { graphql } from "relay-runtime";
import type { MeasureTasksTabQuery } from "#/__generated__/core/MeasureTasksTabQuery.graphql"; import type { MeasureTasksTabQuery } from "#/__generated__/core/MeasureTasksTabQuery.graphql";
@@ -48,12 +48,11 @@ const tasksQuery = graphql`
export default function MeasureTasksTab() { export default function MeasureTasksTab() {
const { __ } = useTranslate(); const { __ } = useTranslate();
const { measure } = useOutletContext<{ const { measureId } = useParams<{ measureId: string }>();
measure: { id: string }; if (!measureId) {
}>(); throw new Error("Missing :measureId param in route");
const { node } = useLazyLoadQuery<MeasureTasksTabQuery>(tasksQuery, { }
measureId: measure.id, const { node } = useLazyLoadQuery<MeasureTasksTabQuery>(tasksQuery, { measureId });
});
if (node.__typename !== "Measure") { if (node.__typename !== "Measure") {
throw new Error("invalid node type"); throw new Error("invalid node type");
} }
@@ -63,7 +62,7 @@ export default function MeasureTasksTab() {
<div className="relative"> <div className="relative">
<TasksCard connectionId={connectionId} tasks={node.tasks.edges} /> <TasksCard connectionId={connectionId} tasks={node.tasks.edges} />
{node.canCreateTask && ( {node.canCreateTask && (
<TaskFormDialog connection={connectionId} measureId={measure.id}> <TaskFormDialog connection={connectionId} measureId={measureId}>
<Button <Button
variant="secondary" variant="secondary"
icon={IconPlusLarge} icon={IconPlusLarge}