# Upstream NexusCRM patch # Keep ActivityTimeline quick-actions (Log a Call, Email, Weekly Update) out of # the record-header default action set; they belong to the Activity tab. # # Target: pingcap-inc/NexusCRM @ origin/main (verified against 2102cbd). # Apply inside a clean NexusCRM checkout: # git apply --recount docs/.../record-header-activity-actions.patch # or, if upstream context has drifted: # patch -p1 -F3 < record-header-activity-actions.patch # diff --git a/frontend/src/features/record/components/ActionRendererHost.tsx b/frontend/src/features/record/components/ActionRendererHost.tsx --- a/frontend/src/features/record/components/ActionRendererHost.tsx +++ b/frontend/src/features/record/components/ActionRendererHost.tsx @@ -121,6 +121,10 @@ }): StandardAction[] { const resolved = options.layout?.headerActions && options.layout.headerActions.length > 0 ? resolvePlacedStandardActions(options.layout.headerActions, options.standardActions, options.objectApiName) - : getObjectScopedStandardActions(options.standardActions, options.objectApiName); + : getObjectScopedStandardActions(options.standardActions, options.objectApiName) + // Activity-timeline quick-actions (Log a Call, Email, Weekly Update) live in + // the record's Activity tab, not the header. Keep them out of the unauthored + // header fallback; an explicit `headerActions` placement still pins one. + .filter(action => (action.category || '') !== 'ActivityTimeline'); return filterVisibleActionsForRecord(resolved, options.record); } diff --git a/frontend/src/features/record/components/ActionRendererHost.test.tsx b/frontend/src/features/record/components/ActionRendererHost.test.tsx --- a/frontend/src/features/record/components/ActionRendererHost.test.tsx +++ b/frontend/src/features/record/components/ActionRendererHost.test.tsx @@ -202,6 +202,52 @@ })).toEqual([sourceAction]); }); + it('excludes ActivityTimeline quick-actions from the unauthored record-header fallback', () => { + const activityAction: StandardAction = { + name: 'taskCall', + label: 'Log a Call', + type: 'standard', + rendererId: 'app-pack:crm/action-renderer/task-activity-quick-create', + targetObjects: ['SourceRecord'], + category: 'ActivityTimeline', + }; + const headerAction: StandardAction = { + name: 'convertLead', + label: 'Convert Lead', + type: 'standard', + rendererId: 'renderer:lead', + targetObjects: ['SourceRecord'], + category: 'LeadActions', + }; + + expect(resolveVisibleStandardActionsForRecord({ + layout: sourceLayout(), + standardActions: [activityAction, headerAction], + objectApiName: 'SourceRecord', + record: { Id: 'SRC-1' }, + })).toEqual([headerAction]); + }); + + it('still honors an explicitly placed ActivityTimeline action in the header', () => { + const activityAction: StandardAction = { + name: 'taskCall', + label: 'Log a Call', + type: 'standard', + rendererId: 'app-pack:crm/action-renderer/task-activity-quick-create', + targetObjects: ['SourceRecord'], + category: 'ActivityTimeline', + }; + + expect(resolveVisibleStandardActionsForRecord({ + layout: sourceLayout([{ name: 'taskCall', label: 'Log a Call', type: 'Standard' }]), + standardActions: [activityAction], + objectApiName: 'SourceRecord', + record: { Id: 'SRC-1' }, + })).toEqual([activityAction]); + }); + it('suppresses all standard actions for locked records', () => { const sourceAction: StandardAction = {