Skip to content

feat: sponsor media upload tab#783

Open
santipalenque wants to merge 3 commits intomasterfrom
feature/sponsor-media-upload-tab
Open

feat: sponsor media upload tab#783
santipalenque wants to merge 3 commits intomasterfrom
feature/sponsor-media-upload-tab

Conversation

@santipalenque
Copy link

@santipalenque santipalenque commented Feb 5, 2026

https://app.clickup.com/t/86b7991cx

Summary by CodeRabbit

  • New Features
    • Added a Sponsor Media Uploads tab showing sponsor and general media requests with auto-load, pagination, sorting, and row actions (view, download, upload, delete).
  • Localization
    • New English labels and messages for the media upload tab and its actions.
  • Other
    • Introduced media upload status labels: Pending, Deadline Alert, Complete.

@coderabbitai
Copy link

coderabbitai bot commented Feb 5, 2026

📝 Walkthrough

Walkthrough

Adds a Sponsor Media Upload feature: new Redux actions and reducer, store wiring, a React tab component with pagination/sorting and table UI, localization keys and a status constant, and an updated .env.example to include media-upload/read in sponsor scopes.

Changes

Cohort / File(s) Summary
Redux Actions
src/actions/sponsor-mu-actions.js
New thunk actions and action type constants to request/receive sponsor and general media upload lists; builds API requests, dispatches loading, and handles errors via snackbarErrorHandler.
Redux Reducer
src/reducers/sponsors/sponsor-page-mu-list-reducer.js
New reducer managing sponsorRequests and generalRequests state, pagination, sorting, summitTZ, and computes expires_at formatted per summit timezone.
Store Wiring
src/store.js
Registers new sponsorPageMUListState into persisted root reducers.
UI — Sponsor Page
src/pages/sponsors/edit-sponsor-page.js
Adds a new tab for media uploads and shifts users list into its own panel.
UI — Media Upload Tab
src/pages/sponsors/sponsor-media-upload-tab/index.js
New connected React component SponsorMediaUploadTab: fetches data on mount, supports pagination/sorting, renders table with status chips and action buttons.
Constants & i18n
src/utils/constants.js, src/i18n/en.json
Adds SPONSOR_MEDIA_UPLOAD_STATUS constant and mu_tab localization keys (labels, messages, status, etc.).
Config
.env.example
Expands SPONSOR_PAGES_SCOPES to include media-upload/read alongside existing scopes.

Sequence Diagram

sequenceDiagram
    participant UI as SponsorMediaUploadTab
    participant Store as Redux Store
    participant Action as getSponsorMURequests
    participant API as Sponsor Pages API
    participant Reducer as sponsorPageMUListReducer

    UI->>Store: dispatch getSponsorMURequests()
    activate Action
    Action->>Store: dispatch REQUEST_SPONSOR_MEDIA_UPLOADS
    activate Reducer
    Reducer-->>Store: clear requests, set params (page, order, summitTZ)
    deactivate Reducer

    Action->>API: GET /summits/{id}/sponsors/{id}/media-uploads?...
    API-->>Action: 200 { pagination, data }

    Action->>Store: dispatch RECEIVE_SPONSOR_MEDIA_UPLOADS
    activate Reducer
    Reducer-->>Store: map data, compute expires_at (epochToMomentTimeZone), set pagination
    deactivate Reducer
    deactivate Action

    Store-->>UI: updated props via mapStateToProps
    UI->>UI: re-render table (pagination, sorting)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • tomrndom
  • romanetar
  • gcutrini

Poem

🐰
I hopped through tabs where media hide,
Polished chips and pages wide,
Requests line up in tidy rows,
Deadlines blink where upload flows,
I nibble code and watch it glide.

🚥 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 'feat: sponsor media upload tab' clearly summarizes the main change: adding a new sponsor media upload tab component. It is concise, specific, and directly related to the primary objective of the pull request.
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
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/sponsor-media-upload-tab

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: 4

Caution

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

⚠️ Outside diff range comments (2)
.env.example (1)

16-20: ⚠️ Potential issue | 🟡 Minor

Fix dotenv-linter UnorderedKey warning.

SPONSOR_PAGES_SCOPES should appear before SPONSOR_USERS_API_SCOPES to satisfy the linter.

🔧 Reorder example
-PURCHASES_API_SCOPES="purchases-show-medata/read purchases-show-medata/write show-form/read show-form/write customized-form/write customized-form/read carts/read carts/write"
-SPONSOR_USERS_API_SCOPES="show-medata/read show-medata/write access-requests/read access-requests/write sponsor-users/read sponsor-users/write groups/read groups/write"
+PURCHASES_API_SCOPES="purchases-show-medata/read purchases-show-medata/write show-form/read show-form/write customized-form/write customized-form/read carts/read carts/write"
+SPONSOR_PAGES_SCOPES="page-template/read page-template/write show-page/read show-page/write media-upload/read"
+SPONSOR_USERS_API_SCOPES="show-medata/read show-medata/write access-requests/read access-requests/write sponsor-users/read sponsor-users/write groups/read groups/write"
src/pages/sponsors/edit-sponsor-page.js (1)

