"use client"; import { ExternalLink, Info } from "lucide-react"; import { type FormEvent, useRef, useState } from "react"; import { ProviderTypeIcon } from "@/components/icons/providers-badge/provider-type-icon"; import { Alert, AlertDescription, Badge, Button, Textarea, useToast, } from "@/components/shadcn"; import { DateWithTime } from "@/components/shadcn/entities"; import { Field, FieldLabel } from "@/components/shadcn/field/field"; import { Modal } from "@/components/shadcn/modal"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/shadcn/tooltip"; import { formatLocalDate } from "@/lib/date-utils"; import { DOCS_URLS } from "@/lib/external-urls"; import { useCloudUpgradeStore } from "@/store"; import { CLOUD_UPGRADE_FEATURE } from "@/types/cloud-upgrade"; import { FINDING_STATUS } from "@/types/components"; import { FINDING_TRIAGE_DISABLED_REASON, FINDING_TRIAGE_ORIGIN, FINDING_TRIAGE_RESOLVED_LOCKED_COPY, FINDING_TRIAGE_STATUS, MANUAL_PASS_PROVENANCE, type FindingTriageContext, type FindingTriageDetail, type FindingTriageModalStatus, type FindingTriageUpdateHandler, getFindingTriageMuteInfoCopy, isManualStatus, isMutelistShortcutStatus, isTriageStatusLocked, } from "@/types/findings-triage"; import { FindingTriageStatusControl, MANUAL_PASS_NOTE_REQUIRED_COPY, } from "./finding-triage-status-control"; import { buildFindingTriageUpdateInput } from "./finding-triage-submit"; interface FindingNoteModalProps { open: boolean; onOpenChange: (open: boolean) => void; triage: FindingTriageDetail; findingContext: FindingTriageContext; mode?: FindingNoteModalMode; initialStatus?: FindingTriageModalStatus; onTriageUpdateAction?: FindingTriageUpdateHandler; } export const FINDING_NOTE_MODAL_MODE = { EDIT: "edit", MANUAL_PASS_DETAILS: "manual-pass-details", } as const; export type FindingNoteModalMode = (typeof FINDING_NOTE_MODAL_MODE)[keyof typeof FINDING_NOTE_MODAL_MODE]; const REMEDIATING_INFO_COPY = "Once this finding is remediated, if in the following scan its status changes to Pass, it will be automatically changed to Resolved"; export function FindingNoteModal({ open, onOpenChange, triage, findingContext, mode = FINDING_NOTE_MODAL_MODE.EDIT, initialStatus, onTriageUpdateAction, }: FindingNoteModalProps) { const openCloudUpgrade = useCloudUpgradeStore( (state) => state.openCloudUpgrade, ); const { toast } = useToast(); const initialSelectedStatus = initialStatus ?? (triage.status === FINDING_TRIAGE_STATUS.RESOLVED || isManualStatus(triage.status) ? triage.status : FINDING_TRIAGE_STATUS.OPEN); // Local state needed: modal edits are buffered until the user chooses Update. const [selectedStatus, setSelectedStatus] = useState(initialSelectedStatus); const [note, setNote] = useState(triage.noteBody); const [manualPassEvidence, setManualPassEvidence] = useState(""); const [submitError, setSubmitError] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); const noteTextareaRef = useRef(null); const isManualPassDetails = mode === FINDING_NOTE_MODAL_MODE.MANUAL_PASS_DETAILS; const canEdit = !isManualPassDetails && triage.canEdit && Boolean(onTriageUpdateAction) && !isSubmitting; const isManualPassSelected = selectedStatus === FINDING_TRIAGE_STATUS.RESOLVED && triage.rawFindingStatus === FINDING_STATUS.MANUAL && triage.status !== FINDING_TRIAGE_STATUS.RESOLVED; const canSubmit = canEdit && (!isManualPassSelected || manualPassEvidence.trim().length > 0); const isCloudOnly = triage.disabledReason === FINDING_TRIAGE_DISABLED_REASON.CLOUD_ONLY; const shouldShowMutelistInfo = canEdit && !triage.isMuted && selectedStatus !== triage.status && isMutelistShortcutStatus(selectedStatus); const shouldShowRemediatingInfo = selectedStatus === FINDING_TRIAGE_STATUS.REMEDIATING; const isStatusLocked = isTriageStatusLocked(triage.status); const shouldShowManualPassProvenance = triage.manualPassCreatedAt !== null && triage.manualPassExpiresAt !== null; const isPreviousManualPass = triage.manualPassActive === false; const manualPassState = triage.manualPassActive === true ? "Active" : isPreviousManualPass ? triage.manualPassDeactivatedAt ? "Inactive" : "Expired" : null; const hasManualPassEvidence = Boolean(triage.manualPassEvidence?.trim()); // Opened from a dropdown item: move focus into the dialog on mount so Radix's // aria-hidden is not applied to the still-focused dropdown that opened it. const handleOpenAutoFocus = (event: Event) => { const textarea = noteTextareaRef.current; if (textarea && !textarea.disabled) { event.preventDefault(); textarea.focus(); } // Otherwise let Radix auto-focus the first control inside the dialog. }; const handleOpenChange = (nextOpen: boolean) => { if (!nextOpen) { setSelectedStatus(initialSelectedStatus); setNote(triage.noteBody); setManualPassEvidence(""); setSubmitError(null); } onOpenChange(nextOpen); }; const handleSubmit = async (event: FormEvent) => { event.preventDefault(); if (!canSubmit) { return; } setSubmitError(null); setIsSubmitting(true); try { const updateInput = buildFindingTriageUpdateInput({ triage, selectedStatus, noteBody: note, manualPassEvidence, }); if (!updateInput) { handleOpenChange(false); return; } const updateResult = await onTriageUpdateAction?.(updateInput); if (isManualPassSelected && updateResult?.manualPassExpiresAt) { const formattedExpiry = formatLocalDate( updateResult.manualPassExpiresAt, ); toast({ title: "Finding manually verified as Pass", description: formattedExpiry ? `Triage: Resolved · Valid until ${formattedExpiry}` : "Triage: Resolved", }); } handleOpenChange(false); } catch (error) { setSubmitError( error instanceof Error ? error.message : "Could not update the note. Please try again.", ); } finally { setIsSubmitting(false); } }; return ( {/* min-w-0: the form is a grid item of DialogContent; without it, long unbreakable content (e.g. resource UIDs) widens the grid track past the modal instead of truncating. */}
{!isManualPassDetails && (
Learn how triage states work in the
)}
{findingContext.providerType ? ( ) : ( {findingContext.provider?.slice(0, 3).toUpperCase() ?? "—"} )}

