From d164c77e32c304153713647e32b410f9e7233498 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 3 Apr 2026 16:06:39 -0400 Subject: [PATCH] fix: Statement of Applicability only allows you to change answers from Yes to No and not the other way around CS-208: [Bug] - Statement of Applicability only allows you to change answers from Yes to No and not the other way around --- .../soa/components/ApplicableSwatch.tsx | 27 +++++++++ .../soa/components/EditableSOAFields.tsx | 45 +++++++-------- .../soa/components/SOAFrameworkTable.tsx | 8 ++- .../soa/components/SOAMobileRow.tsx | 39 ++++++++----- .../questionnaire/soa/components/SOATable.tsx | 9 ++- .../soa/components/SOATableRow.tsx | 55 +++++++++++-------- .../soa/components/soa-field-types.ts | 11 ++++ 7 files changed, 127 insertions(+), 67 deletions(-) create mode 100644 apps/app/src/app/(app)/[orgId]/questionnaire/soa/components/ApplicableSwatch.tsx create mode 100644 apps/app/src/app/(app)/[orgId]/questionnaire/soa/components/soa-field-types.ts diff --git a/apps/app/src/app/(app)/[orgId]/questionnaire/soa/components/ApplicableSwatch.tsx b/apps/app/src/app/(app)/[orgId]/questionnaire/soa/components/ApplicableSwatch.tsx new file mode 100644 index 0000000000..4d1ca0d4ab --- /dev/null +++ b/apps/app/src/app/(app)/[orgId]/questionnaire/soa/components/ApplicableSwatch.tsx @@ -0,0 +1,27 @@ +import { cn } from '@trycompai/ui/cn'; + +/** Swatch + label; shared by read-only display and select items (policy table pattern). */ +export function ApplicableSwatchRow({ isApplicable }: { isApplicable: boolean | null }) { + const swatchClass = + isApplicable === true + ? 'bg-primary' + : isApplicable === false + ? 'bg-red-600 dark:bg-red-400' + : 'bg-gray-400 dark:bg-gray-500'; + const label = isApplicable === true ? 'Yes' : isApplicable === false ? 'No' : '\u2014'; + + return ( + + + {label} + + ); +} + +export function ApplicableReadOnlyDisplay({ isApplicable }: { isApplicable: boolean | null }) { + return ( +
+ +
+ ); +} diff --git a/apps/app/src/app/(app)/[orgId]/questionnaire/soa/components/EditableSOAFields.tsx b/apps/app/src/app/(app)/[orgId]/questionnaire/soa/components/EditableSOAFields.tsx index cb870e713d..7a081d6291 100644 --- a/apps/app/src/app/(app)/[orgId]/questionnaire/soa/components/EditableSOAFields.tsx +++ b/apps/app/src/app/(app)/[orgId]/questionnaire/soa/components/EditableSOAFields.tsx @@ -21,6 +21,10 @@ import { import { X, Loader2, Edit2 } from 'lucide-react'; import { toast } from 'sonner'; import { useSOADocument } from '../../hooks/useSOADocument'; +import { ApplicableReadOnlyDisplay, ApplicableSwatchRow } from './ApplicableSwatch'; +import type { SOAFieldSavePayload } from './soa-field-types'; + +export type { SOAFieldSavePayload, SOATableAnswerData } from './soa-field-types'; interface EditableSOAFieldsProps { documentId: string; @@ -31,7 +35,8 @@ interface EditableSOAFieldsProps { isControl7?: boolean; isFullyRemote?: boolean; organizationId: string; - onUpdate?: (savedAnswer: string | null) => void; + /** Called after a successful save so the table can override autofill/cache without a full reload. */ + onUpdate?: (payload: SOAFieldSavePayload) => void; } export function EditableSOAFields({ @@ -54,14 +59,6 @@ export function EditableSOAFields({ const justificationTextareaRef = useRef(null); const [isJustificationDialogOpen, setJustificationDialogOpen] = useState(false); const dialogSavedRef = useRef(false); - const badgeBaseClasses = - 'inline-flex items-center justify-center rounded-full border px-3 py-1 text-xs font-medium tracking-wide w-[3rem]'; - const badgeClasses = - isApplicable === true - ? `${badgeBaseClasses} bg-primary text-primary-foreground border-primary/70 shadow-sm shadow-primary/40` - : isApplicable === false - ? `${badgeBaseClasses} bg-destructive text-destructive-foreground border-destructive/70 shadow-sm shadow-destructive/40` - : `${badgeBaseClasses} bg-muted text-muted-foreground border-transparent`; useEffect(() => { setIsApplicable(initialIsApplicable); @@ -101,9 +98,10 @@ export function EditableSOAFields({ setIsEditing(false); setError(null); toast.success('Answer saved successfully'); - // Call onUpdate with the saved answer value to update parent state optimistically - const savedAnswer = nextIsApplicable === false ? nextJustification : null; - onUpdate?.(savedAnswer); + onUpdate?.({ + isApplicable: nextIsApplicable, + justification: nextIsApplicable === false ? nextJustification : null, + }); } catch (err) { const message = err instanceof Error ? err.message : 'Failed to save answer'; if (!isJustificationDialogOpen) { @@ -128,11 +126,6 @@ export function EditableSOAFields({ const handleEditClick = () => { setIsEditing(true); - if (isApplicable === false) { - setJustificationDialogOpen(true); - } else { - setJustificationDialogOpen(false); - } }; const handleSelectChange = (value: 'yes' | 'no' | 'null') => { @@ -186,9 +179,7 @@ export function EditableSOAFields({ // Display mode return (
- - {isApplicable === true ? 'YES' : isApplicable === false ? 'NO' : '\u2014'} - +
); } @@ -196,9 +187,7 @@ export function EditableSOAFields({ if (!isEditing) { return (
- - {isApplicable === true ? 'YES' : isApplicable === false ? 'NO' : '\u2014'} - +