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'}
-
+