Conversation
📝 WalkthroughWalkthroughAdds 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 Changes
Sequence DiagramsequenceDiagram
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)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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 | 🟡 MinorFix dotenv-linter UnorderedKey warning.
SPONSOR_PAGES_SCOPESshould appear beforeSPONSOR_USERS_API_SCOPESto 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 | 🟠 MajorFix tab/panel index mismatch (Users vs Pages).
Lines 236–241 render the Users list under index
2(Pages), while index1(Users) shows placeholder text. This misroutes the tab content. UseSPONSOR_TABS.*values forCustomTabPanelindices 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).
| 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()); | ||
| }); |
There was a problem hiding this comment.
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.
| 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> |
There was a problem hiding this comment.
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.
| 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.
| 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 | ||
| }; |
There was a problem hiding this comment.
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).
https://app.clickup.com/t/86b7991cx
Summary by CodeRabbit