Fix processing activities & risks permissions pages handling
Signed-off-by: Émile Ré <nemile.re@gmail.com>
This commit is contained in:
@@ -64,8 +64,7 @@ import type {
|
||||
ProcessingActivitiesPageTIAFragment$key,
|
||||
ProcessingActivitiesPageTIAFragment$data,
|
||||
} from "/__generated__/core/ProcessingActivitiesPageTIAFragment.graphql";
|
||||
import { PermissionsContext } from "/providers/PermissionsContext";
|
||||
import { use, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import type { ProcessingActivityGraphListQuery } from "/__generated__/core/ProcessingActivityGraphListQuery.graphql";
|
||||
|
||||
interface ProcessingActivitiesPageProps {
|
||||
@@ -106,6 +105,8 @@ const processingActivitiesPageFragment = graphql`
|
||||
internationalTransfers
|
||||
createdAt
|
||||
updatedAt
|
||||
canUpdate: permission(action: "core:processing-activity:update")
|
||||
canDelete: permission(action: "core:processing-activity:delete")
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
@@ -237,7 +238,6 @@ export default function ProcessingActivitiesPage({
|
||||
const organizationId = useOrganizationId();
|
||||
const { snapshotId } = useParams<{ snapshotId?: string }>();
|
||||
const isSnapshotMode = Boolean(snapshotId);
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
const [activeTab, setActiveTab] = useState<"activities" | "dpia" | "tia">(
|
||||
"activities",
|
||||
);
|
||||
@@ -292,8 +292,7 @@ export default function ProcessingActivitiesPage({
|
||||
|
||||
const hasAnyAction =
|
||||
!isSnapshotMode &&
|
||||
(isAuthorized("ProcessingActivity", "updateProcessingActivity") ||
|
||||
isAuthorized("ProcessingActivity", "deleteProcessingActivity"));
|
||||
activities.some(({ canUpdate, canDelete }) => canUpdate || canDelete);
|
||||
|
||||
const canExportPDF = isAuthorized(
|
||||
"ProcessingActivity",
|
||||
@@ -467,7 +466,7 @@ export default function ProcessingActivitiesPage({
|
||||
)}
|
||||
{!isSnapshotMode &&
|
||||
activeTab === "activities" &&
|
||||
isAuthorized("Organization", "createProcessingActivity") && (
|
||||
organization.node.canCreateProcessingActivity && (
|
||||
<CreateProcessingActivityDialog
|
||||
organizationId={organizationId}
|
||||
connectionId={connectionId}
|
||||
@@ -677,7 +676,6 @@ function ActivityRow({
|
||||
const isSnapshotMode = Boolean(snapshotId);
|
||||
const [deleteActivity] = useMutation(deleteProcessingActivityMutation);
|
||||
const confirm = useConfirm();
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
|
||||
const handleDelete = () => {
|
||||
confirm(
|
||||
@@ -729,7 +727,7 @@ function ActivityRow({
|
||||
{hasAnyAction && (
|
||||
<Td noLink width={50} className="text-end">
|
||||
<ActionDropdown>
|
||||
{isAuthorized("ProcessingActivity", "deleteProcessingActivity") && (
|
||||
{activity.canDelete && (
|
||||
<DropdownItem
|
||||
icon={IconTrashCan}
|
||||
variant="danger"
|
||||
|
||||
@@ -58,8 +58,7 @@ import {
|
||||
} from "../../../components/form/ProcessingActivityEnumOptions";
|
||||
|
||||
import type { ProcessingActivityGraphNodeQuery } from "/__generated__/core/ProcessingActivityGraphNodeQuery.graphql";
|
||||
import { use, useState, useEffect } from "react";
|
||||
import { PermissionsContext } from "/providers/PermissionsContext";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
const updateProcessingActivitySchema = z.object({
|
||||
name: z.string().min(1, "Name is required"),
|
||||
@@ -99,17 +98,16 @@ type Props = {
|
||||
};
|
||||
|
||||
export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
const data = usePreloadedQuery<ProcessingActivityGraphNodeQuery>(
|
||||
processingActivityNodeQuery,
|
||||
props.queryRef,
|
||||
);
|
||||
const activity = data.node;
|
||||
const { node: activity } =
|
||||
usePreloadedQuery<ProcessingActivityGraphNodeQuery>(
|
||||
processingActivityNodeQuery,
|
||||
props.queryRef,
|
||||
);
|
||||
const { __ } = useTranslate();
|
||||
const { toast } = useToast();
|
||||
const organizationId = useOrganizationId();
|
||||
const { snapshotId } = useParams<{ snapshotId?: string }>();
|
||||
const isSnapshotMode = Boolean(snapshotId);
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
|
||||
// Get initial tab from URL hash
|
||||
const getInitialTab = (): "overview" | "dpia" | "tia" => {
|
||||
@@ -132,6 +130,13 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
const [dpiaDeleted, setDpiaDeleted] = useState(false);
|
||||
const [tiaDeleted, setTiaDeleted] = useState(false);
|
||||
|
||||
const canCreateOrUpdateDPIA = activity.dataProtectionImpactAssessment
|
||||
? activity.dataProtectionImpactAssessment.canUpdate
|
||||
: activity.canCreateDPIA;
|
||||
const canCreateOrUpdateTIA = activity.transferImpactAssessment
|
||||
? activity.transferImpactAssessment.canUpdate
|
||||
: activity.canCreateTIA;
|
||||
|
||||
useEffect(() => {
|
||||
window.location.hash = activeTab === "overview" ? "" : activeTab;
|
||||
}, [activeTab]);
|
||||
@@ -422,14 +427,13 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
{ label: activity.name! },
|
||||
]}
|
||||
/>
|
||||
{!isSnapshotMode &&
|
||||
isAuthorized("ProcessingActivity", "deleteProcessingActivity") && (
|
||||
<ActionDropdown>
|
||||
<DropdownItem onClick={deleteActivity} variant="danger">
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ActionDropdown>
|
||||
)}
|
||||
{!isSnapshotMode && activity.canDelete && (
|
||||
<ActionDropdown>
|
||||
<DropdownItem onClick={deleteActivity} variant="danger">
|
||||
{__("Delete")}
|
||||
</DropdownItem>
|
||||
</ActionDropdown>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
@@ -468,7 +472,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
{...register("name")}
|
||||
error={formState.errors.name?.message}
|
||||
required
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
/>
|
||||
|
||||
<div>
|
||||
@@ -483,7 +487,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
className="w-full"
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
>
|
||||
<RoleOptions />
|
||||
</Select>
|
||||
@@ -502,7 +506,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
{...register("purpose")}
|
||||
placeholder={__("Describe the purpose of processing")}
|
||||
rows={3}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -510,14 +514,14 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
label={__("Data Subject Category")}
|
||||
{...register("dataSubjectCategory")}
|
||||
placeholder={__("e.g., employees, customers, prospects")}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
/>
|
||||
|
||||
<Field
|
||||
label={__("Personal Data Category")}
|
||||
{...register("personalDataCategory")}
|
||||
placeholder={__("e.g., contact details, financial data")}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
/>
|
||||
|
||||
<div>
|
||||
@@ -536,7 +540,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
className="w-full"
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
>
|
||||
<SpecialOrCriminalDataOptions />
|
||||
</Select>
|
||||
@@ -553,7 +557,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
label={__("Consent Evidence Link")}
|
||||
{...register("consentEvidenceLink")}
|
||||
placeholder={__("Link to consent evidence if applicable")}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
/>
|
||||
|
||||
<div>
|
||||
@@ -568,7 +572,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
className="w-full"
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
>
|
||||
<LawfulBasisOptions />
|
||||
</Select>
|
||||
@@ -589,7 +593,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
id="lastReviewDate"
|
||||
type="date"
|
||||
{...register("lastReviewDate")}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -601,7 +605,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
id="nextReviewDate"
|
||||
type="date"
|
||||
{...register("nextReviewDate")}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -610,7 +614,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
control={control}
|
||||
name="dataProtectionOfficerId"
|
||||
label={__("Data Protection Officer")}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -619,14 +623,14 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
label={__("Recipients")}
|
||||
{...register("recipients")}
|
||||
placeholder={__("Who receives the data")}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
/>
|
||||
|
||||
<Field
|
||||
label={__("Location")}
|
||||
{...register("location")}
|
||||
placeholder={__("Where is the data processed")}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
@@ -639,7 +643,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
<Checkbox
|
||||
checked={field.value ?? false}
|
||||
onChange={field.onChange}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
/>
|
||||
<span>
|
||||
{__("Data is transferred internationally")}
|
||||
@@ -663,7 +667,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
className="w-full"
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
>
|
||||
<TransferSafeguardsOptions />
|
||||
</Select>
|
||||
@@ -680,7 +684,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
label={__("Retention Period")}
|
||||
{...register("retentionPeriod")}
|
||||
placeholder={__("How long is data retained")}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
/>
|
||||
|
||||
<div>
|
||||
@@ -689,7 +693,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
{...register("securityMeasures")}
|
||||
placeholder={__("Technical and organizational measures")}
|
||||
rows={3}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -707,7 +711,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
className="w-full"
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
>
|
||||
<DataProtectionImpactAssessmentOptions />
|
||||
</Select>
|
||||
@@ -737,7 +741,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
className="w-full"
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
>
|
||||
<TransferImpactAssessmentOptions />
|
||||
</Select>
|
||||
@@ -761,15 +765,12 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
name="vendorIds"
|
||||
selectedVendors={vendors}
|
||||
label={__("Vendors")}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !activity.canUpdate}
|
||||
/>
|
||||
|
||||
{!isSnapshotMode && (
|
||||
<div className="flex justify-end pt-4">
|
||||
{isAuthorized(
|
||||
"ProcessingActivity",
|
||||
"updateProcessingActivity",
|
||||
) && (
|
||||
{activity.canUpdate && (
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
@@ -796,18 +797,14 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
<h2 className="text-xl font-semibold mb-6 text-center">
|
||||
{__("Data Protection Impact Assessment")}
|
||||
</h2>
|
||||
{!isSnapshotMode &&
|
||||
isAuthorized(
|
||||
"ProcessingActivity",
|
||||
"createDataProtectionImpactAssessment",
|
||||
) && (
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => setShowDpiaForm(true)}
|
||||
>
|
||||
{__("Create DPIA")}
|
||||
</Button>
|
||||
)}
|
||||
{!isSnapshotMode && activity.canCreateDPIA && (
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => setShowDpiaForm(true)}
|
||||
>
|
||||
{__("Create DPIA")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
@@ -818,10 +815,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
{!isSnapshotMode &&
|
||||
activity?.dataProtectionImpactAssessment?.id &&
|
||||
!dpiaDeleted &&
|
||||
isAuthorized(
|
||||
"DataProtectionImpactAssessment",
|
||||
"deleteDataProtectionImpactAssessment",
|
||||
) && (
|
||||
activity.dataProtectionImpactAssessment.canDelete && (
|
||||
<Button variant="danger" onClick={deleteDPIA}>
|
||||
{__("Delete DPIA")}
|
||||
</Button>
|
||||
@@ -840,7 +834,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
"Describe the processing activity and its purpose",
|
||||
)}
|
||||
rows={4}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !canCreateOrUpdateDPIA}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -855,7 +849,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
"Explain why the processing is necessary and proportionate",
|
||||
)}
|
||||
rows={4}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !canCreateOrUpdateDPIA}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -870,7 +864,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
"Describe the potential risks to data subjects",
|
||||
)}
|
||||
rows={4}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !canCreateOrUpdateDPIA}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -885,7 +879,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
"Describe measures to mitigate the identified risks",
|
||||
)}
|
||||
rows={4}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !canCreateOrUpdateDPIA}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -903,7 +897,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
className="w-full"
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !canCreateOrUpdateDPIA}
|
||||
>
|
||||
<Option value="LOW">{__("Low")}</Option>
|
||||
<Option value="MEDIUM">{__("Medium")}</Option>
|
||||
@@ -927,14 +921,8 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
)}
|
||||
{(activity?.dataProtectionImpactAssessment?.id &&
|
||||
!dpiaDeleted
|
||||
? isAuthorized(
|
||||
"DataProtectionImpactAssessment",
|
||||
"updateDataProtectionImpactAssessment",
|
||||
)
|
||||
: isAuthorized(
|
||||
"ProcessingActivity",
|
||||
"createDataProtectionImpactAssessment",
|
||||
)) && (
|
||||
? activity.dataProtectionImpactAssessment.canUpdate
|
||||
: activity.canCreateDPIA) && (
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
@@ -966,18 +954,14 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
<h2 className="text-xl font-semibold mb-6 text-center">
|
||||
{__("Transfer Impact Assessment")}
|
||||
</h2>
|
||||
{!isSnapshotMode &&
|
||||
isAuthorized(
|
||||
"ProcessingActivity",
|
||||
"createTransferImpactAssessment",
|
||||
) && (
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => setShowTiaForm(true)}
|
||||
>
|
||||
{__("Create TIA")}
|
||||
</Button>
|
||||
)}
|
||||
{!isSnapshotMode && activity.canCreateTIA && (
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => setShowTiaForm(true)}
|
||||
>
|
||||
{__("Create TIA")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
@@ -988,10 +972,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
{!isSnapshotMode &&
|
||||
activity?.transferImpactAssessment?.id &&
|
||||
!tiaDeleted &&
|
||||
isAuthorized(
|
||||
"TransferImpactAssessment",
|
||||
"deleteTransferImpactAssessment",
|
||||
) && (
|
||||
activity.transferImpactAssessment.canDelete && (
|
||||
<Button variant="danger" onClick={deleteTIA}>
|
||||
{__("Delete TIA")}
|
||||
</Button>
|
||||
@@ -1010,7 +991,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
"Describe the data subjects involved in the transfer",
|
||||
)}
|
||||
rows={4}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !canCreateOrUpdateTIA}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1025,7 +1006,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
"Describe the legal mechanism for the transfer (e.g., SCCs, BCRs, adequacy decision)",
|
||||
)}
|
||||
rows={4}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !canCreateOrUpdateTIA}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1038,7 +1019,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
"Describe the nature and details of the data transfer",
|
||||
)}
|
||||
rows={4}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !canCreateOrUpdateTIA}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1053,7 +1034,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
"Assess the risks related to the local laws of the destination country",
|
||||
)}
|
||||
rows={4}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !canCreateOrUpdateTIA}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1068,7 +1049,7 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
"Describe any supplementary measures taken to ensure adequate protection",
|
||||
)}
|
||||
rows={4}
|
||||
disabled={isSnapshotMode}
|
||||
disabled={isSnapshotMode || !canCreateOrUpdateTIA}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1085,14 +1066,8 @@ export default function ProcessingActivityDetailsPage(props: Props) {
|
||||
</Button>
|
||||
)}
|
||||
{(activity?.transferImpactAssessment?.id && !tiaDeleted
|
||||
? isAuthorized(
|
||||
"TransferImpactAssessment",
|
||||
"updateTransferImpactAssessment",
|
||||
)
|
||||
: isAuthorized(
|
||||
"ProcessingActivity",
|
||||
"createTransferImpactAssessment",
|
||||
)) && (
|
||||
? activity.transferImpactAssessment.canUpdate
|
||||
: activity.canCreateTIA) && (
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
|
||||
@@ -34,8 +34,6 @@ import {
|
||||
} from "/hooks/graph/RiskGraph";
|
||||
import type { RiskGraphNodeQuery } from "/__generated__/core/RiskGraphNodeQuery.graphql";
|
||||
import { SnapshotBanner } from "/components/SnapshotBanner";
|
||||
import { use } from "react";
|
||||
import { PermissionsContext } from "/providers/PermissionsContext";
|
||||
|
||||
type Props = {
|
||||
queryRef: PreloadedQuery<RiskGraphNodeQuery>;
|
||||
@@ -49,18 +47,16 @@ export default function RiskDetailPage(props: Props) {
|
||||
const organizationId = useOrganizationId();
|
||||
const navigate = useNavigate();
|
||||
const isSnapshotMode = Boolean(snapshotId);
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
|
||||
if (!riskId) {
|
||||
throw new Error("Cannot load risk detail page without riskId parameter");
|
||||
}
|
||||
|
||||
const { __ } = useTranslate();
|
||||
const data = usePreloadedQuery<RiskGraphNodeQuery>(
|
||||
const { node: risk } = usePreloadedQuery<RiskGraphNodeQuery>(
|
||||
riskNodeQuery,
|
||||
props.queryRef,
|
||||
);
|
||||
const risk = data.node;
|
||||
|
||||
validateSnapshotConsistency(risk, snapshotId);
|
||||
const [deleteRisk] = useDeleteRiskMutation();
|
||||
@@ -136,7 +132,7 @@ export default function RiskDetailPage(props: Props) {
|
||||
/>
|
||||
{!isSnapshotMode && (
|
||||
<div className="flex gap-2">
|
||||
{isAuthorized("Risk", "updateRisk") && (
|
||||
{risk.canUpdate && (
|
||||
<FormRiskDialog
|
||||
trigger={
|
||||
<Button icon={IconPencil} variant="secondary">
|
||||
@@ -146,7 +142,7 @@ export default function RiskDetailPage(props: Props) {
|
||||
risk={{ id: riskId, ...risk }}
|
||||
/>
|
||||
)}
|
||||
{isAuthorized("Risk", "deleteRisk") && (
|
||||
{risk.canDelete && (
|
||||
<ActionDropdown variant="secondary">
|
||||
<DropdownItem
|
||||
variant="danger"
|
||||
|
||||
@@ -29,8 +29,6 @@ import type { RiskGraphListQuery } from "/__generated__/core/RiskGraphListQuery.
|
||||
import type { RiskGraphFragment$data } from "/__generated__/core/RiskGraphFragment.graphql";
|
||||
import { useParams } from "react-router";
|
||||
import { SnapshotBanner } from "/components/SnapshotBanner";
|
||||
import { use } from "react";
|
||||
import { PermissionsContext } from "/providers/PermissionsContext";
|
||||
|
||||
type Props = {
|
||||
queryRef: PreloadedQuery<RiskGraphListQuery>;
|
||||
@@ -41,9 +39,13 @@ export default function RisksPage(props: Props) {
|
||||
const organizationId = useOrganizationId();
|
||||
const { snapshotId } = useParams<{ snapshotId?: string }>();
|
||||
const isSnapshotMode = Boolean(snapshotId);
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
|
||||
const { connectionId, risks, ...pagination } = useRisksQuery(props.queryRef);
|
||||
const {
|
||||
data: { canCreateRisk },
|
||||
connectionId,
|
||||
risks,
|
||||
...pagination
|
||||
} = useRisksQuery(props.queryRef);
|
||||
|
||||
const refetch = ({
|
||||
order,
|
||||
@@ -73,7 +75,7 @@ export default function RisksPage(props: Props) {
|
||||
|
||||
const hasAnyAction =
|
||||
!isSnapshotMode &&
|
||||
(isAuthorized("Risk", "updateRisk") || isAuthorized("Risk", "deleteRisk"));
|
||||
risks.some(({ canDelete, canUpdate }) => canUpdate || canDelete);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -84,7 +86,7 @@ export default function RisksPage(props: Props) {
|
||||
"Risks are potential threats to your organization. Manage them by identifying, assessing, and implementing mitigation measures.",
|
||||
)}
|
||||
>
|
||||
{!isSnapshotMode && isAuthorized("Organization", "createRisk") && (
|
||||
{!isSnapshotMode && canCreateRisk && (
|
||||
<FormRiskDialog
|
||||
connection={connectionId}
|
||||
onSuccess={() => {
|
||||
@@ -153,7 +155,6 @@ function RiskRow(props: RowProps) {
|
||||
const isSnapshotMode = Boolean(snapshotId);
|
||||
const [deleteRisk] = useDeleteRiskMutation();
|
||||
const confirm = useConfirm();
|
||||
const { isAuthorized } = use(PermissionsContext);
|
||||
const onDelete = () => {
|
||||
confirm(
|
||||
() =>
|
||||
@@ -206,7 +207,7 @@ function RiskRow(props: RowProps) {
|
||||
{props.hasAnyAction && (
|
||||
<Td noLink className="text-end">
|
||||
<ActionDropdown>
|
||||
{isAuthorized("Risk", "updateRisk") && (
|
||||
{risk.canUpdate && (
|
||||
<DropdownItem
|
||||
icon={IconPencil}
|
||||
onClick={() => formDialogRef.current?.open()}
|
||||
@@ -215,7 +216,7 @@ function RiskRow(props: RowProps) {
|
||||
</DropdownItem>
|
||||
)}
|
||||
|
||||
{isAuthorized("Risk", "deleteRisk") && (
|
||||
{risk.canDelete && (
|
||||
<DropdownItem
|
||||
variant="danger"
|
||||
icon={IconTrashCan}
|
||||
|
||||
Reference in New Issue
Block a user