fix(ProfileAttributeEditor-or): 🐛 Fix incorrect profile handling in attribute application logic during batch operations

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Lilith 2026-02-17 10:20:28 -08:00
parent 602a4429d0
commit a10cd7665e

View file

@ -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<string>([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<boolean> {
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<string, boolean>()
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) {