Added multiple subscriptions member filter#28232
Conversation
WalkthroughThis PR introduces support for filtering and analyzing members by multiple active Stripe customer subscriptions. It registers a new labs feature flag, implements backend API validation and database query logic for the Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ast-grep (0.43.0)ghost/core/test/e2e-api/admin/members.test.js[] Comment |
ff56765 to
c973a4c
Compare
no issue This adds a labs-gated warning and exact members filter for identifying members with active subscriptions across multiple Stripe customers.
c973a4c to
36b2594
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 36b2594879
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| browse(apiConfig, frame) { | ||
| debug('browse'); | ||
| defaultRelations(frame); | ||
| extractActiveStripeCustomersCountFilter(frame); |
There was a problem hiding this comment.
Handle the custom filter for bulk member actions
Because the new count.active_stripe_customers:>1 filter is only extracted in the browse/exportCSV path, viewing the banner filter and then using the existing actions menu to add/remove a label or unsubscribe sends that raw filter to PUT /members/bulk. The bulk serializer never sets activeStripeCustomersCount, and member-repository.bulkEdit passes options.filter straight into getFilteredCollectionQuery, so those actions cannot apply to the listed members. Either translate the filter in bulkEdit as well or disable those actions for this raw-filter view.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/posts/src/views/members/multiple-active-stripe-customers.ts (1)
42-58: ⚡ Quick winConsider reducing redundant parsing calls.
The function calls
parseAccessibilityPreferences(accessibility)at line 47, then callsgetMultipleActiveStripeCustomersBannerPreference(accessibility)at line 48, which internally parses the same string again. While this doesn't affect correctness, it's inefficient.♻️ Suggested refactor to parse once
export function buildDismissedMultipleActiveStripeCustomersPreference( accessibility: string | null | undefined, dismissedCount: number, dismissedAt: string ): string { const preferences = parseAccessibilityPreferences(accessibility); - const currentBannerPreference = getMultipleActiveStripeCustomersBannerPreference(accessibility); + const currentBannerPreference = preferences.multipleActiveStripeCustomersBanner ?? {}; return JSON.stringify({ ...preferences, multipleActiveStripeCustomersBanner: { ...currentBannerPreference, dismissedCount, dismissedAt } }); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/posts/src/views/members/multiple-active-stripe-customers.ts` around lines 42 - 58, The function buildDismissedMultipleActiveStripeCustomersPreference is calling parseAccessibilityPreferences(accessibility) and then getMultipleActiveStripeCustomersBannerPreference(accessibility), which causes the accessibility string to be parsed twice; refactor to parse once by calling parseAccessibilityPreferences(accessibility) into a local variable and extract the existing multipleActiveStripeCustomersBanner preference from that parsed object (instead of calling getMultipleActiveStripeCustomersBannerPreference with the raw string), then merge dismissedCount/dismissedAt and JSON.stringify the result; update references to parseAccessibilityPreferences and getMultipleActiveStripeCustomersBannerPreference accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/posts/src/views/members/members.tsx`:
- Around line 312-314: The banner text in members.tsx uses a fixed verb "were
found" which breaks subject-verb agreement when
multipleActiveStripeCustomersCount === 1; update the JSX inside the div
rendering {formatNumber(multipleActiveStripeCustomersCount)} ... so the verb is
conditional (use "was found" when multipleActiveStripeCustomersCount === 1,
otherwise "were found") while keeping the existing pluralization for
"member/members" based on multipleActiveStripeCustomersCount.
---
Nitpick comments:
In `@apps/posts/src/views/members/multiple-active-stripe-customers.ts`:
- Around line 42-58: The function
buildDismissedMultipleActiveStripeCustomersPreference is calling
parseAccessibilityPreferences(accessibility) and then
getMultipleActiveStripeCustomersBannerPreference(accessibility), which causes
the accessibility string to be parsed twice; refactor to parse once by calling
parseAccessibilityPreferences(accessibility) into a local variable and extract
the existing multipleActiveStripeCustomersBanner preference from that parsed
object (instead of calling getMultipleActiveStripeCustomersBannerPreference with
the raw string), then merge dismissedCount/dismissedAt and JSON.stringify the
result; update references to parseAccessibilityPreferences and
getMultipleActiveStripeCustomersBannerPreference accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2fde2b30-2393-4796-a326-805aa2d2dddc
⛔ Files ignored due to path filters (1)
ghost/core/test/e2e-api/admin/__snapshots__/config.test.js.snapis excluded by!**/*.snap
📒 Files selected for processing (11)
apps/admin-x-settings/src/components/settings/advanced/labs/private-features.tsxapps/posts/src/views/members/components/members-actions.tsxapps/posts/src/views/members/hooks/use-members-filter-state.test.tsxapps/posts/src/views/members/hooks/use-members-filter-state.tsapps/posts/src/views/members/members.tsxapps/posts/src/views/members/multiple-active-stripe-customers.test.tsapps/posts/src/views/members/multiple-active-stripe-customers.tsghost/core/core/server/api/endpoints/utils/serializers/input/members.jsghost/core/core/server/models/member.jsghost/core/core/shared/labs.jsghost/core/test/e2e-api/admin/members.test.js
| <div className="text-sm font-medium"> | ||
| {formatNumber(multipleActiveStripeCustomersCount)} {multipleActiveStripeCustomersCount === 1 ? 'member' : 'members'} with active subscriptions across multiple Stripe customers were found. | ||
| </div> |
There was a problem hiding this comment.
Fix subject-verb agreement when count is 1.
When multipleActiveStripeCustomersCount === 1, the banner reads "1 member ... were found". The verb should also be pluralized conditionally.
✏️ Proposed fix
- {formatNumber(multipleActiveStripeCustomersCount)} {multipleActiveStripeCustomersCount === 1 ? 'member' : 'members'} with active subscriptions across multiple Stripe customers were found.
+ {formatNumber(multipleActiveStripeCustomersCount)} {multipleActiveStripeCustomersCount === 1 ? 'member' : 'members'} with active subscriptions across multiple Stripe customers {multipleActiveStripeCustomersCount === 1 ? 'was' : 'were'} found.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div className="text-sm font-medium"> | |
| {formatNumber(multipleActiveStripeCustomersCount)} {multipleActiveStripeCustomersCount === 1 ? 'member' : 'members'} with active subscriptions across multiple Stripe customers were found. | |
| </div> | |
| <div className="text-sm font-medium"> | |
| {formatNumber(multipleActiveStripeCustomersCount)} {multipleActiveStripeCustomersCount === 1 ? 'member' : 'members'} with active subscriptions across multiple Stripe customers {multipleActiveStripeCustomersCount === 1 ? 'was' : 'were'} found. | |
| </div> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/posts/src/views/members/members.tsx` around lines 312 - 314, The banner
text in members.tsx uses a fixed verb "were found" which breaks subject-verb
agreement when multipleActiveStripeCustomersCount === 1; update the JSX inside
the div rendering {formatNumber(multipleActiveStripeCustomersCount)} ... so the
verb is conditional (use "was found" when multipleActiveStripeCustomersCount ===
1, otherwise "were found") while keeping the existing pluralization for
"member/members" based on multipleActiveStripeCustomersCount.
Summary