Skip to content

feat: customize sponsor managed pages#841

Open
tomrndom wants to merge 6 commits intomasterfrom
feature/sponsor-managed-page-customize
Open

feat: customize sponsor managed pages#841
tomrndom wants to merge 6 commits intomasterfrom
feature/sponsor-managed-page-customize

Conversation

@tomrndom
Copy link

@tomrndom tomrndom commented Mar 26, 2026

ref: https://app.clickup.com/t/86b7991c7

Signed-off-by: Tomás Castillo tcastilloboireau@gmail.com

Summary by CodeRabbit

  • New Features

    • Added a "Customize" column and flow to open a managed-page-specific popup, fetch single managed pages, and save customized managed pages.
    • Popup accepts an optional title and switches save handling by mode.
  • Bug Fixes

    • Fixed add-on "select all" normalization and ensured upload-deadline times show correctly in edit forms.
  • Documentation

    • Updated success messages to distinguish page creation vs. save.
  • Tests

    • Added tests for the managed-page customize flow, save behavior, and list refreshes.

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
…oad tables after save

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aaaf6614-e95c-4032-a977-a7ef75d5d227

📥 Commits

Reviewing files that changed from the base of the PR and between c478b9e and 2b437c0.

📒 Files selected for processing (1)
  • src/pages/sponsors/sponsor-pages-tab/index.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/sponsors/sponsor-pages-tab/index.js

📝 Walkthrough

Walkthrough

Adds single-managed-page fetch and update thunks, splits create vs update save flows with a new normalize-to-customize payload, surfaces a “Customize” UI flow and popup title prop, updates i18n keys, and extends tests to cover the customize flow.

Changes

Cohort / File(s) Summary
Redux actions & normalization
src/actions/sponsor-pages-actions.js
Added RECEIVE_SPONSOR_MANAGED_PAGE, SPONSOR_MANAGED_PAGE_UPDATED; new thunk getSponsorManagedPage(pageId); refactored getSponsorManagedPages/getSponsorCustomizedPages; saveSponsorManagedPage now branches update (PUT with normalizeSponsorManagedPageToCustomize, dispatch SPONSOR_CUSTOMIZED_PAGE_UPDATED) vs create (POST); added normalizeSponsorManagedPageToCustomize; adjusted normalizeSponsorManagedPage select-all check.
Reducer
src/reducers/sponsors/sponsor-page-pages-list-reducer.js
Handles RECEIVE_SPONSOR_MANAGED_PAGE to set currentEditPage from payload and converts module upload_deadline via epochToMomentTimeZone; minor refactor for RECEIVE_SPONSOR_CUSTOMIZED_PAGE.
UI / Sponsor Pages tab
src/pages/sponsors/sponsor-pages-tab/index.js
Imported/bound getSponsorManagedPage; added “Customize” column with ArrowForward button; handleManagedEdit fetches single managed page then opens managedPagePopup; popup switching between managed/customized modes; added handleSaveManagedPage to save then refresh both lists and close popup; changed new-page popup choice to customizedPagePopup.
Popup component
src/pages/sponsors-global/page-templates/page-template-popup/index.js
PageTemplatePopup accepts optional title prop used for dialog header; propTypes updated to include title.
i18n
src/i18n/en.json
Updated sponsor.pages_tab keys: added/adjusted managed_page_saved, managed_page_created, custom_page_saved, custom_page_created, added title_customize; split page_template_list.page_crud.title into title_create / title_edit.
Tests
src/pages/sponsors/sponsor-pages-tab/__tests__/sponsor-pages-tab.test.js
Extended mocks/tests to cover managed-page customize flow: render “Customize” button/icon, ensure getSponsorManagedPage called and popup opens, verify saveSponsorManagedPage triggers refreshes for managed/customized lists and closes popup.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant UI as "Sponsor Pages UI"
    participant Redux as "Actions / Thunks"
    participant API as "API Server"
    participant Store as "Redux Store"

    User->>UI: Click "Customize" on managed page
    UI->>Redux: dispatch getSponsorManagedPage(pageId)
    Redux->>API: GET /show-pages/{pageId}?expand=modules
    API-->>Redux: managed page + modules
    Redux->>Store: dispatch RECEIVE_SPONSOR_MANAGED_PAGE
    Store-->>UI: currentEditPage (deadlines converted)
    UI->>User: open managedPagePopup with data
    User->>UI: Edit and Save
    UI->>Redux: dispatch saveSponsorManagedPage(entity)
    Redux->>Redux: normalizeSponsorManagedPageToCustomize(entity)
    Redux->>API: PUT /show-pages/{pageId}
    API-->>Redux: success
    Redux->>Store: dispatch SPONSOR_CUSTOMIZED_PAGE_UPDATED
    Redux->>Redux: dispatch getSponsorManagedPages & getSponsorCustomizedPages (refresh)
    UI->>UI: close popup and refresh lists
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • romanetar
  • smarcet