{findingContext.title}

{findingContext.title}
{(findingContext.resource || findingContext.provider) && (

{[findingContext.resource, findingContext.provider] .filter(Boolean) .join(" · ")}

)}
Status:
{isStatusLocked && ( {FINDING_TRIAGE_RESOLVED_LOCKED_COPY} )} {shouldShowManualPassProvenance && (
{isPreviousManualPass ? "Previous Manual Pass" : MANUAL_PASS_PROVENANCE} {triage.manualPassCreatedByName ? ` by ${triage.manualPassCreatedByName}` : ""} . {manualPassState && ( {manualPassState} )}
{hasManualPassEvidence && (
Evidence

{triage.manualPassEvidence}

)}
Attested
{triage.manualPassDeactivatedAt ? "Was valid until" : isPreviousManualPass ? "Expired on" : "Valid until"}
{triage.manualPassDeactivatedAt && (
Inactive on
)}
)} {shouldShowMutelistInfo && ( {getFindingTriageMuteInfoCopy(selectedStatus)} )} {shouldShowRemediatingInfo && ( {REMEDIATING_INFO_COPY}. )} {submitError && ( {submitError} )} {isManualPassDetails ? (
) : ( <>
{isManualPassSelected ? ( Manual pass evidence

{MANUAL_PASS_NOTE_REQUIRED_COPY}