Skip to content

Added gift links admin UI for analytics, posts list, and settings#28897

Merged
jonatansberg merged 6 commits into
mainfrom
jonatan-ber-3729-gift-links-admin-ui-editor-settings
Jun 30, 2026
Merged

Added gift links admin UI for analytics, posts list, and settings#28897
jonatansberg merged 6 commits into
mainfrom
jonatan-ber-3729-gift-links-admin-ui-editor-settings

Conversation

@jonatansberg

Copy link
Copy Markdown
Member

ref https://linear.app/ghost/issue/BER-3729

Summary

Adds the publisher-facing gift-links admin UI on top of the service + admin API already on main. A single React gift-link modal is reused across surfaces instead of maintaining a separate Ember modal:

  • Post analytics screen — a "Share as a gift" entry in the post share modal opens the modal.
  • Ember posts/pages list — the right-click context menu fires an openGiftLinkModal event over the state bridge; a host mounted alongside the Ember fallback (at the /posts and /pages routes) opens the React modal in place.
  • Settings → Advanced → Danger zone — a "Reset all gift links" action.

All gated behind the existing private giftLinks flag.

Notes

  • The modal makes two separate reads: link details (admin API gift_links) and usage (visits/views via the same Tinybird analytics path as every other analytics surface). Usage degrades gracefully — when analytics is off, or the per-link usage pipe (BER-3746/BER-3728) isn't deployed yet, the visitor count is simply hidden and everything else still works.
  • Share URL is the canonical post URL + ?gift=<token> (no utm).
  • Posts reuse the post-analytics screen's cached query (shared POST_ANALYTICS_INCLUDE); pages fetch on their own route. API wrapper names follow the backend controllers (ensure / create / removeAll).

Testing

  • apps/posts unit suite (482) green; new URL-builder unit test; Ember eligibility-util unit test.
  • admin-x-settings danger-zone acceptance test for reset-all (passing locally).
  • Typecheck + lint clean across posts, admin, shade, admin-x-framework, admin-x-settings, and ghost/admin.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR adds gift-link management across admin and posts. It introduces gift-link API client mutations, a settings action to reset all gift links, an Ember-to-React bridge for opening a React gift-link modal from posts and pages lists, and new posts-app hooks, utilities, modal UI, and sharing entry points. It also adds related tests, package exports, and route updates.

Possibly related PRs

  • TryGhost/Ghost#28784 — Adds the admin API client surface for gift-link operations, including resource-scoped paths and remove-all handling.
  • TryGhost/Ghost#28693 — Adds backend gift-link routes and response shapes that match the client mutations used here.
  • TryGhost/Ghost#28837 — Covers the remove-all gift-link endpoint and naming changes used by the admin reset flow.

Suggested reviewers

  • kevinansfield
  • EvanHahn
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding gift links admin UI across analytics, posts list, and settings.
Description check ✅ Passed The description matches the implemented gift-links UI work and accurately summarizes the affected surfaces.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jonatan-ber-3729-gift-links-admin-ui-editor-settings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nx-cloud

nx-cloud Bot commented Jun 25, 2026

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit 316fc37

Command Status Duration Result
nx run @tryghost/admin-x-settings:test:acceptance ✅ Succeeded 9m 58s View ↗
nx run-many --target=build --projects=tag:publi... ✅ Succeeded 1s View ↗
nx run-many -t test:unit -p @tryghost/admin-x-f... ✅ Succeeded 6m 50s View ↗
nx run @tryghost/admin:build ✅ Succeeded 4m View ↗
nx run ghost-admin:test ✅ Succeeded 2m 44s View ↗
nx run @tryghost/activitypub:test:acceptance ✅ Succeeded 37s View ↗
nx run-many -t lint -p @tryghost/admin-x-framew... ✅ Succeeded 19s View ↗
nx run ghost:build:assets ✅ Succeeded 2s View ↗
nx run ghost:build:tsc ✅ Succeeded 6s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-06-30 07:17:55 UTC

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/posts/src/utils/gift-link.ts (1)