214-244: ⚠️ Potential issue | 🟠 Major

Fix tab/panel index mismatch (Users vs Pages).

Lines 236–241 render the Users list under index 2 (Pages), while index 1 (Users) shows placeholder text. This misroutes the tab content. Use SPONSOR_TABS.* values for CustomTabPanel indices to prevent drift.

✅ Suggested alignment
-        <CustomTabPanel value={selectedTab} index={1}>
-          USERS
-        </CustomTabPanel>
-        <CustomTabPanel value={selectedTab} index={2}>
-          <SponsorUsersListPerSponsorPage sponsor={entity} />
-        </CustomTabPanel>
-        <CustomTabPanel value={selectedTab} index={3}>
+        <CustomTabPanel value={selectedTab} index={SPONSOR_TABS.USERS}>
+          <SponsorUsersListPerSponsorPage sponsor={entity} />
+        </CustomTabPanel>
+        <CustomTabPanel value={selectedTab} index={SPONSOR_TABS.MEDIA_UPLOADS}>
           <SponsorMediaUploadTab sponsor={entity} summitId={currentSummit.id} />
         </CustomTabPanel>
🤖 Fix all issues with AI agents
In `@src/actions/sponsor-mu-actions.js`:
- Around line 45-69: The code dispatches startLoading() before calling
getRequest but only dispatches stopLoading() inside the .then branch so
stopLoading() is never called on rejection; modify the call that invokes
getRequest(createAction(REQUEST_SPONSOR_MEDIA_UPLOADS),
createAction(RECEIVE_SPONSOR_MEDIA_UPLOADS), ...)(params)(dispatch) so that
stopLoading() is always executed by using a try/finally around an await of that
request or by appending .finally(() => dispatch(stopLoading())), keeping the
same parameters and handlers (snackbarErrorHandler) and preserving
startLoading()/stopLoading() calls.
- Around line 31-67: The action getSponsorMURequests is passing currentPage in
the payload object to getRequest but the reducer expects page; change the
payload object passed in the getRequest call (the object currently written as {
order, orderDir, currentPage, perPage, summitTZ }) to use page: currentPage
instead of currentPage so the reducer (which destructures page) receives the
correct pagination key and currentPage is preserved in state.

In `@src/pages/sponsors/sponsor-media-upload-tab/index.js`:
- Around line 101-123: The renderers for the View and Download IconButton
components are disabling buttons when a file exists due to the condition
"!!row.file"; change the condition to disable when the file is missing (use
"!row.file" or equivalent) so that handleSponsorView and handleSponsorDownload
are enabled when row.file is truthy and disabled when falsy; update both
IconButton render blocks associated with the View (uses handleSponsorView) and
Download (uses handleSponsorDownload) columns accordingly.

In `@src/reducers/sponsors/sponsor-page-mu-list-reducer.js`:
- Around line 65-78: The mapped object in the payload.response.data mapping
(inside the reducer that builds rows for SponsorMediaUploadTab) only returns id,
code, name, items_count and expires_at, but the UI expects add_on, max_size,
format, deadline, status and file; update the mapping logic that constructs each
row (the payload.response.data.map callback) to extract and normalize the
corresponding API fields (e.g., add_on, max_size, format, deadline—converting
deadline via epochToMomentTimeZone like expires_at—and compute a safe status
value and file metadata) so the returned object includes keys add_on, max_size,
format, deadline, status, file along with the existing fields used by
SponsorMediaUploadTab and ensure any missing fields use sensible defaults (e.g.,
"N/A" or null).

Comment on lines 45 to 69
dispatch(startLoading());

const params = {
page: currentPage,
// fields: "id,code,name,level,expire_date",
// relations: "add_ons",
per_page: perPage,
access_token: accessToken
};

// order
if (order != null && orderDir != null) {
const orderDirSign = orderDir === 1 ? "" : "-";
params.order = `${orderDirSign}${order}`;
}

return getRequest(
createAction(REQUEST_SPONSOR_MEDIA_UPLOADS),
createAction(RECEIVE_SPONSOR_MEDIA_UPLOADS),
`${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/custom-media-request-modules`,
snackbarErrorHandler,
{ order, orderDir, currentPage, perPage, summitTZ }
)(params)(dispatch).then(() => {
dispatch(stopLoading());
});
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

Ensure loading stops on errors (use finally).

If the request rejects, stopLoading() isn’t called, leaving the UI in a loading state. Wrap the call in try/finally or use .finally(...).

