Skip to content

Commit

Permalink
Merge pull request #980 from singnet/ui-fix
Browse files Browse the repository at this point in the history
Added feedback modals, fixed header and footer
  • Loading branch information
MarinaFedy authored Jan 30, 2025
2 parents d115b1b + e2fb794 commit 6392e23
Show file tree
Hide file tree
Showing 26 changed files with 701 additions and 53 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ REACT_APP_SNET_SUPPORT_MAIL=
REACT_APP_SENTRY_DSN=
REACT_APP_IS_ALL_SERVICES_AVAILIBLE=
REACT_APP_TRAINING_ENABLE=
REACT_APP_FEEDBACK_ATTACHMENTS_FOLDER=
REACT_APP_FEEDBACK_AUTH_TOKEN=
REACT_APP_FEEDBACK_BASE_URL=
REACT_APP_FEEDBACK_AUTH_TOKEN=
6 changes: 6 additions & 0 deletions config-overrides.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ const options = {
"@commonComponents": path.resolve(__dirname, "src/assets/thirdPartyServices/common/"),
"@standardComponents": path.resolve(__dirname, "src/assets/thirdPartyServices/standardComponents/"),
"@integratedComponents": path.resolve(__dirname, "src/components/common/"),
"@assets": path.resolve(__dirname, "src/assets"),
"@common": path.resolve(__dirname, "src/components/common"),
"@components": path.resolve(__dirname, "src/components"),
"@config": path.resolve(__dirname, "src/config"),
"@utility": path.resolve(__dirname, "src/utility"),
"@redux": path.resolve(__dirname, "src/Redux"),
},
};

Expand Down
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { CircularProgress } from "@mui/material";
import initHotjar from "./assets/externalScripts/hotjar";
import initGDPRNotification from "./assets/externalScripts/gdpr";
import PaymentCancelled from "./components/ServiceDetails/PaymentCancelled";
import FeedbackFormModal from "@components/FeedbackFormModal";

