From e68000bc6f2b715f55f7d4bcf13dcee537601f87 Mon Sep 17 00:00:00 2001 From: dkoo Date: Mon, 13 Apr 2026 11:49:29 -0600 Subject: [PATCH 1/7] refactor(access-control): move Metering settings to top of settings cards --- .../views/content-gates/edit/custom-access.tsx | 4 ++-- .../views/content-gates/edit/registration.tsx | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/wizards/audience/views/content-gates/edit/custom-access.tsx b/src/wizards/audience/views/content-gates/edit/custom-access.tsx index 2564f99157..a4852f6894 100644 --- a/src/wizards/audience/views/content-gates/edit/custom-access.tsx +++ b/src/wizards/audience/views/content-gates/edit/custom-access.tsx @@ -42,15 +42,15 @@ export default function CustomAccess( { customAccess, onChange, isNewsletter = f return ( <> - { ! isNewsletter && ( <> - handleChange( { metering } ) } /> + ) } + ); } diff --git a/src/wizards/audience/views/content-gates/edit/registration.tsx b/src/wizards/audience/views/content-gates/edit/registration.tsx index bc96797725..b96b911914 100644 --- a/src/wizards/audience/views/content-gates/edit/registration.tsx +++ b/src/wizards/audience/views/content-gates/edit/registration.tsx @@ -32,17 +32,8 @@ export default function Registration( { registration, onChange, isNewsletter = f ); return ( <> - - handleChange( { require_verification: ! registration.require_verification } ) } - /> - { ! isNewsletter && ( <> - handleChange( { metering } ) } /> + ) } + + handleChange( { require_verification: ! registration.require_verification } ) } + /> + ); } From a49bb06296dc2e3ddc70aecd65a537abdf61d7b6 Mon Sep 17 00:00:00 2001 From: dkoo Date: Mon, 13 Apr 2026 12:11:01 -0600 Subject: [PATCH 2/7] refactor: move edit layout buttons to Card dropdown menu --- packages/components/src/card/core-card.js | 48 +++++++++--- .../content-gates/content-gate-settings.tsx | 73 +++++++++++-------- 2 files changed, 77 insertions(+), 44 deletions(-) diff --git a/packages/components/src/card/core-card.js b/packages/components/src/card/core-card.js index 7ccbeb3413..8830d87af8 100644 --- a/packages/components/src/card/core-card.js +++ b/packages/components/src/card/core-card.js @@ -7,7 +7,7 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { Button, Card as CardWrapper, CardHeader, CardFooter, DropdownMenu, MenuItem, ToggleControl } from '@wordpress/components'; +import { Button, Card as CardWrapper, CardHeader, CardFooter, DropdownMenu, MenuGroup, MenuItem, ToggleControl } from '@wordpress/components'; import { Icon, chevronDown, chevronRight, chevronUp, dragHandle, moreVertical } from '@wordpress/icons'; /** @@ -140,17 +140,41 @@ const CoreCard = ( { { actions?.length > 0 && ( { () => - actions.map( ( action, index ) => ( - - { action.label } - - ) ) + actions.map( ( action, index ) => { + // Actions can be an array of sub-actions, which are rendered within a MenuGroup. + if ( Array.isArray( action ) ) { + return ( + + { action.map( ( subAction, i ) => { + return ( + + { subAction.label } + + ); + } ) } + + ); + } + return ( + + { action.label } + + ); + } ) } ) } diff --git a/src/wizards/audience/views/content-gates/content-gate-settings.tsx b/src/wizards/audience/views/content-gates/content-gate-settings.tsx index ab0973558f..ff10d61a5d 100644 --- a/src/wizards/audience/views/content-gates/content-gate-settings.tsx +++ b/src/wizards/audience/views/content-gates/content-gate-settings.tsx @@ -9,7 +9,7 @@ import { createInterpolateElement, useRef } from '@wordpress/element'; /** * Internal dependencies */ -import { Badge, Button, Card, Grid, Router, useConfirmDialog } from '../../../../../packages/components/src'; +import { Badge, Card, Grid, Router, useConfirmDialog } from '../../../../../packages/components/src'; import { useWizardData } from '../../../../../packages/components/src/wizard/store/utils'; import { useWizardApiFetch } from '../../../hooks/use-wizard-api-fetch'; import { WIZARD_STORE_NAMESPACE } from '../../../../../packages/components/src/wizard/store'; @@ -118,6 +118,45 @@ export default function ContentGateSettings( { ); }; + const actions = [ + [ + { + label: __( 'Edit', 'newspack-plugin' ), + action: () => history.push( `/edit/${ gate.id }` ), + disabled: isFetching, + }, + { + label: gate.status !== 'publish' ? __( 'Activate', 'newspack-plugin' ) : __( 'Deactivate', 'newspack-plugin' ), + action: () => updateStatus.current?.( gate.status === 'publish' ? 'draft' : 'publish' ), + disabled: isFetching, + }, + { + label: __( 'Delete', 'newspack-plugin' ), + action: () => requestDelete( handleDelete ), + disabled: isFetching, + destructive: true, + }, + ], + ]; + const hasRegistrationLayout = gate.registration?.active && gate.registration.gate_layout_id; + const hasCustomAccessLayout = gate.custom_access?.active && gate.custom_access.access_rules?.length > 0 && gate.custom_access.gate_layout_id; + const layoutOptions: { label: string; action?: () => void; href?: string }[] = []; + if ( hasRegistrationLayout ) { + layoutOptions.push( { + label: __( 'Edit registered access layout', 'newspack-plugin' ), + href: getEditGateLayoutUrl( gate.id, 'registration' ), + } ); + } + if ( hasCustomAccessLayout ) { + layoutOptions.push( { + label: __( 'Edit paid access layout', 'newspack-plugin' ), + href: getEditGateLayoutUrl( gate.id, 'custom_access' ), + } ); + } + if ( layoutOptions.length > 0 ) { + actions.push( layoutOptions ); + } + return ( <> { deleteDialog } @@ -137,24 +176,7 @@ export default function ContentGateSettings( { ), - actions: [ - { - label: __( 'Edit', 'newspack-plugin' ), - action: () => history.push( `/edit/${ gate.id }` ), - disabled: isFetching, - }, - { - label: gate.status !== 'publish' ? __( 'Activate', 'newspack-plugin' ) : __( 'Deactivate', 'newspack-plugin' ), - action: () => updateStatus.current?.( gate.status === 'publish' ? 'draft' : 'publish' ), - disabled: isFetching, - }, - { - label: __( 'Delete', 'newspack-plugin' ), - action: () => requestDelete( handleDelete ), - disabled: isFetching, - destructive: true, - }, - ], + actions, } } > @@ -198,11 +220,6 @@ export default function ContentGateSettings( {

) } { ! gate.registration?.active &&

{ __( 'N/A', 'newspack-plugin' ) }

} - { gate.registration?.active && gate.registration.gate_layout_id && ( - - ) } ) }
@@ -242,14 +259,6 @@ export default function ContentGateSettings( { { ( ! gate.custom_access?.active || gate.custom_access.access_rules?.length === 0 ) && (

{ __( 'N/A', 'newspack-plugin' ) }

) } - { gate.custom_access?.active && - gate.custom_access.access_rules?.length > 0 && - gate.custom_access.gate_layout_id && - ! isNewsletter && ( - - ) }
From 38ffb72ecef46a74fae2264bfff213864def2644 Mon Sep 17 00:00:00 2001 From: dkoo Date: Mon, 13 Apr 2026 12:35:32 -0600 Subject: [PATCH 3/7] feat: add Edit Layout buttons to Registered / Paid Access cards --- .../src/card-settings-group/index.tsx | 14 +++++++++++- packages/components/src/card/core-card.js | 15 +++++++++++++ packages/components/src/card/style-core.scss | 12 +++++++--- .../views/content-gates/edit/index.tsx | 22 +++++++++++++++++-- 4 files changed, 57 insertions(+), 6 deletions(-) diff --git a/packages/components/src/card-settings-group/index.tsx b/packages/components/src/card-settings-group/index.tsx index 375f6d3501..14a650f47b 100644 --- a/packages/components/src/card-settings-group/index.tsx +++ b/packages/components/src/card-settings-group/index.tsx @@ -12,6 +12,7 @@ const CardSettingsGroup = ( { actionType = 'none', children, icon = null, + headerAction, title = '', description = '', isActive = false, @@ -21,6 +22,16 @@ const CardSettingsGroup = ( { children?: React.ReactNode; icon?: React.ReactNode; title: string; + headerAction?: { + label: string; + icon?: React.ReactNode; + href?: string; + onClick?: () => void; + disabled?: boolean; + destructive?: boolean; + tone?: 'primary' | 'secondary' | 'tertiary' | 'link'; + variant?: 'primary' | 'secondary' | 'tertiary' | 'link'; + }; description?: string; isActive?: boolean; onEnable?: () => void; @@ -38,7 +49,8 @@ const CardSettingsGroup = ( { { description &&

{ description }

} ), - onHeaderClick: onEnable, + headerAction, + onToggle: onEnable, icon, iconBackgroundColor: true, isActive, diff --git a/packages/components/src/card/core-card.js b/packages/components/src/card/core-card.js index 8830d87af8..5f27c85f2e 100644 --- a/packages/components/src/card/core-card.js +++ b/packages/components/src/card/core-card.js @@ -28,6 +28,7 @@ const CoreCard = ( { className, footer, header, + headerAction, headerStyle, childrenStyle, footerStyle, @@ -178,6 +179,20 @@ const CoreCard = ( { } ) } + { headerAction && ( + + ) } ) } { children && ( diff --git a/packages/components/src/card/style-core.scss b/packages/components/src/card/style-core.scss index 47aad36865..d3342ba3cd 100644 --- a/packages/components/src/card/style-core.scss +++ b/packages/components/src/card/style-core.scss @@ -52,6 +52,7 @@ } &__header { color: wp-colors.$gray-700; + flex-wrap: wrap; position: relative; .newspack-card--core__action { margin: 0; @@ -79,6 +80,11 @@ right: 0; top: 0; } + &__action { + background-color:wp-colors.$white; + flex: 1 100%; + justify-content: center !important; + } } &__header--is-draggable { padding-left: 80px !important; @@ -161,7 +167,7 @@ &__is-active:not(.newspack-card--core__is-draggable) { border-color: var(--wp-admin-theme-color); box-shadow: var(--wp-admin-theme-color) 0 0 0 1px; - button.newspack-card--core__header { + .newspack-card--core__header { background-color: rgba( var(--wp-admin-theme-color--rgb), 0.04 ); color: var(--wp-admin-theme-color); h1, @@ -178,7 +184,7 @@ } &.newspack-card--core__has-children { - button.newspack-card--core__header { + .newspack-card--core__header { border-bottom-left-radius: 0; border-bottom-right-radius: 0; } @@ -214,7 +220,7 @@ } } &.is-destructive { - button.newspack-card--core__header { + .newspack-card--core__header { h1, h2, h3, diff --git a/src/wizards/audience/views/content-gates/edit/index.tsx b/src/wizards/audience/views/content-gates/edit/index.tsx index 2c95e60fde..750846f277 100644 --- a/src/wizards/audience/views/content-gates/edit/index.tsx +++ b/src/wizards/audience/views/content-gates/edit/index.tsx @@ -9,7 +9,7 @@ import { __, sprintf } from '@wordpress/i18n'; import { __experimentalVStack as VStack } from '@wordpress/components'; // eslint-disable-line @wordpress/no-unsafe-wp-apis import { useDispatch } from '@wordpress/data'; import { createInterpolateElement, useCallback, useEffect, useRef, useState } from '@wordpress/element'; -import { commentAuthorAvatar, currencyDollar, envelope, postList, settings } from '@wordpress/icons'; +import { commentAuthorAvatar, currencyDollar, envelope, pencil, postList, settings } from '@wordpress/icons'; /** * Internal dependencies @@ -22,7 +22,7 @@ import { useWizardApiFetch } from '../../../../hooks/use-wizard-api-fetch'; import ContentRules from './content-rules'; import Registration from './registration'; import CustomAccess from './custom-access'; -import { getGateStatus, getGateStatusBadgeLevel } from '../utils'; +import { getEditGateLayoutUrl, getGateStatus, getGateStatusBadgeLevel } from '../utils'; const { useHistory } = Router; @@ -550,6 +550,15 @@ const Edit = ( { match, updateGatesData, slug = AUDIENCE_CONTENT_GATES_WIZARD_SL __( 'Readers must log in to view %s.', 'newspack-plugin' ), isNewsletter ? __( 'these lists', 'newspack-plugin' ) : __( 'this content', 'newspack-plugin' ) ) } + headerAction={ + registration?.active && registration.gate_layout_id + ? { + label: __( 'Edit layout', 'newspack-plugin' ), + href: getEditGateLayoutUrl( gate.id, 'registration' ), + icon: pencil, + } + : undefined + } icon={ commentAuthorAvatar } isActive={ registration?.active } onEnable={ () => setRegistration( { ...registration, active: ! registration.active } ) } @@ -564,6 +573,15 @@ const Edit = ( { match, updateGatesData, slug = AUDIENCE_CONTENT_GATES_WIZARD_SL 'Set conditions like subscriptions, domain, and more. Readers must meet at least one condition to gain access.', 'newspack-plugin' ) } + headerAction={ + customAccess?.active && customAccess.access_rules?.length > 0 + ? { + label: __( 'Edit layout', 'newspack-plugin' ), + href: getEditGateLayoutUrl( gate.id, 'custom_access' ), + icon: pencil, + } + : undefined + } icon={ currencyDollar } isActive={ customAccess?.active } onEnable={ () => setCustomAccess( { ...customAccess, active: ! customAccess.active } ) } From 6984bf8377430f04e3dc9feb7be1e9ddf291b4b5 Mon Sep 17 00:00:00 2001 From: dkoo Date: Mon, 13 Apr 2026 14:13:24 -0600 Subject: [PATCH 4/7] fix: show Paid Access edit layout button just when enabled --- src/wizards/audience/views/content-gates/edit/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wizards/audience/views/content-gates/edit/index.tsx b/src/wizards/audience/views/content-gates/edit/index.tsx index 750846f277..bc675f7447 100644 --- a/src/wizards/audience/views/content-gates/edit/index.tsx +++ b/src/wizards/audience/views/content-gates/edit/index.tsx @@ -574,7 +574,7 @@ const Edit = ( { match, updateGatesData, slug = AUDIENCE_CONTENT_GATES_WIZARD_SL 'newspack-plugin' ) } headerAction={ - customAccess?.active && customAccess.access_rules?.length > 0 + customAccess?.active ? { label: __( 'Edit layout', 'newspack-plugin' ), href: getEditGateLayoutUrl( gate.id, 'custom_access' ), From 6daf5a72a8a581f73480c726add74b7fa70bb47a Mon Sep 17 00:00:00 2001 From: dkoo Date: Wed, 15 Apr 2026 17:35:36 -0600 Subject: [PATCH 5/7] fix: if editing new gate, create and then redirect to layout post --- .../views/content-gates/edit/index.tsx | 90 ++++++++++--------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/src/wizards/audience/views/content-gates/edit/index.tsx b/src/wizards/audience/views/content-gates/edit/index.tsx index bc675f7447..e9c1be77cc 100644 --- a/src/wizards/audience/views/content-gates/edit/index.tsx +++ b/src/wizards/audience/views/content-gates/edit/index.tsx @@ -121,44 +121,51 @@ const Edit = ( { match, updateGatesData, slug = AUDIENCE_CONTENT_GATES_WIZARD_SL ), } ); - const handleCreate = useCallback( () => { - if ( isFetching ) { - return; - } - isSaving.current = true; - resetNotices(); - resetError(); - const _gate = { - ...gate, - title, - content_rules: contentRules, - registration, - custom_access: customAccess, - }; - wizardApiFetch< Gate >( - { - path: `/newspack/v1/wizard/${ slug }`, - method: 'POST', - data: { gate: _gate }, - }, - { - onSuccess( data ) { - updateGatesData( [ ...gatesRef.current, { ...data } ] ); - history.push( `/content-gates` ); - addNotice( { - // translators: %s is the gate title. - message: sprintf( __( '"%s" gate created.', 'newspack-plugin' ), title ), - type: 'success', - id: 'content-gate-created', - actions: [ { label: __( 'Edit', 'newspack-plugin' ), url: `#/edit/${ data.id }` } ], - } ); - }, - onFinally: () => { - isSaving.current = false; - }, + const handleCreate = useCallback( + ( redirectToLayout: '' | 'registration' | 'custom_access' = '' ) => { + if ( isFetching ) { + return; } - ); - }, [ gate, contentRules, registration, customAccess, status, title ] ); + isSaving.current = true; + resetNotices(); + resetError(); + const _gate = { + ...gate, + title, + content_rules: contentRules, + registration, + custom_access: customAccess, + }; + wizardApiFetch< Gate >( + { + path: `/newspack/v1/wizard/${ slug }`, + method: 'POST', + data: { gate: _gate }, + }, + { + onSuccess( data ) { + updateGatesData( [ ...gatesRef.current, { ...data } ] ); + if ( redirectToLayout !== '' ) { + window.location.assign( getEditGateLayoutUrl( data.id, redirectToLayout ) ); + } else { + history.push( '/content-gates' ); + } + addNotice( { + // translators: %s is the gate title. + message: sprintf( __( '"%s" gate created.', 'newspack-plugin' ), title ), + type: 'success', + id: 'content-gate-created', + actions: [ { label: __( 'Edit', 'newspack-plugin' ), url: `#/edit/${ data.id }` } ], + } ); + }, + onFinally: () => { + isSaving.current = false; + }, + } + ); + }, + [ gate, contentRules, registration, customAccess, status, title ] + ); const handleSave = useCallback( () => { if ( isFetching ) { @@ -551,10 +558,12 @@ const Edit = ( { match, updateGatesData, slug = AUDIENCE_CONTENT_GATES_WIZARD_SL isNewsletter ? __( 'these lists', 'newspack-plugin' ) : __( 'this content', 'newspack-plugin' ) ) } headerAction={ - registration?.active && registration.gate_layout_id + registration?.active ? { label: __( 'Edit layout', 'newspack-plugin' ), - href: getEditGateLayoutUrl( gate.id, 'registration' ), + href: + ! isNew && registration.gate_layout_id ? getEditGateLayoutUrl( gate.id, 'registration' ) : undefined, + onClick: ! isNew && registration.gate_layout_id ? undefined : () => handleCreate( 'registration' ), icon: pencil, } : undefined @@ -577,7 +586,8 @@ const Edit = ( { match, updateGatesData, slug = AUDIENCE_CONTENT_GATES_WIZARD_SL customAccess?.active ? { label: __( 'Edit layout', 'newspack-plugin' ), - href: getEditGateLayoutUrl( gate.id, 'custom_access' ), + href: ! isNew && customAccess.gate_layout_id ? getEditGateLayoutUrl( gate.id, 'custom_access' ) : undefined, + onClick: ! isNew && customAccess.gate_layout_id ? undefined : () => handleCreate( 'custom_access' ), icon: pencil, } : undefined From f9a57a05ebdd2eabdc7f0ebb0a8f3b8fa3e717b9 Mon Sep 17 00:00:00 2001 From: Derrick Koo Date: Thu, 16 Apr 2026 13:31:51 -0600 Subject: [PATCH 6/7] Update packages/components/src/card/core-card.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/components/src/card/core-card.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/card/core-card.js b/packages/components/src/card/core-card.js index 5f27c85f2e..4acbdbce62 100644 --- a/packages/components/src/card/core-card.js +++ b/packages/components/src/card/core-card.js @@ -185,7 +185,7 @@ const CoreCard = ( { icon={ headerAction.icon } href={ headerAction.href } disabled={ headerAction.disabled || false } - destructive={ headerAction.destructive || false } + isDestructive={ headerAction.destructive || false } onClick={ headerAction.onClick } tone={ headerAction.tone || 'primary' } variant={ headerAction.variant || 'secondary' } From 60505d9ed7ccc55321d773145037babe72a95ca6 Mon Sep 17 00:00:00 2001 From: Derrick Koo Date: Thu, 16 Apr 2026 13:32:29 -0600 Subject: [PATCH 7/7] Update packages/components/src/card/style-core.scss Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/components/src/card/style-core.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/card/style-core.scss b/packages/components/src/card/style-core.scss index d3342ba3cd..641b8513eb 100644 --- a/packages/components/src/card/style-core.scss +++ b/packages/components/src/card/style-core.scss @@ -81,7 +81,7 @@ top: 0; } &__action { - background-color:wp-colors.$white; + background-color: wp-colors.$white; flex: 1 100%; justify-content: center !important; }