"🐰 A Customizing Hop"

I fetched a page with nimble paws,
Converted deadlines, trimmed the flaws,
Modules sorted, payloads neat,
A Customize click — what a treat!
Saved, refreshed, and hopped to my logs. 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main feature being added: the ability to customize sponsor managed pages, which aligns with all substantial changes across the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/sponsor-managed-page-customize

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/actions/sponsor-pages-actions.js (1)

282-293: ⚠️ Potential issue | 🔴 Critical

normalizeSponsorManagedPage now throws before the create request.

Line 287 calls includes on normalizedEntity.allowed_add_ons, but that field is never initialized here. The “add managed page from template” path will fail with a TypeError before the POST is sent.

Proposed fix
 const normalizeSponsorManagedPage = (entity) => {
   const normalizedEntity = {
     show_page_ids: entity.pages
   };
+  const selectedAddOns = entity.add_ons ?? [];
 
-  if (normalizedEntity.allowed_add_ons.includes("all")) {
+  if (selectedAddOns.includes("all")) {
     normalizedEntity.apply_to_all_add_ons = true;
     normalizedEntity.allowed_add_ons = [];
   } else {
-    normalizedEntity.allowed_add_ons = entity.add_ons.map((a) => a.id);
+    normalizedEntity.allowed_add_ons = selectedAddOns.map((a) => a.id ?? a);
     normalizedEntity.apply_to_all_add_ons = false;
   }
 
   return normalizedEntity;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/actions/sponsor-pages-actions.js` around lines 282 - 293,
normalizeSponsorManagedPage currently calls
normalizedEntity.allowed_add_ons.includes(...) before allowed_add_ons is
initialized; fix by initializing allowed_add_ons from entity.add_ons (e.g.,
normalizedEntity.allowed_add_ons = (entity.add_ons || []).map(a => a.id)) before
the if-check, then perform the includes("all") test and set apply_to_all_add_ons
and allowed_add_ons (to [] when "all" is present) accordingly; ensure you handle
a missing/null entity.add_ons by defaulting to an empty array so the
add-managed-page flow doesn’t throw.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/actions/sponsor-pages-actions.js`:
- Around line 298-305: The call to normalizeSelectAllField inside
normalizeSponsorManagedPageToCustomize is passing the wrong flag name
("apply_to_all_types"), causing the companion flag for allowed_add_ons to be
emitted incorrectly; change the third argument to "apply_to_all_add_ons" so
normalizeSelectAllField(serialization of allowed_add_ons) writes the expected
apply_to_all_add_ons flag (update the call in
normalizeSponsorManagedPageToCustomize that currently references
allowed_add_ons).

In `@src/pages/sponsors/sponsor-pages-tab/index.js`:
- Around line 274-292: The popup is being closed in the .finally block causing
edits to be lost on save failure and skipping cleanup; change the flow in
handleSaveManagedPage so setOpenPopup(null) is called only on successful save
(inside the .then chain after refresh calls) and also invoke
handleClosePagePopup() there to clear currentEditPage, while leaving the popup
open on .catch so the user can correct errors; reference saveSponsorManagedPage,
getSponsorManagedPages, getSponsorCustomizedPages, setOpenPopup, and
handleClosePagePopup when making this change.

---

Outside diff comments:
In `@src/actions/sponsor-pages-actions.js`:
- Around line 282-293: normalizeSponsorManagedPage currently calls
normalizedEntity.allowed_add_ons.includes(...) before allowed_add_ons is
initialized; fix by initializing allowed_add_ons from entity.add_ons (e.g.,
normalizedEntity.allowed_add_ons = (entity.add_ons || []).map(a => a.id)) before
the if-check, then perform the includes("all") test and set apply_to_all_add_ons
and allowed_add_ons (to [] when "all" is present) accordingly; ensure you handle
a missing/null entity.add_ons by defaulting to an empty array so the
add-managed-page flow doesn’t throw.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 76ed2285-9141-418e-bda7-94bd694fbb28

📥 Commits

Reviewing files that changed from the base of the PR and between af9800a and a5d6d41.

📒 Files selected for processing (4)
  • src/actions/sponsor-pages-actions.js
  • src/i18n/en.json
  • src/pages/sponsors/sponsor-pages-tab/index.js
  • src/reducers/sponsors/sponsor-page-pages-list-reducer.js

Comment on lines +274 to +292
const handleSaveManagedPage = (entity) => {
saveSponsorManagedPage(entity)
.then(() => {
getSponsorManagedPages(
term,
DEFAULT_CURRENT_PAGE,
managedPages.perPage,
managedPages.order,
managedPages.orderDir
);
getSponsorCustomizedPages(
term,
DEFAULT_CURRENT_PAGE,
customizedPages.perPage,
customizedPages.order,
customizedPages.orderDir
);
})
.finally(() => setOpenPopup(null));
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't close the managed-page editor in finally.

Line 292 closes the dialog even when saveSponsorManagedPage fails, which drops the user's edits. It also skips handleClosePagePopup, so currentEditPage stays populated and can leak into the next “New Page” open.

Proposed fix
   const handleSaveManagedPage = (entity) => {
     saveSponsorManagedPage(entity)
       .then(() => {
         getSponsorManagedPages(
           term,
           DEFAULT_CURRENT_PAGE,
           managedPages.perPage,
           managedPages.order,
           managedPages.orderDir
         );
         getSponsorCustomizedPages(
           term,
           DEFAULT_CURRENT_PAGE,
           customizedPages.perPage,
           customizedPages.order,
           customizedPages.orderDir
         );
-      })
-      .finally(() => setOpenPopup(null));
+        handleClosePagePopup();
+      });
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-pages-tab/index.js` around lines 274 - 292, The
popup is being closed in the .finally block causing edits to be lost on save
failure and skipping cleanup; change the flow in handleSaveManagedPage so
setOpenPopup(null) is called only on successful save (inside the .then chain
after refresh calls) and also invoke handleClosePagePopup() there to clear
currentEditPage, while leaving the popup open on .catch so the user can correct
errors; reference saveSponsorManagedPage, getSponsorManagedPages,
getSponsorCustomizedPages, setOpenPopup, and handleClosePagePopup when making
this change.

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Copy link

@coderabbitai coderabbitai bot left a comment

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 (2)
src/pages/sponsors/sponsor-pages-tab/index.js (1)

199-200: Remove the temporary console log.

console.log("CHECK!", item) will run on every Customize click and dump the row payload into the browser console.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-pages-tab/index.js` around lines 199 - 200, Remove
the temporary console.log("CHECK!", item) that prints the row payload on every
Customize click; inside the click handler where
getSponsorManagedPage(item.id).then(() => setOpenPopup("managedPagePopup")) is
invoked, delete the console.log statement (or replace it with a controlled
debug/logging call if persistent logging is required) so only
getSponsorManagedPage and setOpenPopup remain.
src/pages/sponsors/sponsor-pages-tab/__tests__/sponsor-pages-tab.test.js (1)

305-307: Assert the refresh arguments, not only the call counts.

These expectations still pass if the save handler rebuilds either grid with the wrong filters or pagination. Verifying the last getSponsorManagedPages / getSponsorCustomizedPages arguments here would catch the missing hideArchived regression in handleSaveManagedPage.

Example assertion upgrade
       await waitFor(() => {
-        expect(getSponsorManagedPages).toHaveBeenCalledTimes(2);
-        expect(getSponsorCustomizedPages).toHaveBeenCalledTimes(2);
+        expect(getSponsorManagedPages).toHaveBeenLastCalledWith(
+          "",
+          1,
+          10,
+          "id",
+          1,
+          false
+        );
+        expect(getSponsorCustomizedPages).toHaveBeenLastCalledWith(
+          "",
+          1,
+          10,
+          "id",
+          1,
+          false
+        );
       });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-pages-tab/__tests__/sponsor-pages-tab.test.js`
around lines 305 - 307, The test currently only checks call counts for
getSponsorManagedPages and getSponsorCustomizedPages; change it to assert the
last call arguments to ensure the correct filters/pagination are passed (e.g.,
verify that the last call to getSponsorManagedPages and
getSponsorCustomizedPages includes the expected hideArchived flag and other
filter/pagination params), and update the assertions around the save flow
(handleSaveManagedPage) to use toHaveBeenLastCalledWith-style checks or
expect.objectContaining on the last call args rather than relying solely on
toHaveBeenCalledTimes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/pages/sponsors/sponsor-pages-tab/index.js`:
- Around line 279-292: The post-save refresh calls to getSponsorManagedPages and
getSponsorCustomizedPages are missing the current hideArchived flag, causing
grids to repopulate incorrectly when "Hide archived" is enabled; update the two
calls in the post-save refresh to pass the appropriate hideArchived value (e.g.,
managedPages.hideArchived and customizedPages.hideArchived or the shared
hideArchived state) as an additional parameter to getSponsorManagedPages(...)
and getSponsorCustomizedPages(...) so the refresh respects the archived filter.

---

Nitpick comments:
In `@src/pages/sponsors/sponsor-pages-tab/__tests__/sponsor-pages-tab.test.js`:
- Around line 305-307: The test currently only checks call counts for
getSponsorManagedPages and getSponsorCustomizedPages; change it to assert the
last call arguments to ensure the correct filters/pagination are passed (e.g.,
verify that the last call to getSponsorManagedPages and
getSponsorCustomizedPages includes the expected hideArchived flag and other
filter/pagination params), and update the assertions around the save flow
(handleSaveManagedPage) to use toHaveBeenLastCalledWith-style checks or
expect.objectContaining on the last call args rather than relying solely on
toHaveBeenCalledTimes.

In `@src/pages/sponsors/sponsor-pages-tab/index.js`:
- Around line 199-200: Remove the temporary console.log("CHECK!", item) that
prints the row payload on every Customize click; inside the click handler where
getSponsorManagedPage(item.id).then(() => setOpenPopup("managedPagePopup")) is
invoked, delete the console.log statement (or replace it with a controlled
debug/logging call if persistent logging is required) so only
getSponsorManagedPage and setOpenPopup remain.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 35d93b55-7a8e-486d-b8ac-1a682644832e

📥 Commits

Reviewing files that changed from the base of the PR and between a5d6d41 and d3ed9e9.

📒 Files selected for processing (4)
  • src/i18n/en.json
  • src/pages/sponsors-global/page-templates/page-template-popup/index.js
  • src/pages/sponsors/sponsor-pages-tab/__tests__/sponsor-pages-tab.test.js
  • src/pages/sponsors/sponsor-pages-tab/index.js

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/actions/sponsor-pages-actions.js (1)

282-293: ⚠️ Potential issue | 🔴 Critical

Restore the add_ons source for the “all add-ons” check.

normalizedEntity only contains show_page_ids here, so Line 287 dereferences undefined.includes("all"). Every create save will throw before the POST. Read from entity.add_ons (or initialize normalizedEntity.allowed_add_ons first) before mapping IDs.

Suggested fix
-  if (normalizedEntity.allowed_add_ons.includes("all")) {
+  if (entity.add_ons.includes("all")) {
     normalizedEntity.apply_to_all_add_ons = true;
     normalizedEntity.allowed_add_ons = [];
   } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/actions/sponsor-pages-actions.js` around lines 282 - 293, In
normalizeSponsorManagedPage, the code checks
normalizedEntity.allowed_add_ons.includes("all") before allowed_add_ons is set,
causing a crash; change the logic to read from entity.add_ons (or initialize
normalizedEntity.allowed_add_ons first) — e.g., build allowed_add_ons from
entity.add_ons (mapping to ids) or set normalizedEntity.allowed_add_ons =
entity.add_ons.map(a => a.id) before the includes("all") check, then set
apply_to_all_add_ons and clear allowed_add_ons when "all" is present; update the
branches inside normalizeSponsorManagedPage to reference entity.add_ons or the
initialized normalizedEntity.allowed_add_ons consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/actions/sponsor-pages-actions.js`:
- Around line 282-293: In normalizeSponsorManagedPage, the code checks
normalizedEntity.allowed_add_ons.includes("all") before allowed_add_ons is set,
causing a crash; change the logic to read from entity.add_ons (or initialize
normalizedEntity.allowed_add_ons first) — e.g., build allowed_add_ons from
entity.add_ons (mapping to ids) or set normalizedEntity.allowed_add_ons =
entity.add_ons.map(a => a.id) before the includes("all") check, then set
apply_to_all_add_ons and clear allowed_add_ons when "all" is present; update the
branches inside normalizeSponsorManagedPage to reference entity.add_ons or the
initialized normalizedEntity.allowed_add_ons consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1092a026-c347-4b5d-8b5c-4f658b2517ed

📥 Commits

Reviewing files that changed from the base of the PR and between d3ed9e9 and c478b9e.

📒 Files selected for processing (1)
  • src/actions/sponsor-pages-actions.js

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
@tomrndom tomrndom requested a review from smarcet March 26, 2026 13:31
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.

1 participant