7-12: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Edge case: hard-coded ? breaks URLs that already carry a query string.

buildGiftLinkUrl always prefixes with ?, so a postUrl already containing a query string would yield a malformed URL with two ?. Canonical post URLs are normally clean, so this is just defensive hardening.

♻️ Use the correct separator
-    return `${postUrl}?gift=${encodeURIComponent(token)}`;
+    const separator = postUrl.includes('?') ? '&' : '?';
+    return `${postUrl}${separator}gift=${encodeURIComponent(token)}`;
🤖 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/utils/gift-link.ts` around lines 7 - 12, The buildGiftLinkUrl
helper always appends the gift token with a hard-coded query separator, which
breaks when postUrl already contains existing query parameters. Update
buildGiftLinkUrl to choose the correct separator based on whether postUrl
already includes a query string, and keep the token encoding behavior unchanged.
🤖 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/PostAnalytics/modals/gift-link-modal.tsx`:
- Around line 135-143: Disable the ShareModal.CopyButton in gift-link-modal
while the link is being generated so it cannot copy an empty string. Update the
copy button in the gift-link CopyURLBox to use the existing ensuring state from
the modal logic, and keep the disabled state aligned with the same
giftLinkUrl/ensuring flow used in this component.

---

Nitpick comments:
In `@apps/posts/src/utils/gift-link.ts`:
- Around line 7-12: The buildGiftLinkUrl helper always appends the gift token
with a hard-coded query separator, which breaks when postUrl already contains
existing query parameters. Update buildGiftLinkUrl to choose the correct
separator based on whether postUrl already includes a query string, and keep the
token encoding behavior unchanged.
🪄 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: 9397fe1c-217a-4d38-88dc-8ce6dd3e7870

📥 Commits

Reviewing files that changed from the base of the PR and between 515c39b and e4e3aba.

📒 Files selected for processing (24)
  • apps/admin-x-framework/src/api/gift-links.ts
  • apps/admin-x-settings/src/components/settings/advanced/danger-zone.tsx
  • apps/admin-x-settings/test/acceptance/advanced/dangerzone.test.ts
  • apps/admin/src/ember-bridge/ember-bridge.tsx
  • apps/admin/src/ember-bridge/index.ts
  • apps/admin/src/gift-link-modal-host.tsx
  • apps/admin/src/routes.tsx
  • apps/posts/package.json
  • apps/posts/src/hooks/use-can-manage-gift-link.ts
  • apps/posts/src/hooks/use-gift-link-usage.ts
  • apps/posts/src/hooks/use-post-details.ts
  • apps/posts/src/providers/post-analytics-context.tsx
  • apps/posts/src/utils/constants.ts
  • apps/posts/src/utils/gift-link.ts
  • apps/posts/src/views/PostAnalytics/components/post-analytics-header.tsx
  • apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx
  • apps/posts/test/unit/utils/gift-link.test.ts
  • apps/shade/src/components/posts-stats/post-share-modal.tsx
  • ghost/admin/app/components/posts-list/context-menu.hbs
  • ghost/admin/app/components/posts-list/context-menu.js
  • ghost/admin/app/services/feature.js
  • ghost/admin/app/services/state-bridge.js
  • ghost/admin/app/utils/gift-link.js
  • ghost/admin/tests/unit/utils/gift-link-test.js

Comment thread apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e4e3aba258

ℹ️ 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".

Comment thread apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx
@jonatansberg jonatansberg force-pushed the jonatan-ber-3729-gift-links-admin-ui-editor-settings branch 3 times, most recently from cd8049c to 0f9b693 Compare June 25, 2026 14:30

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0f9b6932f5

ℹ️ 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".

