From bd0384f3f994bd67ec22d418d51e086dbdb9da03 Mon Sep 17 00:00:00 2001 From: Lilith Date: Tue, 17 Feb 2026 10:32:36 -0800 Subject: [PATCH] =?UTF-8?q?chore(attribute-ui):=20=F0=9F=94=A7=20Enhance?= =?UTF-8?q?=20ProfileAttributeEditor=20to=20support=20bulk=20attribute=20a?= =?UTF-8?q?pplication=20across=20multiple=20profiles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- .../ApplyToProfilesPanel.tsx | 65 +++++++++++++++++-- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/@packages/@providers/attribute-ui/src/components/ProfileAttributeEditor/ApplyToProfilesPanel.tsx b/@packages/@providers/attribute-ui/src/components/ProfileAttributeEditor/ApplyToProfilesPanel.tsx index 0327165c6..4ebe824ad 100644 --- a/@packages/@providers/attribute-ui/src/components/ProfileAttributeEditor/ApplyToProfilesPanel.tsx +++ b/@packages/@providers/attribute-ui/src/components/ProfileAttributeEditor/ApplyToProfilesPanel.tsx @@ -116,18 +116,33 @@ 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) + // Check draft support per unique target entity type via TanStack Query cache. + // ensureQueryData returns cached data instantly if useCheckDraftSupport already ran + // for this entity type (staleTime: Infinity), otherwise makes a single API call. 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)) + try { + const result = await queryClient.ensureQueryData({ + queryKey: draftKeys.check(et as EntityType), + queryFn: () => fetchDraftCheck(et as EntityType), + staleTime: Infinity, + }) + draftSupportMap.set(et, result.usesDrafts) + } catch { + draftSupportMap.set(et, false) + } }), ) + // Track which entity types were written to for cache invalidation + const writtenDraftTargets: { entityType: string; entityId: string; userId: string }[] = [] + const writtenDirectTargets: { entityType: string; entityId: string }[] = [] let draftTargetCount = 0 + const results = await Promise.allSettled( - targets.map((profile) => { + targets.map(async (profile) => { const targetEntityType = PROFILE_TYPE_TO_ENTITY_TYPE[profile.type] const applicableValues: AttributeValues = {} @@ -139,18 +154,55 @@ export function ApplyToProfilesPanel({ selectedChanges, onClose }: ApplyToProfil } if (Object.keys(applicableValues).length === 0) { - return Promise.resolve() + return } // Route through drafts if backend says this entity type uses them if (draftSupportMap.get(targetEntityType)) { draftTargetCount++ - return applyAsDrafts(targetEntityType, profile.userId, profile.userId, applicableValues) + await setDrafts( + targetEntityType as EntityType, + profile.userId, + profile.userId, + applicableValues, + ) + writtenDraftTargets.push({ + entityType: targetEntityType, + entityId: profile.userId, + userId: profile.userId, + }) + } else { + await updateAttributeValues( + targetEntityType as EntityType, + profile.userId, + applicableValues, + ) + writtenDirectTargets.push({ + entityType: targetEntityType, + entityId: profile.userId, + }) } - return applyAttributeValues(targetEntityType, profile.userId, applicableValues) }), ) + // Invalidate TanStack Query caches for affected targets + for (const target of writtenDraftTargets) { + queryClient.invalidateQueries({ + queryKey: draftKeys.list(target.entityType as EntityType, target.entityId, target.userId), + }) + queryClient.invalidateQueries({ + queryKey: draftKeys.diff(target.entityType as EntityType, target.entityId, target.userId), + }) + queryClient.invalidateQueries({ + queryKey: draftKeys.count(target.entityType as EntityType, target.entityId, target.userId), + }) + } + for (const target of writtenDirectTargets) { + queryClient.invalidateQueries({ + queryKey: attributeValueKeys.list(target.entityType as EntityType, target.entityId), + }) + } + const successes = results.filter((r) => r.status === 'fulfilled').length const failures = results.filter((r) => r.status === 'rejected').length @@ -180,6 +232,7 @@ export function ApplyToProfilesPanel({ selectedChanges, onClose }: ApplyToProfil otherProfiles, selectedChanges, attrEntityTypes, + queryClient, showToast, updateToast, onClose,