Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/actions/page-template-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ export const getPageTemplates =
orderDir = DEFAULT_ORDER_DIR,
hideArchived = false
) =>
async (dispatch) => {
async (dispatch, getState) => {
const { currentSummitState } = getState();
const { currentSummit } = currentSummitState;
const summitTZ = currentSummit.time_zone.name;
Comment on lines +57 to +60
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

Guard against missing summit time zone before dereferencing.

currentSummit or time_zone can be null during initial load; this would throw and break the list fetch. Add a safe fallback.

🛠️ Suggested fix
-  const { currentSummit } = currentSummitState;
-  const summitTZ = currentSummit.time_zone.name;
+  const { currentSummit } = currentSummitState || {};
+  const summitTZ = currentSummit?.time_zone?.name ?? "UTC";

Also applies to: 98-98

🤖 Prompt for AI Agents
In `@src/actions/page-template-actions.js` around lines 57 - 60, The code
dereferences currentSummit.time_zone.name into summitTZ without guarding for
missing currentSummit or time_zone; update the async action (the function using
dispatch,getState) to first check that currentSummit and currentSummit.time_zone
exist and use a safe fallback (e.g., 'UTC' or null) before reading .name, or
bail out early if unset; apply the same guard to the other occurrence around the
code referenced at the second spot (the other async action/usage at the later
location) so the list fetch won't throw when currentSummit or time_zone is null.

const accessToken = await getAccessTokenSafely();
const filter = [];

Expand Down Expand Up @@ -92,7 +95,7 @@ export const getPageTemplates =
createAction(RECEIVE_PAGE_TEMPLATES),
`${window.SPONSOR_PAGES_API_URL}/api/v1/page-templates`,
authErrorHandler,
{ order, orderDir, page, perPage, term, hideArchived }
{ order, orderDir, page, perPage, term, hideArchived, summitTZ }
)(params)(dispatch).then(() => {
dispatch(stopLoading());
});
Expand Down
128 changes: 127 additions & 1 deletion src/actions/sponsor-pages-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,31 @@ import {
createAction,
getRequest,
postRequest,
putRequest,
startLoading,
stopLoading
} from "openstack-uicore-foundation/lib/utils/actions";
import T from "i18n-react/dist/i18n-react";
import moment from "moment-timezone";
import { escapeFilterValue, getAccessTokenSafely } from "../utils/methods";
import { getSponsorForms } from "./sponsor-forms-actions";
import {
DEFAULT_CURRENT_PAGE,
DEFAULT_ORDER_DIR,
DEFAULT_PER_PAGE
DEFAULT_PER_PAGE,
PAGES_MODULE_KINDS
} from "../utils/constants";
import { snackbarErrorHandler, snackbarSuccessHandler } from "./base-actions";

export const REQUEST_SPONSOR_PAGES = "REQUEST_SPONSOR_PAGES";
export const RECEIVE_SPONSOR_PAGES = "RECEIVE_SPONSOR_PAGES";

export const RECEIVE_SPONSOR_PAGE = "RECEIVE_SPONSOR_PAGE";

export const SPONSOR_PAGE_UPDATED = "SPONSOR_PAGE_UPDATED";
export const SPONSOR_PAGE_ADDED = "SPONSOR_PAGE_ADDED";
export const RESET_SPONSOR_PAGE_FORM = "RESET_SPONSOR_PAGE_FORM";

export const GLOBAL_PAGE_CLONED = "GLOBAL_PAGE_CLONED";

export const getSponsorPages =
Expand Down Expand Up @@ -93,6 +102,123 @@ export const getSponsorPages =
});
};

export const getSponsorPage = (pageId) => async (dispatch, getState) => {
const { currentSummitState } = getState();
const { currentSummit } = currentSummitState;
const accessToken = await getAccessTokenSafely();

dispatch(startLoading());

const params = {
access_token: accessToken,
expand: "modules.file_type"
};

return getRequest(
null,
createAction(RECEIVE_SPONSOR_PAGE),
`${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/show-pages/${pageId}`,
authErrorHandler
)(params)(dispatch).then(() => {
dispatch(stopLoading());
});
};
Comment on lines +105 to +125
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

