-
Notifications
You must be signed in to change notification settings - Fork 4
feat: cart tab - add form flow #797
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
58889a0
55852b4
e75421d
57063ca
c9e2604
1aa3015
1b76d15
03b7561
58db288
1832285
70d7668
f35a39b
e253e48
cd9c506
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,22 +14,37 @@ | |
| import { | ||
| authErrorHandler, | ||
| createAction, | ||
| getRequest, | ||
| deleteRequest, | ||
| getRequest, | ||
| postRequest, | ||
| putRequest, | ||
| startLoading, | ||
| stopLoading | ||
| } from "openstack-uicore-foundation/lib/utils/actions"; | ||
|
|
||
| import T from "i18n-react"; | ||
| import { escapeFilterValue, getAccessTokenSafely } from "../utils/methods"; | ||
| import { snackbarErrorHandler, snackbarSuccessHandler } from "./base-actions"; | ||
| import { ERROR_CODE_404 } from "../utils/constants"; | ||
| import { | ||
| DEFAULT_CURRENT_PAGE, | ||
| DEFAULT_ORDER_DIR, | ||
| DEFAULT_PER_PAGE, | ||
| ERROR_CODE_404 | ||
| } from "../utils/constants"; | ||
|
|
||
| export const REQUEST_SPONSOR_CART = "REQUEST_SPONSOR_CART"; | ||
| export const RECEIVE_SPONSOR_CART = "RECEIVE_SPONSOR_CART"; | ||
| export const SPONSOR_CART_FORM_DELETED = "SPONSOR_CART_FORM_DELETED"; | ||
| export const SPONSOR_CART_FORM_LOCKED = "SPONSOR_CART_FORM_LOCKED"; | ||
| export const REQUEST_CART_AVAILABLE_FORMS = "REQUEST_CART_AVAILABLE_FORMS"; | ||
| export const RECEIVE_CART_AVAILABLE_FORMS = "RECEIVE_CART_AVAILABLE_FORMS"; | ||
| export const REQUEST_CART_FORM = "REQUEST_CART_FORM"; | ||
| export const RECEIVE_CART_FORM = "RECEIVE_CART_FORM"; | ||
| export const REQUEST_CART_SPONSOR_FORM = "REQUEST_CART_SPONSOR_FORM"; | ||
| export const RECEIVE_CART_SPONSOR_FORM = "RECEIVE_CART_SPONSOR_FORM"; | ||
| export const FORM_CART_SAVED = "FORM_CART_SAVED"; | ||
| export const SPONSOR_CART_NOTE_ADDED = "SPONSOR_CART_NOTE_ADDED"; | ||
| export const SPONSOR_CART_NOTE_UPDATED = "SPONSOR_CART_NOTE_UPDATED"; | ||
| export const SPONSOR_CART_NOTE_DELETED = "SPONSOR_CART_NOTE_DELETED"; | ||
|
|
||
| const customErrorHandler = (err, res) => (dispatch, state) => { | ||
| const code = err.status; | ||
|
|
@@ -163,3 +178,268 @@ export const unlockSponsorCartForm = (formId) => async (dispatch, getState) => { | |
| dispatch(stopLoading()); | ||
| }); | ||
| }; | ||
|
|
||
| export const getSponsorFormsForCart = | ||
| ( | ||
| term = "", | ||
| currentPage = DEFAULT_CURRENT_PAGE, | ||
| order = "id", | ||
| orderDir = DEFAULT_ORDER_DIR | ||
| ) => | ||
| async (dispatch, getState) => { | ||
| const { currentSummitState } = getState(); | ||
| const { currentSummit } = currentSummitState; | ||
| const accessToken = await getAccessTokenSafely(); | ||
| const filter = ["has_items==1"]; | ||
|
|
||
| dispatch(startLoading()); | ||
|
|
||
| if (term) { | ||
| const escapedTerm = escapeFilterValue(term); | ||
| filter.push(`name=@${escapedTerm},code=@${escapedTerm}`); | ||
| } | ||
|
|
||
| const params = { | ||
| page: currentPage, | ||
| fields: "id,code,name,items", | ||
| per_page: DEFAULT_PER_PAGE, | ||
| access_token: accessToken | ||
| }; | ||
|
|
||
| if (filter.length > 0) { | ||
| params["filter[]"] = filter; | ||
| } | ||
|
|
||
| // order | ||
| if (order != null && orderDir != null) { | ||
| const orderDirSign = orderDir === 1 ? "" : "-"; | ||
| params.order = `${orderDirSign}${order}`; | ||
| } | ||
|
|
||
| return getRequest( | ||
| createAction(REQUEST_CART_AVAILABLE_FORMS), | ||
| createAction(RECEIVE_CART_AVAILABLE_FORMS), | ||
| `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/show-forms`, | ||
| authErrorHandler, | ||
| { term, order, orderDir, currentPage } | ||
| )(params)(dispatch).then(() => { | ||
| dispatch(stopLoading()); | ||
| }); | ||
| }; | ||
|
|
||
| // get sponsor show form by id USING V2 API | ||
| export const getSponsorCartForm = | ||
| (cartFormId) => async (dispatch, getState) => { | ||
| const { currentSummitState, currentSponsorState } = getState(); | ||
| const { currentSummit } = currentSummitState; | ||
| const { entity: sponsor } = currentSponsorState; | ||
| const accessToken = await getAccessTokenSafely(); | ||
|
|
||
| dispatch(startLoading()); | ||
|
|
||
| const params = { | ||
| access_token: accessToken | ||
| }; | ||
|
|
||
| return getRequest( | ||
| createAction(REQUEST_CART_FORM), | ||
| createAction(RECEIVE_CART_FORM), | ||
| `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/carts/current/forms/${cartFormId}`, | ||
| authErrorHandler | ||
| )(params)(dispatch).then(() => { | ||
| dispatch(stopLoading()); | ||
| }); | ||
| }; | ||
|
|
||
| // get sponsor show form by id USING V2 API | ||
| export const getSponsorForm = (formId) => async (dispatch, getState) => { | ||
| const { currentSummitState } = getState(); | ||
| const { currentSummit } = currentSummitState; | ||
| const accessToken = await getAccessTokenSafely(); | ||
|
|
||
| dispatch(startLoading()); | ||
|
|
||
| const params = { | ||
| access_token: accessToken | ||
| }; | ||
|
|
||
| return getRequest( | ||
| createAction(REQUEST_CART_SPONSOR_FORM), | ||
| createAction(RECEIVE_CART_SPONSOR_FORM), | ||
| `${window.PURCHASES_API_URL}/api/v2/summits/${currentSummit.id}/show-forms/${formId}`, | ||
| authErrorHandler | ||
| )(params)(dispatch).then(() => { | ||
| dispatch(stopLoading()); | ||
| }); | ||
|
Comment on lines
+271
to
+273
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same issue: Use Consistent with the previous comment, replace 🛠️ Suggested fix- )(params)(dispatch).then(() => {
- dispatch(stopLoading());
- });
+ )(params)(dispatch)
+ .catch((err) => {
+ console.error(err);
+ })
+ .finally(() => {
+ dispatch(stopLoading());
+ });🤖 Prompt for AI Agents |
||
| }; | ||
|
|
||
| const normalizeItems = (items) => | ||
| items.map((item) => { | ||
| const { quantity, custom_rate, ...normalizedItem } = item; | ||
| const hasQtyFields = item.meta_fields.some( | ||
| (f) => f.class_field === "Form" && f.type_name === "Quantity" | ||
| ); | ||
| const metaFields = item.meta_fields.filter( | ||
| (item) => item.current_value !== null | ||
| ); | ||
|
|
||
| return { | ||
| ...normalizedItem, | ||
| ...(hasQtyFields ? {} : { quantity }), | ||
| ...(custom_rate > 0 ? { custom_rate } : {}), | ||
| meta_fields: metaFields | ||
| }; | ||
| }); | ||
|
Comment on lines
+276
to
+292
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
echo "=== Searching for custom_rate usage in form/page components ==="
rg -n "custom_rate" src/ --type=js -B 2 -A 2 | head -100
echo ""
echo "=== Looking for currency conversion utilities ==="
rg -n "amountToCents|toCents|dollarsTo|centsToDollars|currencyAmount" src/utils/ src/lib/ --type=js
echo ""
echo "=== Checking sponsor-cart-actions.js reducer line 88 for context ==="
sed -n '80,100p' src/actions/sponsor-cart-actions.jsRepository: fntechgit/summit-admin Length of output: 5249 🏁 Script executed: #!/bin/bash
echo "=== Finding MuiFormikPriceField implementation ==="
rg -n "const MuiFormikPriceField|function MuiFormikPriceField|export.*MuiFormikPriceField" src/ --type=js -A 10
echo ""
echo "=== Checking where normalizeItems is called ==="
rg -n "normalizeItems\(" src/actions/sponsor-cart-actions.js -B 5 -A 5
echo ""
echo "=== Finding currencyAmountFromCents definition ==="
rg -n "currencyAmountFromCents|amountFromCents" src/ --type=js -B 2 -A 3 | head -50Repository: fntechgit/summit-admin Length of output: 6032 🏁 Script executed: #!/bin/bash
echo "=== Looking for conversion functions to cents ==="
rg -n "amountToCents|toCents|inCents.*true" src/ --type=js
echo ""
echo "=== Check MuiFormikPriceField full implementation for conversion logic ==="
cat -n src/components/mui/formik-inputs/mui-formik-pricefield.jsRepository: fntechgit/summit-admin Length of output: 4094 Convert The Suggested fix...(custom_rate > 0 ? { custom_rate: amountToCents(custom_rate) } : {})Remember to import 🤖 Prompt for AI Agents |
||
|
|
||
| export const addCartForm = | ||
| (formId, addOnId, formValues) => async (dispatch, getState) => { | ||
| const { currentSummitState, currentSponsorState } = getState(); | ||
| const accessToken = await getAccessTokenSafely(); | ||
| const { currentSummit } = currentSummitState; | ||
| const { entity: sponsor } = currentSponsorState; | ||
|
|
||
| const params = { | ||
| access_token: accessToken | ||
| }; | ||
|
|
||
| dispatch(startLoading()); | ||
|
|
||
| const normalizedEntity = { | ||
| form_id: formId, | ||
| addon_id: addOnId, | ||
| discount_type: formValues.discount_type, | ||
| discount_amount: formValues.discount_amount, | ||
| items: normalizeItems(formValues.items) | ||
|
Comment on lines
+307
to
+312
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Normalize amount discounts before sending
💡 Suggested fix+import { FORM_DISCOUNT_OPTIONS } from "../utils/constants";
@@
const normalizedEntity = {
form_id: formId,
addon_id: addOnId,
discount_type: formValues.discount_type,
- discount_value: formValues.discount_amount,
+ discount_value:
+ formValues.discount_type === FORM_DISCOUNT_OPTIONS.AMOUNT
+ ? amountToCents(formValues.discount_amount || 0)
+ : formValues.discount_amount,
items: normalizeItems(formValues.items)
};🤖 Prompt for AI Agents |
||
| }; | ||
|
|
||
| return postRequest( | ||
| null, | ||
| createAction(FORM_CART_SAVED), | ||
| `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/carts/current/forms`, | ||
| normalizedEntity, | ||
| snackbarErrorHandler | ||
| )(params)(dispatch) | ||
| .then(() => { | ||
| dispatch( | ||
| snackbarSuccessHandler({ | ||
| title: T.translate("general.success"), | ||
| html: T.translate("sponsor_list.sponsor_added") | ||
| }) | ||
| ); | ||
| }) | ||
| .finally(() => dispatch(stopLoading())); | ||
| }; | ||
|
|
||
| export const updateCartForm = | ||
| (formId, formValues) => async (dispatch, getState) => { | ||
| const { currentSummitState, currentSponsorState } = getState(); | ||
| const accessToken = await getAccessTokenSafely(); | ||
| const { currentSummit } = currentSummitState; | ||
| const { entity: sponsor } = currentSponsorState; | ||
|
|
||
| const params = { | ||
| access_token: accessToken | ||
| }; | ||
|
|
||
| dispatch(startLoading()); | ||
|
|
||
| const normalizedEntity = { | ||
| discount_type: formValues.discount_type, | ||
| discount_amount: formValues.discount_amount, | ||
| items: normalizeItems(formValues.items) | ||
| }; | ||
|
|
||
| return putRequest( | ||
| null, | ||
| createAction(FORM_CART_SAVED), | ||
| `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/carts/current/forms/${formId}`, | ||
| normalizedEntity, | ||
| snackbarErrorHandler | ||
| )(params)(dispatch) | ||
| .then(() => { | ||
| dispatch( | ||
| snackbarSuccessHandler({ | ||
| title: T.translate("general.success"), | ||
| html: T.translate("sponsor_list.sponsor_added") | ||
| }) | ||
| ); | ||
| }) | ||
| .finally(() => dispatch(stopLoading())); | ||
| }; | ||
|
|
||
| /* ************************************************************************* */ | ||
| /* NOTES */ | ||
| /* ************************************************************************* */ | ||
|
|
||
| export const saveSponsorCartNote = | ||
| (note, type) => async (dispatch, getState) => { | ||
| const { currentSummitState, currentSponsorState } = getState(); | ||
| const accessToken = await getAccessTokenSafely(); | ||
| const { currentSummit } = currentSummitState; | ||
| const { entity: sponsor } = currentSponsorState; | ||
|
|
||
| const params = { | ||
| access_token: accessToken | ||
| }; | ||
|
|
||
| dispatch(startLoading()); | ||
|
|
||
| const normalizedEntity = { | ||
| content: note.content, | ||
| type | ||
| }; | ||
|
|
||
| if (note.id) { | ||
| return putRequest( | ||
| null, | ||
| createAction(SPONSOR_CART_NOTE_UPDATED), | ||
| `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/carts/current/notes/${note.id}`, | ||
| normalizedEntity, | ||
| snackbarErrorHandler | ||
| )(params)(dispatch) | ||
| .catch(console.log) // need to catch promise reject | ||
| .finally(() => { | ||
| dispatch(stopLoading()); | ||
| }); | ||
| } | ||
|
|
||
| return postRequest( | ||
| null, | ||
| createAction(SPONSOR_CART_NOTE_ADDED), | ||
| `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/carts/current/notes`, | ||
| normalizedEntity, | ||
| snackbarErrorHandler | ||
| )(params)(dispatch) | ||
| .catch(console.log) | ||
| .finally(() => dispatch(stopLoading())); | ||
| }; | ||
|
|
||
| export const deleteSponsorCartNote = (noteId) => async (dispatch, getState) => { | ||
| const { currentSummitState, currentSponsorState } = getState(); | ||
| const { currentSummit } = currentSummitState; | ||
| const { entity: sponsor } = currentSponsorState; | ||
| const accessToken = await getAccessTokenSafely(); | ||
| const params = { access_token: accessToken }; | ||
|
|
||
| dispatch(startLoading()); | ||
|
|
||
| return deleteRequest( | ||
| null, | ||
| createAction(SPONSOR_CART_NOTE_DELETED)({ noteId }), | ||
| `${window.PURCHASES_API_URL}/api/v1/summits/${currentSummit.id}/sponsors/${sponsor.id}/carts/current/notes/${noteId}`, | ||
| null, | ||
| snackbarErrorHandler | ||
| )(params)(dispatch) | ||
| .then(() => { | ||
| getSponsorCart()(dispatch, getState); | ||
| dispatch( | ||
| snackbarSuccessHandler({ | ||
| title: T.translate("general.success"), | ||
| html: T.translate("edit_sponsor.cart_tab.sponsor_note.deleted") | ||
| }) | ||
| ); | ||
| }) | ||
| .finally(() => { | ||
| dispatch(stopLoading()); | ||
| }); | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use
.finally()to ensure loading state is cleared on error.If the request fails after
authErrorHandlerruns,stopLoadingwon't be called, potentially leaving the UI in a loading state. Other functions in this file use.finally()for this purpose.🛠️ Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents