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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@
"moment": "^2.29.1",
"moment-duration-format": "^2.3.2",
"moment-timezone": "^0.5.33",
"openstack-uicore-foundation": "5.0.35",
"mui-color-input": "^9.0.0",
"openstack-uicore-foundation": "5.0.36",
"p-limit": "^6.1.0",
"path-browserify": "^1.0.1",
"postcss-loader": "^6.2.1",
Expand Down
149 changes: 87 additions & 62 deletions src/actions/company-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,13 @@ 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 All @@ -50,6 +47,8 @@ export const COMPANY_UPDATED = "COMPANY_UPDATED";
export const COMPANY_ADDED = "COMPANY_ADDED";
export const LOGO_ATTACHED = "LOGO_ATTACHED";
export const BIG_LOGO_ATTACHED = "BIG_LOGO_ATTACHED";
export const LOGO_REMOVED = "LOGO_REMOVED";
export const BIG_LOGO_REMOVED = "BIG_LOGO_REMOVED";

export const getCompanies =
(
Expand Down Expand Up @@ -92,9 +91,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 +113,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 +133,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 +154,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)
Comment thread
smarcet marked this conversation as resolved.
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 @@ -203,19 +206,19 @@ export const attachLogo = (entity, file, picAttr) => async (dispatch) => {
const uploadFile = picAttr === "logo" ? uploadLogo : uploadBigLogo;

if (entity.id) {
dispatch(uploadFile(entity, file));
} else {
return postRequest(
createAction(UPDATE_COMPANY),
createAction(COMPANY_ADDED),
`${window.API_BASE_URL}/api/v1/companies`,
normalizedEntity,
authErrorHandler,
entity
)(params)(dispatch).then((payload) => {
dispatch(uploadFile(payload.response, file));
});
return dispatch(uploadFile(entity, file));
}

return postRequest(
createAction(UPDATE_COMPANY),
createAction(COMPANY_ADDED),
`${window.API_BASE_URL}/api/v1/companies`,
normalizedEntity,
snackbarErrorHandler,
entity
)(params)(dispatch).then((payload) => {
dispatch(uploadFile(payload.response, file));
});
};

const uploadLogo = (entity, file) => async (dispatch) => {
Expand All @@ -225,17 +228,14 @@ const uploadLogo = (entity, file) => async (dispatch) => {
access_token: accessToken
};

postRequest(
return postRequest(
null,
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).finally(() => dispatch(stopLoading()));
};

const uploadBigLogo = (entity, file) => async (dispatch) => {
Expand All @@ -245,31 +245,56 @@ const uploadBigLogo = (entity, file) => async (dispatch) => {
access_token: accessToken
};

postRequest(
return postRequest(
null,
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).finally(() => dispatch(stopLoading()));
};

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

// remove # from color hexa
normalizedEntity.color = normalizedEntity.color.substr(1);
if (normalizedEntity.color.startsWith("#"))
normalizedEntity.color = normalizedEntity.color.substr(1);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

delete normalizedEntity.logo;
delete normalizedEntity.big_logo;
if (entity.id > 0) {
delete normalizedEntity.logo;
delete normalizedEntity.big_logo;
}

return normalizedEntity;
};

export const removeLogo = (entity, picAttr) => async (dispatch) => {
const accessToken = await getAccessTokenSafely();

dispatch(startLoading());

const params = {
access_token: accessToken
};

const endpoint =
picAttr === "logo"
? `${window.API_BASE_URL}/api/v1/companies/${entity.id}/logo`
: `${window.API_BASE_URL}/api/v1/companies/${entity.id}/logo/big`;

const action = picAttr === "logo" ? LOGO_REMOVED : BIG_LOGO_REMOVED;

return deleteRequest(
null,
createAction(action),
endpoint,
{},
snackbarErrorHandler
)(params)(dispatch).finally(() => dispatch(stopLoading()));
};

export const queryCompanies = debounce(async (input, callback) => {
const accessToken = await getAccessTokenSafely();
const endpoint = URI(`${window.API_BASE_URL}/api/v1/companies`);
Expand Down
38 changes: 31 additions & 7 deletions src/components/mui/formik-inputs/mui-formik-async-select.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,27 @@ const MuiFormikAsyncAutocomplete = ({
formatOption = (item) => ({ value: item.id.toString(), label: item.name }),
formatSelectedValue = null,
queryParams = [],
isMulti = false
isMulti = false,
defaultOptions,
...rest
}) => {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
const [field, meta, helpers] = useField(name);
const [options, setOptions] = useState([]);
const [loading, setLoading] = useState(false);
const [searchTerm, setSearchTerm] = useState("");

const value = field.value || (multiple ? [] : null);
const isMultiSelect = isMulti || multiple;

const value = field.value || (isMultiSelect ? [] : null);

// Sync a plain stored value back to the full option object
useEffect(() => {
if (!field.value || typeof field.value === "object" || options.length === 0)
return;
const match = options.find((o) => o.value === String(field.value));
if (match) helpers.setValue(match);
}, [options]);

const error = meta.touched && meta.error;

const fetchOptions = async (input = "") => {
Expand All @@ -45,7 +58,7 @@ const MuiFormikAsyncAutocomplete = ({
};

useEffect(() => {
if (searchTerm) {
if (!defaultOptions && searchTerm) {
const delayDebounce = setTimeout(() => {
fetchOptions(searchTerm);
}, DEBOUNCE_WAIT_250);
Expand All @@ -59,7 +72,7 @@ const MuiFormikAsyncAutocomplete = ({
}, []);

const handleChange = (event, selected) => {
if (!multiple) {
if (!isMultiSelect) {
const selectedValue = plainValue ? selected?.value || "" : selected;
helpers.setValue(selectedValue);
return;
Expand All @@ -82,11 +95,21 @@ const MuiFormikAsyncAutocomplete = ({
value={value}
onChange={handleChange}
loading={loading}
multiple={isMulti}
multiple={isMultiSelect}
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 Expand Up @@ -116,10 +139,11 @@ const MuiFormikAsyncAutocomplete = ({
)}
renderOption={(props, option, { selected }) => (
<li {...props}>
{multiple && <Checkbox checked={selected} sx={{ mr: 1 }} />}
{isMultiSelect && <Checkbox checked={selected} sx={{ mr: 1 }} />}
{option.label}
</li>
)}
{...rest}
/>
);
};
Expand Down
7 changes: 5 additions & 2 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,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 @@ -835,7 +836,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