Ensure loading state clears on failed fetch.

stopLoading() only runs in the success path; on errors the spinner can get stuck. Use finally to guarantee cleanup.

🛠️ Suggested fix
-  return getRequest(
+  return getRequest(
     null,
     createAction(RECEIVE_SPONSOR_PAGE),
     `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/show-pages/${pageId}`,
     authErrorHandler
-  )(params)(dispatch).then(() => {
-    dispatch(stopLoading());
-  });
+  )(params)(dispatch)
+    .finally(() => {
+      dispatch(stopLoading());
+    });
🤖 Prompt for AI Agents
In `@src/actions/sponsor-pages-actions.js` around lines 104 - 124, The
getSponsorPage thunk currently calls dispatch(stopLoading()) only in the success
.then() branch so the loading spinner can remain if the fetch fails; update
getSponsorPage to ensure stopLoading() always runs by appending a .finally() (or
using try/finally if you convert to async/await) to the promise returned by
getRequest (the call that returns a function invoked with (params)(dispatch)),
so that dispatch(stopLoading()) is executed in the .finally() handler; keep the
existing dispatch(startLoading()) at the top and preserve authErrorHandler and
RECEIVE_SPONSOR_PAGE usage.


const normalizeSponsorPage = (entity) => {
const normalizedEntity = { ...entity };

normalizedEntity.modules = entity.modules.map((module) => {
const normalizedModule = { ...module };

if (module.kind === PAGES_MODULE_KINDS.MEDIA && module.upload_deadline) {
normalizedModule.upload_deadline = moment
.utc(module.upload_deadline)
.unix();
}

if (module.kind === PAGES_MODULE_KINDS.MEDIA && module.file_type_id) {
normalizedModule.file_type_id =
module.file_type_id?.value || module.file_type_id;
}

if (module.kind === PAGES_MODULE_KINDS.DOCUMENT && module.file) {
normalizedModule.file = module.file[0] || null;
}

delete normalizedModule._tempId;

return normalizedModule;
});

return normalizedEntity;
Comment on lines +127 to +153
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

Defend against missing modules in normalization.

If entity.modules is undefined (e.g., draft/new), .map will throw.

🛠️ Suggested fix
-  normalizedEntity.modules = entity.modules.map((module) => {
+  const modules = entity.modules ?? [];
+  normalizedEntity.modules = modules.map((module) => {
📝 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
const normalizeSponsorPage = (entity) => {
const normalizedEntity = { ...entity };
normalizedEntity.modules = entity.modules.map((module) => {
const normalizedModule = { ...module };
if (module.kind === PAGES_MODULE_KINDS.MEDIA && module.upload_deadline) {
normalizedModule.upload_deadline = moment
.utc(module.upload_deadline)
.unix();
}
if (module.kind === PAGES_MODULE_KINDS.MEDIA && module.file_type_id) {
normalizedModule.file_type_id =
module.file_type_id?.value || module.file_type_id;
}
if (module.kind === PAGES_MODULE_KINDS.DOCUMENT && module.file) {
normalizedModule.file = module.file[0] || null;
}
delete normalizedModule._tempId;
return normalizedModule;
});
return normalizedEntity;
const normalizeSponsorPage = (entity) => {
const normalizedEntity = { ...entity };
const modules = entity.modules ?? [];
normalizedEntity.modules = modules.map((module) => {
const normalizedModule = { ...module };
if (module.kind === PAGES_MODULE_KINDS.MEDIA && module.upload_deadline) {
normalizedModule.upload_deadline = moment
.utc(module.upload_deadline)
.unix();
}
if (module.kind === PAGES_MODULE_KINDS.MEDIA && module.file_type_id) {
normalizedModule.file_type_id =
module.file_type_id?.value || module.file_type_id;
}
if (module.kind === PAGES_MODULE_KINDS.DOCUMENT && module.file) {
normalizedModule.file = module.file[0] || null;
}
delete normalizedModule._tempId;
return normalizedModule;
});
return normalizedEntity;
};
🤖 Prompt for AI Agents
In `@src/actions/sponsor-pages-actions.js` around lines 126 - 152,
normalizeSponsorPage currently assumes entity.modules exists and calls .map
which throws if modules is undefined; update normalizeSponsorPage to guard
against missing modules by checking entity.modules (or using a default empty
array) before mapping and assign normalizedEntity.modules accordingly so
drafts/new entities without modules don't crash; reference the
normalizeSponsorPage function and the entity.modules usage when making this
change.

};

export const saveSponsorPage = (entity) => async (dispatch, getState) => {
const { currentSummitState } = getState();
const { currentSummit } = currentSummitState;
const accessToken = await getAccessTokenSafely();

dispatch(startLoading());

const params = {
access_token: accessToken
};

const normalizedSponsorPage = normalizeSponsorPage(entity);

if (entity.id) {
return putRequest(
null,
createAction(SPONSOR_PAGE_UPDATED),
`${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/show-pages/${entity.id}`,
normalizedSponsorPage,
snackbarErrorHandler,
entity
)(params)(dispatch)
.then(() => {
dispatch(
snackbarSuccessHandler({
title: T.translate("general.success"),
html: T.translate("sponsor_pages.page_saved")
})
);
})
.catch((err) => {
console.error(err);
})
.finally(() => {
dispatch(stopLoading());
});
Comment on lines +186 to +191
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

Swallowed errors break the promise chain.

The .catch() blocks log the error but don't re-throw, so the returned promise always resolves. Callers (like handleSaveSponsorPage) chain .then() expecting success, but the popup will close and the list will refresh even when the save fails—creating misleading UX.

Either re-throw after logging or remove the .catch() so the caller can handle failures:

🛠️ Suggested fix
      .catch((err) => {
        console.error(err);
+       throw err;
      })

Also applies to: 210-215

🤖 Prompt for AI Agents
In `@src/actions/sponsor-pages-actions.js` around lines 186 - 191, The .catch() in
the promise chain inside sponsor-pages-actions.js logs errors then swallows
them, causing callers like handleSaveSponsorPage to always see a resolved
promise; change the .catch handlers (the ones surrounding
dispatch(stopLoading())) to either remove the .catch so the rejection bubbles up
or re-throw the error after logging (e.g., console.error(err); throw err;) so
callers can handle failures; apply the same change to the second occurrence
noted around the 210-215 range.

}

return postRequest(
null,
createAction(SPONSOR_PAGE_ADDED),
`${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/show-pages`,
normalizedSponsorPage,
snackbarErrorHandler,
entity
)(params)(dispatch)
.then(() => {
dispatch(
snackbarSuccessHandler({
title: T.translate("general.success"),
html: T.translate("sponsor_pages.page_created")
})
);
})
.catch((err) => {
console.error(err);
})
.finally(() => {
dispatch(stopLoading());
});
};

export const resetSponsorPageForm = () => (dispatch) => {
dispatch(createAction(RESET_SPONSOR_PAGE_FORM)({}));
};

export const cloneGlobalPage =
(pagesIds, sponsorIds, allSponsors) => async (dispatch, getState) => {
const { currentSummitState } = getState();
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2641,6 +2641,8 @@
"filter": "Filter",
"sort_by": "Sort By",
"no_sponsors_pages": "No pages found for this search criteria.",
"page_saved": "Page updated successfully.",
"page_created": "Page created successfully.",
"placeholders": {
"search": "Search..."
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ const PageTemplateListPage = ({
archivePageTemplate,
unarchivePageTemplate,
savePageTemplate,
deletePageTemplate
deletePageTemplate,
summitTZ
}) => {
const [pageTemplateId, setPageTemplateId] = useState(null);

Expand Down Expand Up @@ -266,6 +267,7 @@ const PageTemplateListPage = ({
open={!!pageTemplateId}
onClose={() => setPageTemplateId(null)}
onSave={handleSavePageTemplate}
summitTZ={summitTZ}
/>
</div>
);
Expand Down
32 changes: 30 additions & 2 deletions src/pages/sponsors-global/page-templates/page-template-popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@ import AddIcon from "@mui/icons-material/Add";
import CloseIcon from "@mui/icons-material/Close";
import { FormikProvider, useFormik } from "formik";
import * as yup from "yup";
import { epochToMomentTimeZone } from "openstack-uicore-foundation/lib/utils/methods";
import MuiFormikTextField from "../../../components/mui/formik-inputs/mui-formik-textfield";
import PageModules from "./page-template-modules-form";
import {
PAGES_MODULE_KINDS,
PAGE_MODULES_MEDIA_TYPES
} from "../../../utils/constants";

const PageTemplatePopup = ({ pageTemplate, open, onClose, onSave }) => {
const PageTemplatePopup = ({
pageTemplate,
open,
onClose,
onSave,
summitTZ
}) => {
const handleClose = () => {
onClose();
};
Expand Down Expand Up @@ -108,10 +115,31 @@ const PageTemplatePopup = ({ pageTemplate, open, onClose, onSave }) => {
}
});

const normalizeModules = (modules = [], summitTZ = "UTC") =>
modules.map((m) => {
if (m.kind === PAGES_MODULE_KINDS.MEDIA) {
const normalizeModule = { ...m };
if (m.upload_deadline) {
normalizeModule.upload_deadline = epochToMomentTimeZone(
m.upload_deadline,
summitTZ
);
}
if (m.file_type) {
normalizeModule.file_type_id = {
value: m.file_type.id,
label: `${m.file_type.name} (${m.file_type.allowed_extensions})`
};
}
Comment on lines +118 to +133
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

🧩 Analysis chain

🏁 Script executed:

# First, find the file and check the actual code at the specified lines
cat -n "src/pages/sponsors-global/page-templates/page-template-popup.js" | sed -n '110,140p'

Repository: fntechgit/summit-admin

Length of output: 1121


🏁 Script executed:

# Search for how normalizeModules is called throughout the codebase
rg "normalizeModules\(" --max-count=20

Repository: fntechgit/summit-admin

Length of output: 200


🏁 Script executed:

# Find the epochToMomentTimeZone function definition
rg "epochToMomentTimeZone" -A 5 --max-count=10

Repository: fntechgit/summit-admin

Length of output: 50378


🏁 Script executed:

# Find where summitTZ is defined/used in the page-template-popup.js file
grep -n "summitTZ" "src/pages/sponsors-global/page-templates/page-template-popup.js" | head -20

Repository: fntechgit/summit-admin

Length of output: 246


🏁 Script executed:

# Look at the component definition and props to understand summitTZ source
sed -n '1,150p' "src/pages/sponsors-global/page-templates/page-template-popup.js" | grep -A 5 -B 5 "summitTZ\|export\|const\|function"

Repository: fntechgit/summit-admin

Length of output: 3546


🏁 Script executed:

# Find parent components that use PageTemplatePopup to see how summitTZ is passed
rg "PageTemplatePopup" --max-count=10

Repository: fntechgit/summit-admin

Length of output: 817


🏁 Script executed:

# Check if there are any null checks or guards for summitTZ elsewhere in similar components
rg "summitTZ\s*\|\|" --max-count=10

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Check how summitTZ is passed in page-template-list-page.js
grep -A 10 "PageTemplatePopup" "src/pages/sponsors-global/page-templates/page-template-list-page.js" | head -20

Repository: fntechgit/summit-admin

Length of output: 464


🏁 Script executed:

# Check how summitTZ is passed in sponsor-pages-list-page
grep -A 10 "PageTemplatePopup" "src/pages/sponsors/sponsor-pages-list-page/index.js" | head -20

Repository: fntechgit/summit-admin

Length of output: 501


🏁 Script executed:

# Check where summitTZ comes from in page-template-list-page.js
grep -B 20 "summitTZ={summitTZ}" "src/pages/sponsors-global/page-templates/page-template-list-page.js" | grep -E "summitTZ|const|useState|mapStateToProps"

Repository: fntechgit/summit-admin

Length of output: 93


🏁 Script executed:

# Check the component definition and props for sponsor-pages-list-page to see why summitTZ is missing
head -100 "src/pages/sponsors/sponsor-pages-list-page/index.js" | tail -80

Repository: fntechgit/summit-admin

Length of output: 1915


🏁 Script executed:

# Check if summitTZ is ever set to null anywhere in the component files
rg "summitTZ\s*=\s*null" --max-count=5

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Check the mapStateToProps for page-template-list-page to see how summitTZ is sourced
grep -A 30 "mapStateToProps\|const.*summitTZ" "src/pages/sponsors-global/page-templates/page-template-list-page.js" | head -40

Repository: fntechgit/summit-admin

Length of output: 332


🏁 Script executed:

# Find the page template list reducer to see what's in the state
fd "page-template-list" -t f | grep -i reducer

Repository: fntechgit/summit-admin

Length of output: 127


🏁 Script executed:

# Search for where pageTemplateListState is defined/reduced
rg "pageTemplateListState|PAGE_TEMPLATE" --type js -l | head -5

Repository: fntechgit/summit-admin

Length of output: 334


🏁 Script executed:

# Check the page-template-list-reducer to see the state structure and how summitTZ is set
cat -n "src/reducers/sponsors_inventory/page-template-list-reducer.js" | head -60

Repository: fntechgit/summit-admin

Length of output: 2231


🏁 Script executed:

# Continue viewing the reducer to find where summitTZ comes from
cat -n "src/reducers/sponsors_inventory/page-template-list-reducer.js" | tail -60

Repository: fntechgit/summit-admin

Length of output: 2221


🏁 Script executed:

# Also search for where summitTZ is imported or defined in store or root reducer
grep -r "summitTZ" "src/store.js" "src/reducers/index.js" 2>/dev/null | head -10

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Check the full page-template-list-page.js to see where summitTZ comes from
cat -n "src/pages/sponsors-global/page-templates/page-template-list-page.js"

Repository: fntechgit/summit-admin

Length of output: 9102


🏁 Script executed:

# Check the root reducer to see what state is available
cat -n "src/reducers/index.js" | head -80

Repository: fntechgit/summit-admin

Length of output: 119


🏁 Script executed:

# Also check store.js to see the full state structure
cat -n "src/store.js"

Repository: fntechgit/summit-admin

Length of output: 24864


🏁 Script executed:

# Check the currentSummitReducer to see if it has summitTZ
grep -n "summitTZ\|time_zone" "src/reducers/summits/current-summit-reducer.js" | head -20

Repository: fntechgit/summit-admin

Length of output: 137


🏁 Script executed:

# Look at other pages that use summitTZ to see how they source it
grep -B 5 "summitTZ" "src/pages/sponsors/sponsor-pages-list-page/index.js" | head -20

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Check if pageTemplateListReducer should have summitTZ by checking other similar list reducers
grep -l "summitTZ" "src/reducers/sponsors"/*.js | head -3

Repository: fntechgit/summit-admin

Length of output: 228


🏁 Script executed:

# Get the actual state props passed to PageTemplateListPage by checking the full component
grep -A 50 "const PageTemplateListPage" "src/pages/sponsors-global/page-templates/page-template-list-page.js" | grep -E "summitTZ|currentSummit"

Repository: fntechgit/summit-admin

Length of output: 76


🏁 Script executed:

# Check if summitTZ might come from currentSummitState instead
grep "currentSummit.*time_zone\|summitTZ" "src/reducers/summits/current-summit-reducer.js" | head -10

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Check what summitTZ should actually be in sponsor-page-forms-list-reducer
grep -A 30 "DEFAULT_STATE\|summitTZ" "src/reducers/sponsors/sponsor-page-forms-list-reducer.js" | head -50

Repository: fntechgit/summit-admin

Length of output: 1075


🏁 Script executed:

# Verify if PageTemplateListPage is actually receiving summitTZ correctly from Redux
grep "mapStateToProps\|summitTZ" "src/pages/sponsors-global/page-templates/page-template-list-page.js"

Repository: fntechgit/summit-admin

Length of output: 204


🏁 Script executed:

# Check how summitTZ gets into sponsor-page-forms-list-reducer's REQUEST action
grep -B 10 "REQUEST_SPONSOR_MANAGED_FORMS\|getSponsorPages" "src/pages/sponsors-global/page-templates/page-template-list-page.js" | head -20

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Check the actions to see if summitTZ is being passed to getPageTemplates
grep -A 20 "export.*getPageTemplates" "src/actions/page-template-actions.js" | head -30

Repository: fntechgit/summit-admin

Length of output: 689


🏁 Script executed:

# Check if summitTZ is dispatched to pageTemplateListReducer
grep -n "pageTemplateListState\|REQUEST_PAGE_TEMPLATES\|RECEIVE_PAGE_TEMPLATES" "src/actions/page-template-actions.js" | head -20

Repository: fntechgit/summit-admin

Length of output: 293


🏁 Script executed:

# Check the full action to see what's dispatched
cat -n "src/actions/page-template-actions.js" | head -80

Repository: fntechgit/summit-admin

Length of output: 3355


Pass the timezone from state to avoid relying on default parameters.

The summitTZ prop passed to PageTemplatePopup is currently undefined because it's not included in pageTemplateListState. While the default parameter "UTC" catches undefined, this is fragile—it relies on accident rather than intent. For consistency with similar components (e.g., sponsor-page-forms-list-reducer), add summitTZ to pageTemplateListReducer's state so it's properly tracked and passed down, or explicitly coalesce it at the call site to make the behavior intentional.

🤖 Prompt for AI Agents
In `@src/pages/sponsors-global/page-templates/page-template-popup.js` around lines
118 - 133, The normalizeModules function (and PageTemplatePopup) currently rely
on a summitTZ parameter that is undefined because pageTemplateListState lacks
summitTZ; add summitTZ to the pageTemplateListReducer's state (and initialize
it, e.g., to "UTC" or the real summit timezone) and ensure the selector that
builds pageTemplateListState includes this summitTZ so PageTemplatePopup
receives a defined value, or alternatively explicitly coalesce summitTZ at the
call site when passing into normalizeModules/PageTemplatePopup (e.g., pass
state.summitTZ || "UTC"); update references to summitTZ in the reducer/selector
and any component props (pageTemplateListState) to keep behavior explicit and
consistent with sponsor-page-forms-list-reducer.

return normalizeModule;
}
return m;
});

const formik = useFormik({
initialValues: {
...pageTemplate,
modules: pageTemplate?.modules || []
modules: normalizeModules(pageTemplate?.modules, summitTZ) || []
},
validationSchema: yup.object().shape({
code: yup.string().required(T.translate("validation.required")),
Expand Down
40 changes: 32 additions & 8 deletions src/pages/sponsors/sponsor-pages-list-page/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,16 @@ import {
Grid2
} from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
import { getSponsorPages } from "../../../actions/sponsor-pages-actions";
import { getSponsorForm } from "../../../actions/sponsor-forms-actions";
import {
getSponsorPages,
getSponsorPage,
saveSponsorPage,
resetSponsorPageForm
} from "../../../actions/sponsor-pages-actions";
import CustomAlert from "../../../components/mui/custom-alert";
import MuiTable from "../../../components/mui/table/mui-table";
import GlobalPagePopup from "./components/global-page/global-page-popup";
import PageTemplatePopup from "../../sponsors-global/page-templates/page-template-popup";

const SponsorPagesListPage = ({
sponsorPages,
Expand All @@ -38,8 +43,11 @@ const SponsorPagesListPage = ({
orderDir,
hideArchived,
totalCount,
currentSponsorPage,
getSponsorPages,
getSponsorForm
getSponsorPage,
saveSponsorPage,
resetSponsorPageForm
}) => {
const [openPopup, setOpenPopup] = useState(null);

Expand Down Expand Up @@ -67,7 +75,7 @@ const SponsorPagesListPage = ({
};

const handleRowEdit = (row) => {
getSponsorForm(row.id).then(() => {
getSponsorPage(row.id).then(() => {
setOpenPopup("new");
});
Comment on lines 77 to 80
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

Edit vs. new mode can overwrite existing pages.

Both “edit” and “new” open the same popup state and reuse currentSponsorPage, so clicking “new” after an edit can retain id and update instead of creating. Separate modes or clear the template for new.

🛠️ Suggested fix
-  const [openPopup, setOpenPopup] = useState(null);
+  const [popupMode, setPopupMode] = useState(null);

   const handleRowEdit = (row) => {
     getSponsorPage(row.id).then(() => {
-      setOpenPopup("new");
+      setPopupMode("edit");
     });
   };

 ...
-            onClick={() => setOpenPopup("new")}
+            onClick={() => setPopupMode("new")}
 ...
-      <GlobalPagePopup
-        open={openPopup === "clone"}
-        onClose={() => setOpenPopup(null)}
-      />
+      <GlobalPagePopup
+        open={popupMode === "clone"}
+        onClose={() => setPopupMode(null)}
+      />
       <PageTemplatePopup
-        open={openPopup === "new"}
-        pageTemplate={currentSponsorPage}
-        onClose={() => setOpenPopup(null)}
+        open={popupMode === "new" || popupMode === "edit"}
+        pageTemplate={popupMode === "edit" ? currentSponsorPage : null}
+        onClose={() => setPopupMode(null)}
         onSave={handleSaveSponsorPage}
       />

Also applies to: 232-237

🤖 Prompt for AI Agents
In `@src/pages/sponsors/sponsor-pages-list-page/index.js` around lines 75 - 78,
The popup uses the same "new" state and shared currentSponsorPage for both
creating and editing, so opening "new" after an edit can reuse the existing id
and overwrite; update the flow so edit and create use distinct modes (e.g.,
setOpenPopup("edit") in handleRowEdit and setOpenPopup("new") only for creation)
or explicitly clear/reset currentSponsorPage when opening the "new" popup;
change handleRowEdit to set an "edit" mode (or set a separate isEditing flag)
and ensure any code that opens the create popup (the other place where
setOpenPopup("new") is called) resets currentSponsorPage/template to an empty
object so new saves create instead of update.

};
Expand All @@ -93,6 +101,18 @@ const SponsorPagesListPage = ({
);
};

const handleSaveSponsorPage = (entity) => {
saveSponsorPage(entity).then(() => {
setOpenPopup(null);
getSponsorPages();
});
};

const handleTemplatePopupClose = () => {
resetSponsorPageForm();
setOpenPopup(null);
};

const columns = [
{
columnKey: "code",
Expand Down Expand Up @@ -216,10 +236,12 @@ const SponsorPagesListPage = ({
open={openPopup === "clone"}
onClose={() => setOpenPopup(null)}
/>
{/* <FormPagePopup
<PageTemplatePopup
open={openPopup === "new"}
onClose={() => setOpenPopup(null)}
/> */}
pageTemplate={currentSponsorPage}
onClose={handleTemplatePopupClose}
onSave={handleSaveSponsorPage}
/>
</div>
);
};
Expand All @@ -230,5 +252,7 @@ const mapStateToProps = ({ sponsorPagesListState }) => ({

export default connect(mapStateToProps, {
getSponsorPages,
getSponsorForm
getSponsorPage,
saveSponsorPage,
resetSponsorPageForm
})(SponsorPagesListPage);
Loading