diff --git a/apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/BackgroundCheckPathCard.tsx b/apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/BackgroundCheckPathCard.tsx index 7fdc36afa..e7dee2ff3 100644 --- a/apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/BackgroundCheckPathCard.tsx +++ b/apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/BackgroundCheckPathCard.tsx @@ -13,7 +13,7 @@ export interface PathCardProps { iconTone?: 'default' | 'warning'; title: string; description: string; - meta: ReactNode; + meta?: ReactNode; disabled?: boolean; } @@ -57,9 +57,7 @@ export function BackgroundCheckPathCard({ className={cn( 'group relative rounded-[var(--radius)] border px-4 py-3.5 transition-colors duration-200 ease-out', 'outline-none focus-visible:ring-[3px] focus-visible:ring-primary/20', - disabled - ? 'cursor-not-allowed opacity-60' - : 'cursor-pointer hover:border-muted-foreground', + disabled ? 'cursor-not-allowed opacity-60' : 'cursor-pointer hover:border-muted-foreground', selected ? 'border-primary bg-[oklch(0.985_0.012_167)] shadow-[inset_0_0_0_1px_var(--primary)]' : 'border-border bg-background', @@ -81,7 +79,7 @@ export function BackgroundCheckPathCard({
{title}
{description}
-
{meta}
+ {meta ?
{meta}
: null} ); } diff --git a/apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/BackgroundCheckV1Page.tsx b/apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/BackgroundCheckV1Page.tsx index 952c5272e..8fd80206c 100644 --- a/apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/BackgroundCheckV1Page.tsx +++ b/apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/BackgroundCheckV1Page.tsx @@ -1,20 +1,11 @@ 'use client'; import { Text } from '@trycompai/design-system'; -import { Attachment, Flash, Security, Warning } from '@trycompai/design-system/icons'; +import { Attachment, Flash, Security } from '@trycompai/design-system/icons'; import { useState } from 'react'; -import { - BackgroundCheckAttachForm, - type AttachFormValues, -} from './BackgroundCheckAttachForm'; -import { - BackgroundCheckExemptForm, - type ExemptFormValues, -} from './BackgroundCheckExemptForm'; -import { - BackgroundCheckOrderForm, - type OrderFormValues, -} from './BackgroundCheckOrderForm'; +import { BackgroundCheckAttachForm, type AttachFormValues } from './BackgroundCheckAttachForm'; +import { BackgroundCheckExemptForm, type ExemptFormValues } from './BackgroundCheckExemptForm'; +import { BackgroundCheckOrderForm, type OrderFormValues } from './BackgroundCheckOrderForm'; import { BackgroundCheckPathCard } from './BackgroundCheckPathCard'; import { BackgroundCheckScopePanel } from './BackgroundCheckScopePanel'; import { BackgroundCheckStatusStrip } from './BackgroundCheckStatusStrip'; @@ -90,7 +81,11 @@ export function BackgroundCheckV1Page(props: V1PageProps) { How would you like to proceed? -
+
handleSelect('order')} @@ -116,12 +111,6 @@ export function BackgroundCheckV1Page(props: V1PageProps) { icon={Security} title="Mark as exempt" description="This employee won't be required to pass a check." - meta={ - - - Logs a compliance exception - - } />
@@ -141,7 +130,9 @@ export function BackgroundCheckV1Page(props: V1PageProps) { Boolean(props.orderValues.employeeEmail) } disabledReason={ - !props.hasAllowance ? "You're out of credits. Choose a plan to continue." : undefined + !props.hasAllowance + ? "You're out of credits. Choose a plan to continue." + : undefined } /> )} @@ -152,7 +143,9 @@ export function BackgroundCheckV1Page(props: V1PageProps) { onSubmit={props.onAttachSubmit} submitting={props.isAttachSubmitting} canSubmit={ - props.canRequest && Boolean(props.attachValues.vendor) && Boolean(props.attachValues.file) + props.canRequest && + Boolean(props.attachValues.vendor) && + Boolean(props.attachValues.file) } /> )} diff --git a/apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/EmployeeBackgroundCheck.test.tsx b/apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/EmployeeBackgroundCheck.test.tsx index 16e427dba..e6ea6fbe9 100644 --- a/apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/EmployeeBackgroundCheck.test.tsx +++ b/apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/EmployeeBackgroundCheck.test.tsx @@ -93,6 +93,7 @@ describe('EmployeeBackgroundCheck — V1 two-paths', () => { expect(screen.getByText('Order a new check')).toBeInTheDocument(); expect(screen.getByText('Attach an existing report')).toBeInTheDocument(); expect(screen.getByText('Mark as exempt')).toBeInTheDocument(); + expect(screen.queryByText('Logs a compliance exception')).not.toBeInTheDocument(); const orderCard = screen.getByRole('radio', { name: /Order a new check/i }); expect(orderCard).toHaveAttribute('aria-checked', 'true'); diff --git a/apps/app/src/app/(app)/[orgId]/trust/portal-settings/components/BrandSettings.test.tsx b/apps/app/src/app/(app)/[orgId]/trust/portal-settings/components/BrandSettings.test.tsx index 33b7102c1..af69fde67 100644 --- a/apps/app/src/app/(app)/[orgId]/trust/portal-settings/components/BrandSettings.test.tsx +++ b/apps/app/src/app/(app)/[orgId]/trust/portal-settings/components/BrandSettings.test.tsx @@ -106,4 +106,36 @@ describe('BrandSettings permission gating', () => { expect(handlePrimaryColorChange).toHaveBeenCalledWith('#00FF00'); expect(navigationMock.refresh).toHaveBeenCalled(); }); + + it('persists an empty brand color as a reset to default branding', async () => { + setMockPermissions(ADMIN_PERMISSIONS); + const handlePrimaryColorChange = vi.fn(); + + render(); + + const textInput = screen.getByRole('textbox'); + fireEvent.change(textInput, { target: { value: '' } }); + + await waitFor(() => { + expect(trustPortalSettingsMock.updateToggleSettings).toHaveBeenCalledWith({ + enabled: true, + primaryColor: '', + }); + }); + expect(handlePrimaryColorChange).toHaveBeenCalledWith(null); + expect(navigationMock.refresh).toHaveBeenCalled(); + }); + + it('does not persist an invalid brand color', () => { + setMockPermissions(ADMIN_PERMISSIONS); + + render(); + + const textInput = screen.getByRole('textbox'); + fireEvent.change(textInput, { target: { value: 'zzzzzz' } }); + fireEvent.blur(textInput); + + expect(trustPortalSettingsMock.updateToggleSettings).not.toHaveBeenCalled(); + expect(navigationMock.refresh).not.toHaveBeenCalled(); + }); }); diff --git a/apps/app/src/app/(app)/[orgId]/trust/portal-settings/components/BrandSettings.tsx b/apps/app/src/app/(app)/[orgId]/trust/portal-settings/components/BrandSettings.tsx index 85fcfed40..3fd2089bb 100644 --- a/apps/app/src/app/(app)/[orgId]/trust/portal-settings/components/BrandSettings.tsx +++ b/apps/app/src/app/(app)/[orgId]/trust/portal-settings/components/BrandSettings.tsx @@ -33,10 +33,15 @@ interface BrandSettingsProps { onPrimaryColorChange?: (primaryColor: string | null) => void; } -function normalizePrimaryColor(value: unknown): string | null { - if (typeof value !== 'string' || value.length === 0) return null; - if (!HEX_COLOR_PATTERN.test(value)) return null; - return value.toUpperCase(); +function normalizePrimaryColor(value: unknown): string | null | undefined { + if (value === null) return null; + if (typeof value !== 'string') return undefined; + + const trimmedValue = value.trim(); + if (trimmedValue.length === 0) return null; + if (!HEX_COLOR_PATTERN.test(trimmedValue)) return undefined; + + return trimmedValue.toUpperCase(); } export function BrandSettings({ @@ -57,7 +62,7 @@ export function BrandSettings({ }); const lastSaved = useRef<{ [key: string]: string | null }>({ - primaryColor: normalizePrimaryColor(primaryColor), + primaryColor: normalizePrimaryColor(primaryColor) ?? null, }); const savingRef = useRef<{ [key: string]: boolean }>({ @@ -71,7 +76,7 @@ export function BrandSettings({ } const nextPrimaryColor = normalizePrimaryColor(value); - if (!nextPrimaryColor) { + if (nextPrimaryColor === undefined) { return; } @@ -82,7 +87,7 @@ export function BrandSettings({ enabled, primaryColor: field === 'primaryColor' - ? nextPrimaryColor + ? (nextPrimaryColor ?? '') : (form.getValues('primaryColor') ?? undefined), }); toast.success('Brand settings updated'); @@ -106,7 +111,7 @@ export function BrandSettings({ const normalizedPrimaryColor = normalizePrimaryColor(primaryColor); form.reset({ primaryColor: normalizedPrimaryColor ?? undefined }); setPrimaryColorValue(normalizedPrimaryColor ?? ''); - lastSaved.current.primaryColor = normalizedPrimaryColor; + lastSaved.current.primaryColor = normalizedPrimaryColor ?? null; }, [form, primaryColor]); useEffect(() => { @@ -116,8 +121,8 @@ export function BrandSettings({ !savingRef.current.primaryColor ) { const normalizedPrimaryColor = normalizePrimaryColor(debouncedPrimaryColor); - if (normalizedPrimaryColor) { - form.setValue('primaryColor', normalizedPrimaryColor); + if (normalizedPrimaryColor !== undefined) { + form.setValue('primaryColor', normalizedPrimaryColor ?? undefined); void autoSave('primaryColor', normalizedPrimaryColor); } } @@ -126,12 +131,12 @@ export function BrandSettings({ const handlePrimaryColorBlur = useCallback( (e: React.FocusEvent) => { const value = normalizePrimaryColor(e.target.value); - if (!value) { + if (value === undefined) { toast.error('Enter a valid hex color'); return; } - form.setValue('primaryColor', value); - setPrimaryColorValue(value); + form.setValue('primaryColor', value ?? undefined); + setPrimaryColorValue(value ?? ''); void autoSave('primaryColor', value); }, [form, autoSave], @@ -181,7 +186,7 @@ export function BrandSettings({
{ let value = e.target.value; if (value.length > 0 && !value.startsWith('#')) {