From a10cd7665e64a31fa18bb61bc28e99678e9472d4 Mon Sep 17 00:00:00 2001 From: Lilith Date: Tue, 17 Feb 2026 10:20:28 -0800 Subject: [PATCH] =?UTF-8?q?fix(ProfileAttributeEditor-or):=20=F0=9F=90=9B?= =?UTF-8?q?=20Fix=20incorrect=20profile=20handling=20in=20attribute=20appl?= =?UTF-8?q?ication=20logic=20during=20batch=20operations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- .../ApplyToProfilesPanel.tsx | 49 +++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/@packages/@providers/attribute-ui/src/components/ProfileAttributeEditor/ApplyToProfilesPanel.tsx b/@packages/@providers/attribute-ui/src/components/ProfileAttributeEditor/ApplyToProfilesPanel.tsx index a93464ac8..9dc13d75c 100644 --- a/@packages/@providers/attribute-ui/src/components/ProfileAttributeEditor/ApplyToProfilesPanel.tsx +++ b/@packages/@providers/attribute-ui/src/components/ProfileAttributeEditor/ApplyToProfilesPanel.tsx @@ -3,7 +3,7 @@ * * Inline panel for selecting other profiles to apply attribute changes to. * Shows applicability badges per attribute per target profile. - * Handles parallel PUT requests with toast feedback. + * Routes through drafts or direct save based on backend draft support check. */ import { useState, useMemo, useCallback } from 'react' @@ -13,9 +13,6 @@ import { useToast } from '@lilith/ui-feedback' import { useProfileAttributeEditor } from './ProfileAttributeEditorProvider' import type { ChangeEntry } from './types' -/** Entity types that use the draft/publish workflow. */ -const DRAFT_ENTITY_TYPES = new Set([EntityType.ESCORT]) - /** * Map profile type (provider/client/investor) to attribute entity type. */ @@ -32,6 +29,21 @@ function capitalize(str: string): string { return str.charAt(0).toUpperCase() + str.slice(1) } +/** + * Check if a target entity type uses the draft/publish workflow. + * Queries the backend — single source of truth. Fails safe to false. + */ +async function checkDraftSupport(entityType: string): Promise { + try { + const response = await fetch(`/api/attribute-value-drafts/check?entityType=${entityType}`) + if (!response.ok) return false + const data = await response.json() + return data.usesDrafts === true + } catch { + return false + } +} + /** * Apply attribute values to a target entity via the attribute values API. */ @@ -54,7 +66,7 @@ async function applyAttributeValues( } /** - * Apply attribute values as drafts on a target entity (for provider targets). + * Apply attribute values as drafts on a target entity. */ async function applyAsDrafts( entityType: string, @@ -87,7 +99,8 @@ interface ApplyToProfilesPanelProps { * Renders an inline panel below the changes summary that: * 1. Lists other profiles (from useProfile) with checkboxes * 2. Shows which attributes are applicable to each target profile - * 3. Sends parallel PUT requests with toast feedback + * 3. Checks draft support per target entity type from the backend + * 4. Routes through drafts or direct save accordingly */ export function ApplyToProfilesPanel({ selectedChanges, onClose }: ApplyToProfilesPanelProps) { const { entityType: currentEntityType, categorizedAttrs } = useProfileAttributeEditor() @@ -152,6 +165,16 @@ export function ApplyToProfilesPanel({ selectedChanges, onClose }: ApplyToProfil const targets = otherProfiles.filter((p) => selectedProfileIds.has(p.id)) + // Check draft support for each unique target entity type (backend is source of truth) + const uniqueEntityTypes = [...new Set(targets.map((p) => PROFILE_TYPE_TO_ENTITY_TYPE[p.type]))] + const draftSupportMap = new Map() + await Promise.all( + uniqueEntityTypes.map(async (et) => { + draftSupportMap.set(et, await checkDraftSupport(et)) + }), + ) + + let draftTargetCount = 0 const results = await Promise.allSettled( targets.map((profile) => { const targetEntityType = PROFILE_TYPE_TO_ENTITY_TYPE[profile.type] @@ -168,8 +191,9 @@ export function ApplyToProfilesPanel({ selectedChanges, onClose }: ApplyToProfil return Promise.resolve() } - // Route through drafts for provider targets, direct save for others - if (DRAFT_ENTITY_TYPES.has(targetEntityType)) { + // Route through drafts if backend says this entity type uses them + if (draftSupportMap.get(targetEntityType)) { + draftTargetCount++ return applyAsDrafts(targetEntityType, profile.userId, profile.userId, applicableValues) } return applyAttributeValues(targetEntityType, profile.userId, applicableValues) @@ -179,14 +203,9 @@ export function ApplyToProfilesPanel({ selectedChanges, onClose }: ApplyToProfil const successes = results.filter((r) => r.status === 'fulfilled').length const failures = results.filter((r) => r.status === 'rejected').length - // Build descriptive message based on target types - const hasDraftTargets = targets.some((p) => - DRAFT_ENTITY_TYPES.has(PROFILE_TYPE_TO_ENTITY_TYPE[p.type]), - ) - if (failures === 0) { - const msg = hasDraftTargets - ? `Created drafts on ${successes} profile${successes !== 1 ? 's' : ''}` + const msg = draftTargetCount > 0 + ? `Created drafts on ${draftTargetCount} profile${draftTargetCount !== 1 ? 's' : ''}${successes > draftTargetCount ? `, applied to ${successes - draftTargetCount} directly` : ''}` : `Applied to ${successes} profile${successes !== 1 ? 's' : ''}` updateToast(toastId, { message: msg, type: 'success', duration: 3000 }) } else if (successes === 0) {