Comment thread apps/posts/src/hooks/use-gift-link-usage.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 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/PostAnalytics/Overview/overview.tsx`:
- Around line 218-225: The “Share” button in PostAnalytics/Overview is only
revealed on hover, so it stays hidden for keyboard and touch users. Update the
Button in the relevant render path to also reveal on focus-visible (mirroring
the existing Growth “View more” behavior if applicable), while keeping the
current hover animation intact. Use the Button element and its existing
className/variant setup as the reference point, and apply the same accessibility
fix wherever the same hover-only pattern appears.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 718f5ac2-0d5e-458f-b497-8aa983b49480

📥 Commits

Reviewing files that changed from the base of the PR and between cd8049c and 0f9b693.

📒 Files selected for processing (25)
  • apps/admin-x-framework/src/api/gift-links.ts
  • apps/admin-x-settings/src/components/settings/advanced/danger-zone.tsx
  • apps/admin-x-settings/test/acceptance/advanced/dangerzone.test.ts
  • apps/admin/src/ember-bridge/ember-bridge.tsx
  • apps/admin/src/ember-bridge/index.ts
  • apps/admin/src/gift-link-modal-host.tsx
  • apps/admin/src/routes.tsx
  • apps/posts/package.json
  • apps/posts/src/hooks/use-can-manage-gift-link.ts
  • apps/posts/src/hooks/use-gift-link-usage.ts
  • apps/posts/src/hooks/use-post-details.ts
  • apps/posts/src/providers/post-analytics-context.tsx
  • apps/posts/src/utils/constants.ts
  • apps/posts/src/utils/gift-link.ts
  • apps/posts/src/views/PostAnalytics/Overview/overview.tsx
  • apps/posts/src/views/PostAnalytics/components/post-analytics-header.tsx
  • apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx
  • apps/posts/test/unit/utils/gift-link.test.ts
  • apps/shade/src/components/posts-stats/post-share-modal.tsx
  • ghost/admin/app/components/posts-list/context-menu.hbs
  • ghost/admin/app/components/posts-list/context-menu.js
  • ghost/admin/app/services/feature.js
  • ghost/admin/app/services/state-bridge.js
  • ghost/admin/app/utils/gift-link.js
  • ghost/admin/tests/unit/utils/gift-link-test.js
✅ Files skipped from review due to trivial changes (3)
  • apps/admin/src/ember-bridge/index.ts
  • apps/posts/src/providers/post-analytics-context.tsx
  • apps/posts/test/unit/utils/gift-link.test.ts
🚧 Files skipped from review as they are similar to previous changes (21)
  • apps/admin-x-settings/test/acceptance/advanced/dangerzone.test.ts
  • apps/posts/src/utils/gift-link.ts
  • apps/posts/package.json
  • apps/posts/src/hooks/use-can-manage-gift-link.ts
  • ghost/admin/app/components/posts-list/context-menu.hbs
  • ghost/admin/app/services/feature.js
  • apps/admin/src/routes.tsx
  • apps/admin/src/ember-bridge/ember-bridge.tsx
  • apps/admin-x-framework/src/api/gift-links.ts
  • apps/posts/src/hooks/use-post-details.ts
  • apps/posts/src/views/PostAnalytics/components/post-analytics-header.tsx
  • apps/posts/src/utils/constants.ts
  • ghost/admin/app/utils/gift-link.js
  • ghost/admin/app/components/posts-list/context-menu.js
  • apps/shade/src/components/posts-stats/post-share-modal.tsx
  • ghost/admin/app/services/state-bridge.js
  • ghost/admin/tests/unit/utils/gift-link-test.js
  • apps/posts/src/hooks/use-gift-link-usage.ts
  • apps/admin-x-settings/src/components/settings/advanced/danger-zone.tsx
  • apps/admin/src/gift-link-modal-host.tsx
  • apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx

Comment thread apps/posts/src/views/PostAnalytics/Overview/overview.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 143b521979

ℹ️ 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".

Comment thread apps/posts/src/views/PostAnalytics/Overview/overview.tsx Outdated
Comment thread apps/admin-x-framework/src/api/gift-links.ts Outdated
const handleRemoveAllGiftLinks = () => {
NiceModal.show(ConfirmationModal, {
title: 'Reset all gift links?',
prompt: 'This immediately invalidates every active gift link across your site. Anyone holding one will lose access. New gift links can still be created afterwards.',

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weylandswart some wordsmithing required here?

<ListItem
action={<Button aria-label='Reset all gift links' color='red' label='Reset' onClick={handleRemoveAllGiftLinks} />}
bgOnHover={false}
detail='Invalidate every active gift link across your site. Anyone holding one will lose access.'

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weylandswart same here

Comment thread apps/admin/src/gift-link-modal-host.tsx Outdated
Comment thread apps/admin/src/routes.tsx Outdated
Comment thread apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx Outdated
Comment thread apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx Outdated
Comment thread apps/posts/src/views/PostAnalytics/modals/gift-link-modal.tsx Outdated
Comment thread apps/posts/src/views/PostAnalytics/Overview/overview.tsx Outdated
Comment thread ghost/admin/app/utils/gift-link.js Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 39b501f49f

ℹ️ 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".

Comment thread apps/admin-x-framework/src/api/users.ts
jonatansberg and others added 4 commits June 29, 2026 15:48
ref https://linear.app/ghost/issue/BER-3729

- one shared React gift-link modal is reused across the post-analytics
  screen and the Ember posts/pages list, rather than maintaining a
  separate Ember modal: the list's right-click menu fires an
  `openGiftLinkModal` event over the state bridge and a host mounted
  alongside the Ember fallback opens the React modal in place
- the modal fetches link details (admin API) and usage (the same
  Tinybird analytics path as everything else) separately, degrading to
  no visitor count when analytics is off or the usage pipe isn't
  deployed yet; the share URL is the canonical post URL + `?gift=<token>`
- adds the danger-zone "reset all gift links" action and a "share as a
  gift" entry in the post share modal
- all gated behind the existing private `giftLinks` flag
ref https://linear.app/ghost/issue/BER-3729

- dropped the extra flex wrapper around ShareModal.Footer + the gift link
- ShareModal.Content (a DialogContent) already lays its children out in a
  grid with gap-6, so the wrapper only duplicated that spacing (with an
  inconsistent gap-5) and broke the pattern where each ShareModal.* section
  is a direct child of Content
- Narrowed gift-link reads via useActiveGiftLink so callers get the token
  directly instead of indexing the response array
- Routed useReadGiftLink through the giftLinkPath helper
- Colocated the role check as canManageGiftLinks in the users API and
  dropped Author from gift-link eligibility (React + Ember)
- Added visibility/uuid to the Page type and dropped the widening cast
- Replaced the binary access-label ternary with a visibility-keyed map
- Keyed the gift-link modal for reset instead of an on-close effect, and
  memoized its handlers with useCallback
- Renamed the modal host's target state to entry; trimmed stale comments
@jonatansberg jonatansberg force-pushed the jonatan-ber-3729-gift-links-admin-ui-editor-settings branch from 39b501f to e027100 Compare June 29, 2026 13:58

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e0271005ba

ℹ️ 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".

When analytics is off or the usage query errors, useGiftLinkUsage returns
undefined to distinguish 'unavailable' from a real zero. Rather than coercing
that to 0 (or showing a meaningless placeholder), hide the visitor metric until
the count is known, matching how the modal hides its badge.
@jonatansberg jonatansberg force-pushed the jonatan-ber-3729-gift-links-admin-ui-editor-settings branch from 316fc37 to 18e6135 Compare June 30, 2026 07:02
The Growth 'View more' and Gift link 'Share' buttons were opacity-0 until
hover, leaving them focusable but invisible for keyboard users and unreachable
on touch. Reveal them on focus-visible too.
@jonatansberg jonatansberg merged commit d6c0cc7 into main Jun 30, 2026
41 checks passed
@jonatansberg jonatansberg deleted the jonatan-ber-3729-gift-links-admin-ui-editor-settings branch June 30, 2026 08:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants