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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"moment": "^2.29.1",
"moment-duration-format": "^2.3.2",
"moment-timezone": "^0.5.33",
"mui-color-input": "^9.0.0",
"openstack-uicore-foundation": "5.0.34",
"p-limit": "^6.1.0",
"path-browserify": "^1.0.1",
Expand Down
94 changes: 49 additions & 45 deletions src/actions/company-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,14 @@ import {
createAction,
stopLoading,
startLoading,
showMessage,
showSuccessMessage,
authErrorHandler,
escapeFilterValue,
fetchResponseHandler,
fetchErrorHandler
} from "openstack-uicore-foundation/lib/utils/actions";
import debounce from "lodash/debounce"
import debounce from "lodash/debounce";
import URI from "urijs";
import history from "../history";
import { snackbarErrorHandler, snackbarSuccessHandler } from "./base-actions";
Comment thread
tomrndom marked this conversation as resolved.
import { getAccessTokenSafely } from "../utils/methods";
import {
DEBOUNCE_WAIT,
Expand Down Expand Up @@ -92,9 +90,9 @@ export const getCompanies =
createAction(REQUEST_COMPANIES),
createAction(RECEIVE_COMPANIES),
`${window.API_BASE_URL}/api/v1/companies`,
authErrorHandler,
{ order, orderDir, page, term }
)(params)(dispatch).then(() => {
snackbarErrorHandler,
{ order, orderDir, page, perPage, term }
)(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
Expand All @@ -114,8 +112,8 @@ export const getCompany = (companyId) => async (dispatch) => {
null,
createAction(RECEIVE_COMPANY),
`${window.API_BASE_URL}/api/v1/companies/${companyId}`,
authErrorHandler
)(params)(dispatch).then(() => {
snackbarErrorHandler
)(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
Expand All @@ -134,8 +132,8 @@ export const deleteCompany = (companyId) => async (dispatch) => {
createAction(COMPANY_DELETED)({ companyId }),
`${window.API_BASE_URL}/api/v1/companies/${companyId}`,
null,
authErrorHandler
)(params)(dispatch).then(() => {
snackbarErrorHandler
)(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
Expand All @@ -155,38 +153,42 @@ export const saveCompany = (entity) => async (dispatch) => {
const normalizedEntity = normalizeEntity(entity);

if (entity.id) {
putRequest(
return putRequest(
createAction(UPDATE_COMPANY),
createAction(COMPANY_UPDATED),
`${window.API_BASE_URL}/api/v1/companies/${entity.id}`,
normalizedEntity,
authErrorHandler,
snackbarErrorHandler,
entity
)(params)(dispatch).then(() => {
dispatch(showSuccessMessage(T.translate("edit_company.company_saved")));
});
} else {
const success_message = {
title: T.translate("general.done"),
html: T.translate("edit_company.company_created"),
type: "success"
};
)(params)(dispatch)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[HIGH] Falta return — promesa no propagada al caller

La función async resuelve inmediatamente al llegar aquí porque putRequest(...)(params)(dispatch) no está retornado. El caller en handleSave ve una Promise<void> que ya está resuelta, por lo que .then() dispara antes de que la API responda.

Consecuencias: el dialog se cierra antes de confirmar éxito, la lista se recarga con datos desactualizados, y la protección de doble-submit queda sin efecto.

// ✅ agregar return en ambas ramas
return putRequest(...)(params)(dispatch)
  .then(...)
  .finally(...);

Comment thread
tomrndom marked this conversation as resolved.
Comment thread
tomrndom marked this conversation as resolved.
.then(() => {
dispatch(
snackbarSuccessHandler({
title: T.translate("general.success"),
html: T.translate("edit_company.company_saved")
})
);
})
.finally(() => dispatch(stopLoading()));
}

postRequest(
createAction(UPDATE_COMPANY),
createAction(COMPANY_ADDED),
`${window.API_BASE_URL}/api/v1/companies`,
normalizedEntity,
authErrorHandler,
entity
)(params)(dispatch).then(() => {
return postRequest(
createAction(UPDATE_COMPANY),
createAction(COMPANY_ADDED),
`${window.API_BASE_URL}/api/v1/companies`,
normalizedEntity,
snackbarErrorHandler,
entity
)(params)(dispatch)
.then(() => {
dispatch(
showMessage(success_message, () => {
history.push("/app/companies");
snackbarSuccessHandler({
title: T.translate("general.success"),
html: T.translate("edit_company.company_created")
})
);
});
}
})
.finally(() => dispatch(stopLoading()));
};

export const attachLogo = (entity, file, picAttr) => async (dispatch) => {
Expand All @@ -210,7 +212,7 @@ export const attachLogo = (entity, file, picAttr) => async (dispatch) => {
createAction(COMPANY_ADDED),
`${window.API_BASE_URL}/api/v1/companies`,
normalizedEntity,
authErrorHandler,
snackbarErrorHandler,
entity
)(params)(dispatch).then((payload) => {
dispatch(uploadFile(payload.response, file));
Expand All @@ -230,12 +232,13 @@ const uploadLogo = (entity, file) => async (dispatch) => {
createAction(LOGO_ATTACHED),
`${window.API_BASE_URL}/api/v1/companies/${entity.id}/logo`,
file,
authErrorHandler,
snackbarErrorHandler,
{ pic: entity.pic }
)(params)(dispatch).then(() => {
dispatch(stopLoading());
history.push(`/app/companies/${entity.id}`);
});
)(params)(dispatch)
.then(() => {
history.push(`/app/companies/${entity.id}`);
})
.finally(() => dispatch(stopLoading()));
};

const uploadBigLogo = (entity, file) => async (dispatch) => {
Expand All @@ -250,12 +253,13 @@ const uploadBigLogo = (entity, file) => async (dispatch) => {
createAction(BIG_LOGO_ATTACHED),
`${window.API_BASE_URL}/api/v1/companies/${entity.id}/logo/big`,
file,
authErrorHandler,
snackbarErrorHandler,
{ pic: entity.pic }
)(params)(dispatch).then(() => {
dispatch(stopLoading());
history.push(`/app/companies/${entity.id}`);
});
)(params)(dispatch)
.then(() => {
history.push(`/app/companies/${entity.id}`);
})
.finally(() => dispatch(stopLoading()));
};

const normalizeEntity = (entity) => {
Expand Down
17 changes: 14 additions & 3 deletions src/components/mui/formik-inputs/mui-formik-async-select.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ const MuiFormikAsyncAutocomplete = ({
formatOption = (item) => ({ value: item.id.toString(), label: item.name }),
formatSelectedValue = null,
queryParams = [],
isMulti = false
isMulti = false,
defaultOptions
}) => {
const [field, meta, helpers] = useField(name);
const [options, setOptions] = useState([]);
Expand All @@ -45,7 +46,7 @@ const MuiFormikAsyncAutocomplete = ({
};

useEffect(() => {
if (searchTerm) {
if (!defaultOptions && searchTerm) {
const delayDebounce = setTimeout(() => {
fetchOptions(searchTerm);
}, DEBOUNCE_WAIT_250);
Expand Down Expand Up @@ -86,7 +87,17 @@ const MuiFormikAsyncAutocomplete = ({
fullWidth
getOptionLabel={(option) => option.label || ""}
isOptionEqualToValue={(option, value) => option.value === value.value}
onInputChange={(e, newInput) => setSearchTerm(newInput)}
onInputChange={
!defaultOptions ? (e, newInput) => setSearchTerm(newInput) : undefined
}
filterOptions={
defaultOptions
? (options, { inputValue }) =>
options.filter((opt) =>
opt.label.toLowerCase().includes(inputValue.toLowerCase())
)
: undefined // MUI usa su default que no filtra (deja que la API filtre)
}
renderInput={(params) => (
<TextField
{...params}
Expand Down
7 changes: 5 additions & 2 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,8 @@
"companies": "Companies",
"add_company": "Add Company",
"member_level": "Member Level",
"delete_company_warning": "Are you sure you want to delete company",
"delete_company_warning": "Are you sure you want to delete company {name}",
"no_results": "No items found for this search criteria.",
"placeholders": {
"search_companies": "Search by Company Name"
}
Expand Down Expand Up @@ -827,7 +828,9 @@
"placeholders": {
"select_country": "Select Country",
"sponsored_project": "Select Sponsored Project",
"sponsorship_type": "Select Tier"
"sponsorship_type": "Select Tier",
"select_project_first": "Select a project first",
"no_options": "No options"
}
},
"tag_list": {
Expand Down
Loading
Loading