const ForgotPassword = lazy(() => import("./components/Login/ForgotPassword"));
const ForgotPasswordSubmit = lazy(() => import("./components/Login/ForgotPasswordSubmit"));
Expand Down Expand Up @@ -185,6 +186,7 @@ const App = () => {
</Switch>
</Suspense>
</Router>
<FeedbackFormModal />
<AppLoader />
</ThemeProvider>
);
Expand Down
21 changes: 12 additions & 9 deletions src/Redux/actionCreators/ServiceTrainingActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { startAppLoader, stopAppLoader } from "./LoaderActions";
import { getServiceClient } from "./SDKActions";
import { fetchAuthenticatedUser, updateMetamaskWallet } from "./UserActions";
import { modelStatus } from "../reducers/ServiceTrainingReducer";
import { DatafactoryInstanceS3, DatasetS3Endpoints, TrainingInstanceS3 } from "../../config/DatasetS3Client";
import { DatafactoryInstanceS3, filesToS3Endpoints, TrainingInstanceS3 } from "../../config/DatasetS3Client";
// import { userActions } from ".";
export const SET_MODEL_DETAILS = "SET_MODEL_DETAILS";
export const SET_MODELS_LIST = "SET_MODELS_LIST";
Expand Down Expand Up @@ -193,7 +193,8 @@ const modelStatusByNumber = {

export const publishDatasetForTraining = (fileBlob, name) => async (dispatch) => {
try {
const linkAndKeyDataset = await dispatch(publishDatasetToS3(fileBlob, name, TrainingInstanceS3));
const { email } = await dispatch(fetchAuthenticatedUser());
const linkAndKeyDataset = await publishFilesToS3(fileBlob, name, TrainingInstanceS3, email);
return linkAndKeyDataset;
} catch (error) {
console.log("publishing Dataset For Training error: ", error);
Expand All @@ -202,20 +203,22 @@ export const publishDatasetForTraining = (fileBlob, name) => async (dispatch) =>

export const publishDatasetForImproving = (fileBlob, name) => async (dispatch) => {
try {
const linkAndKeyDataset = await dispatch(publishDatasetToS3(fileBlob, name, DatafactoryInstanceS3));
const { email } = await dispatch(fetchAuthenticatedUser());
const linkAndKeyDataset = await publishFilesToS3(fileBlob, name, DatafactoryInstanceS3, email);
return linkAndKeyDataset;
} catch (error) {
console.log("publishing Dataset For Improving error: ", error);
}
};

export const publishDatasetToS3 = (fileBlob, name, S3Instance) => async (dispatch) => {
const { email } = await dispatch(fetchAuthenticatedUser());

export const publishFilesToS3 = async (fileBlob, name, S3Instance, folder, email) => {
try {
const baseUrl = S3Instance.getUri();
const fileKey = name + "_" + email + "_" + Date.now();
const response = await S3Instance.get(DatasetS3Endpoints.UPLOAD, { params: { key: fileKey } });
let fileKey = Date.now() + "_" + email + "_" + name;
if (folder) {
fileKey = folder + "/" + fileKey;
}
const response = await S3Instance.get(filesToS3Endpoints.UPLOAD, { params: { key: fileKey } });
await axios.put(response.data.uploadURL, fileBlob);
return {
url: `${baseUrl}/download?key=${fileKey}`,
Expand All @@ -227,7 +230,7 @@ export const publishDatasetToS3 = (fileBlob, name, S3Instance) => async (dispatc
};

export const getDatasetSizeFromS3 = async (fileKey, S3instance) => {
return S3instance.get(DatasetS3Endpoints.DOWNLOAD, { params: { key: fileKey, action: "getsize" } })
return S3instance.get(filesToS3Endpoints.DOWNLOAD, { params: { key: fileKey, action: "getsize" } })
.then((response) => {
return response.data.fileSize;
})
Expand Down
Binary file added src/assets/images/feedback.webp
Binary file not shown.
9 changes: 3 additions & 6 deletions src/components/AiMarketplace/MainSection/Filter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@ import { useStyles } from "./styles";
import SearchInputToggler from "./SearchInputToggler";
import ServiceSortOptions from "./ServiceSortOptions";
import ViewToggler from "./ViewToggler";
import StyledDropdown from "../../../common/StyledDropdown";
import { serviceActions } from "../../../../Redux/actionCreators";
import {
defaultPaginationParameters,
generateOrganizationsFilterObject,
} from "../../../../utility/constants/Pagination";
import StyledDropdown from "@common/StyledDropdown";
import { serviceActions } from "@redux/actionCreators";
import { defaultPaginationParameters, generateOrganizationsFilterObject } from "@utility/constants/Pagination";

const Filter = ({ listView, total_count, handleSearchChange, toggleView, currentPagination, showToggler }) => {
const { filterData, pagination } = useSelector((state) => state.serviceReducer);
Expand Down
168 changes: 168 additions & 0 deletions src/components/FeedbackFormModal/FeedbackForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { useDispatch } from "react-redux";
import "./styles.css";
import { FormControl, InputLabel, MenuItem, Select, TextField } from "@mui/material";
import { feedbackCategories, formFieldsMeta, initialFormState, textAreaField } from "./meta";
import { eventListenerForKey } from "@utility/listenerForKey";
import snetValidator from "@utility/snetValidator";
import AlertBox, { alertTypes } from "@common/AlertBox";
import { LoaderContent } from "@utility/constants/LoaderContent";
import { loaderActions } from "@redux/actionCreators";
import StyledButton from "@common/StyledButton";
import UploadAttacments from "./UploadAttacments";

export const supportTypes = {
SNET: "SNET",
PROVIDER: "PROVIDER",
};

const FeedbackForm = ({ closeForm, sendFeedbackAPI }) => {
const dispatch = useDispatch();

const [formFields, setFormFields] = useState(initialFormState);
const [category, setCategory] = useState("");
const [isRequestHandling, setIsRequestHandling] = useState(false);
const [isSubmitAvailable, setIsSubmitAvailable] = useState(false);
const [attachmentUrls, setAttachmentUrls] = useState([]);
const [alert, setAlert] = useState();

const sendFeedback = async () => {
try {
dispatch(loaderActions.startAppLoader(LoaderContent.FEEDBACK));
setIsRequestHandling(true);
const response = await sendFeedbackAPI({ ...formFields, category, attachmentUrls });
if (response?.status === "failed") {
throw new Error(response?.error.message);
}
setAlert({ type: alertTypes.SUCCESS, message: "Our technical support will get in touch with you soon!" });
} catch (error) {
setAlert({ type: alertTypes.ERROR, message: error?.message });
} finally {
await new Promise((resolve) => setTimeout(resolve, 3000));
resetForm();
setIsRequestHandling(false);
dispatch(loaderActions.stopAppLoader());
closeForm();
}
};

const listener = eventListenerForKey({ keyValue: "Enter", callback: sendFeedback, isCtrlPress: true });

useEffect(() => {
if (isSubmitAvailable) {
document.addEventListener("keypress", listener, false);
}
return () => document.removeEventListener("keypress", listener, false);
}, [isSubmitAvailable, listener]);

const invalidMessageByField = useMemo(
() => ({
email: snetValidator.validators.validEmail(formFields.email),
name: !formFields.name ? "name is empty" : undefined,
feedback: !formFields.feedback ? "feedback is empty" : undefined,
category: !category ? "category is empty" : undefined,
}),
[formFields.email, formFields.name, formFields.feedback, category]
);

const invalidMessageByFieldArray = useMemo(() => Object.values(invalidMessageByField), [invalidMessageByField]);
const isFieldMessageEmty = useMemo(
() => invalidMessageByFieldArray.reduce((accumulator, invalidMessage) => accumulator && !invalidMessage, true),
[invalidMessageByFieldArray]
);

const checkIsSubmitAvailable = useCallback(() => {
setIsSubmitAvailable(isFieldMessageEmty && !isRequestHandling);
}, [isFieldMessageEmty, isRequestHandling]);

useEffect(() => {
checkIsSubmitAvailable();
}, [formFields.name, formFields.feedback, category, checkIsSubmitAvailable]);

const resetForm = () => {
setFormFields(initialFormState);
};

const SelectCategory = () => {
return (
<FormControl fullWidth>
<InputLabel id="select-label">Select a category</InputLabel>
<Select
labelId="select-label"
id="select"
value={category}
label="Select a category"
onChange={(event) => {
setCategory(event.target.value);
}}
>
{feedbackCategories.map((feedbackCategory) => (
<MenuItem key={feedbackCategory.value} value={feedbackCategory.value}>
{feedbackCategory.title}
</MenuItem>
))}
</Select>
</FormControl>
);
};

const fieldInvalidMessage = (formField) => {
if (formField?.isValidate && Boolean(formFields[formField.id])) {
const invalidMessage = invalidMessageByField[formField.id];
return invalidMessage;
}
return null;
};

const onInputFormField = (event, formFieldId) => {
setFormFields({ ...formFields, [formFieldId]: event.target.value });
};

return (
<div className="feedback-form-holder feedback-form">
<form className="feedback-form">
<div className="feedback-form-fields">
<SelectCategory />
{Object.values(formFieldsMeta).map((formField) => (
<TextField
key={formField.id}
id={formField.id}
value={formFields[formField.id]}
variant="outlined"
required={formField.isRequired}
label={formField.label}
placeholder={formField.placeholder}
onChange={(event) => {
onInputFormField(event, formField.id);
}}
error={!!fieldInvalidMessage(formField)}
helperText={fieldInvalidMessage(formField)}
/>
))}
<TextField
key={textAreaField.id}
id={textAreaField.id}
value={formFields[textAreaField.id]}
variant="outlined"
required={textAreaField.isRequired}
label={textAreaField.label}
placeholder={textAreaField.placeholder}
multiline
minRows={textAreaField.rows}
onChange={(event) => {
onInputFormField(event, textAreaField.id);
}}
/>
</div>
<UploadAttacments email={formFields?.email} setAlert={setAlert} setFilesUrls={setAttachmentUrls} />
<AlertBox type={alert?.type} message={alert?.message} />
<div className="submit-btn-container">
<StyledButton type="transparent" btnText="Cancel" onClick={closeForm} />
<StyledButton type="blue" btnText="Confirm" disabled={!isSubmitAvailable} onClick={sendFeedback} />
</div>
</form>
</div>
);
};

export default FeedbackForm;
17 changes: 17 additions & 0 deletions src/components/FeedbackFormModal/FeedbackFormModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import FeedbackForm from "./FeedbackForm";
import "./styles.css";
import SNETDialog from "@common/SNETDialog";

const FeedbackFormModal = ({ isModalVisible, setIsModalVisible, sendFeedbackAPI }) => {
const closeModal = () => {
setIsModalVisible(false);
};

return (
<SNETDialog showCloseButton isDialogOpen={isModalVisible} onDialogClose={closeModal} title="Feedback form">
<FeedbackForm sendFeedbackAPI={sendFeedbackAPI} closeForm={closeModal} />
</SNETDialog>
);
};

export default FeedbackFormModal;
15 changes: 15 additions & 0 deletions src/components/FeedbackFormModal/OpenFormButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import feedbackImage from "@assets/images/feedback.webp";
import Button from "@mui/material/Button";
import "./styles.css";

const OpenFormButton = ({ openForm }) => {
return (
<div className="feedback-form-launcher-holder">
<Button onClick={openForm} className="feedback-form-launcher">
<img src={feedbackImage} alt="feedback" />
</Button>
</div>
);
};

export default OpenFormButton;
Loading

0 comments on commit 6392e23

Please sign in to comment.