✅ Suggested fix (try/finally)
-    return getRequest(
-      createAction(REQUEST_SPONSOR_MEDIA_UPLOADS),
-      createAction(RECEIVE_SPONSOR_MEDIA_UPLOADS),
-      `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/custom-media-request-modules`,
-      snackbarErrorHandler,
-      { order, orderDir, currentPage, perPage, summitTZ }
-    )(params)(dispatch).then(() => {
-      dispatch(stopLoading());
-    });
+    try {
+      return await getRequest(
+        createAction(REQUEST_SPONSOR_MEDIA_UPLOADS),
+        createAction(RECEIVE_SPONSOR_MEDIA_UPLOADS),
+        `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/custom-media-request-modules`,
+        snackbarErrorHandler,
+        { order, orderDir, currentPage, perPage, summitTZ }
+      )(params)(dispatch);
+    } finally {
+      dispatch(stopLoading());
+    }
🤖 Prompt for AI Agents
In `@src/actions/sponsor-mu-actions.js` around lines 45 - 69, The code dispatches
startLoading() before calling getRequest but only dispatches stopLoading()
inside the .then branch so stopLoading() is never called on rejection; modify
the call that invokes getRequest(createAction(REQUEST_SPONSOR_MEDIA_UPLOADS),
createAction(RECEIVE_SPONSOR_MEDIA_UPLOADS), ...)(params)(dispatch) so that
stopLoading() is always executed by using a try/finally around an await of that
request or by appending .finally(() => dispatch(stopLoading())), keeping the
same parameters and handlers (snackbarErrorHandler) and preserving
startLoading()/stopLoading() calls.

Comment on lines 101 to 123
render: (row) => (
<IconButton
size="large"
disabled={!!row.file}
onClick={() => handleSponsorView(row)}
>
<EditIcon fontSize="large" />
</IconButton>
)
},
{
columnKey: "download",
header: "",
width: 80,
align: "center",
render: (row) => (
<IconButton
size="large"
disabled={!!row.file}
onClick={() => handleSponsorDownload(row)}
>
<DownloadIcon fontSize="large" />
</IconButton>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Invert the row.file check for View/Download.

Lines 104 and 119 disable the buttons when row.file is truthy, which prevents actions exactly when a file exists. This should likely disable when the file is missing.

✅ Suggested fix
-          disabled={!!row.file}
+          disabled={!row.file}
-          disabled={!!row.file}
+          disabled={!row.file}
📝 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.

Suggested change
render: (row) => (
<IconButton
size="large"
disabled={!!row.file}
onClick={() => handleSponsorView(row)}
>
<EditIcon fontSize="large" />
</IconButton>
)
},
{
columnKey: "download",
header: "",
width: 80,
align: "center",
render: (row) => (
<IconButton
size="large"
disabled={!!row.file}
onClick={() => handleSponsorDownload(row)}
>
<DownloadIcon fontSize="large" />
</IconButton>
render: (row) => (
<IconButton
size="large"
disabled={!row.file}
onClick={() => handleSponsorView(row)}
>
<EditIcon fontSize="large" />
</IconButton>
)
},
{
columnKey: "download",
header: "",
width: 80,
align: "center",
render: (row) => (
<IconButton
size="large"
disabled={!row.file}
onClick={() => handleSponsorDownload(row)}
>
<DownloadIcon fontSize="large" />
</IconButton>
🤖 Prompt for AI Agents
In `@src/pages/sponsors/sponsor-media-upload-tab/index.js` around lines 101 - 123,
The renderers for the View and Download IconButton components are disabling
buttons when a file exists due to the condition "!!row.file"; change the
condition to disable when the file is missing (use "!row.file" or equivalent) so
that handleSponsorView and handleSponsorDownload are enabled when row.file is
truthy and disabled when falsy; update both IconButton render blocks associated
with the View (uses handleSponsorView) and Download (uses handleSponsorDownload)
columns accordingly.

Comment on lines +65 to +78
const requests = payload.response.data.map((a) => {
const expiresAt = a.expires_at
? epochToMomentTimeZone(a.expires_at, state.summitTZ)?.format(
"YYYY/MM/DD"
)
: "N/A";

return {
id: a.id,
code: a.code,
name: a.name,
items_count: a.items_count,
expires_at: expiresAt
};
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

Populate fields expected by SponsorMediaUploadTab.

Line 72–78 only map id/code/name/items_count/expires_at, but the table columns render add_on, max_size, format, deadline, status, and file. This will render empty cells and an undefined status chip. Align the mapped object to the UI keys (or update the table keys to match the API).

🧭 Example alignment (adjust to actual API field names)
       return {
         id: a.id,
         code: a.code,
         name: a.name,
-        items_count: a.items_count,
-        expires_at: expiresAt
+        add_on: a.add_on,
+        max_size: a.max_size,
+        format: a.format,
+        deadline: expiresAt,
+        status: a.status,
+        file: a.file
       };
🤖 Prompt for AI Agents
In `@src/reducers/sponsors/sponsor-page-mu-list-reducer.js` around lines 65 - 78,
The mapped object in the payload.response.data mapping (inside the reducer that
builds rows for SponsorMediaUploadTab) only returns id, code, name, items_count
and expires_at, but the UI expects add_on, max_size, format, deadline, status
and file; update the mapping logic that constructs each row (the
payload.response.data.map callback) to extract and normalize the corresponding
API fields (e.g., add_on, max_size, format, deadline—converting deadline via
epochToMomentTimeZone like expires_at—and compute a safe status value and file
metadata) so the returned object includes keys add_on, max_size, format,
deadline, status, file along with the existing fields used by
SponsorMediaUploadTab and ensure any missing fields use sensible defaults (e.g.,
"N/A" or null).

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