diff --git a/frontend/src/pages/package.js b/frontend/src/pages/package.js index b3966a47..198d2b3b 100644 --- a/frontend/src/pages/package.js +++ b/frontend/src/pages/package.js @@ -23,6 +23,7 @@ import { } from "../store/actions/packageActions"; import ShowUserListDialog from "./showUserListDialog"; import ReportPackageForm from "./reportPackageForm"; +import RatePackageForm from "./ratePackageForm"; import { Button } from "react-bootstrap"; import PackageRatingGraph from "./packageRatingGraph"; @@ -37,6 +38,7 @@ const PackagePage = () => { const [togglePackageMaintainersDialog, settogglePackageMaintainersDialog] = useState(false); const [showReportForm, setShowReportForm] = useState(false); + const [showRateForm, setRateForm] = useState(false); const handleIconsClick = (value) => { if (value === iconsActive) { @@ -131,7 +133,7 @@ const PackagePage = () => { {data.description} - {sideBar(data, setShowReportForm)} + {sideBar(data, setShowReportForm, setRateForm)} @@ -202,6 +204,12 @@ const PackagePage = () => { + setRateForm(false)} + > { +const sideBar = (data, setShowReportForm, setRateForm) => { return (

Install (add to fpm.toml)

@@ -278,13 +286,22 @@ const sideBar = (data, setShowReportForm) => {

Last publish

{updatedDays(data.updated_at)} days ago
- +
+ + +
); }; diff --git a/frontend/src/pages/ratePackageForm.js b/frontend/src/pages/ratePackageForm.js new file mode 100644 index 00000000..eeb81198 --- /dev/null +++ b/frontend/src/pages/ratePackageForm.js @@ -0,0 +1,90 @@ +import React, { useEffect, useState } from "react"; +import { Form, Button, Modal, Spinner } from "react-bootstrap"; +import { useDispatch, useSelector } from "react-redux"; +import { toast, ToastContainer } from "react-toastify"; +import { + ratePackage, + resetErrorMessage, +} from "../store/actions/ratePackageActions"; + +const RatePackageForm = (props) => { + const dispatch = useDispatch(); + const [rating, setRating] = useState(""); + const accessToken = useSelector((state) => state.auth.accessToken); + const isLoading = useSelector((state) => state.ratePackage.isLoading); + const statusCode = useSelector((state) => state.ratePackage.statuscode); + const message = useSelector((state) => state.ratePackage.message); + + const handleSubmit = async (e) => { + e.preventDefault(); + dispatch( + ratePackage( + { rating: rating, namespace: props.namespace, package: props.package }, + accessToken + ) + ); + }; + + useEffect(() => { + if (statusCode === 200) { + toast.success(message); + } else { + toast.error(message); + } + + dispatch(resetErrorMessage()); + }, [statusCode]); + + return ( +
+ + + Rate Package + + + + + Please rate this package on a scale of 1-5 + setRating(e.target.value)} + /> + + + + {!isLoading ? ( + + ) : ( +
+ + Loading... + +
+ )} +
+
+
+ ); +}; + +export default RatePackageForm; diff --git a/frontend/src/store/actions/ratePackageActions.js b/frontend/src/store/actions/ratePackageActions.js new file mode 100644 index 00000000..d9ed385a --- /dev/null +++ b/frontend/src/store/actions/ratePackageActions.js @@ -0,0 +1,51 @@ +import axios from "axios"; + +export const RATE_PACKAGE_REQUEST = "RATE_PACKAGE_REQUEST"; +export const RATE_PACKAGE_SUCCESS = "RATE_PACKAGE_SUCCESS"; +export const RATE_PACKAGE_FAILURE = "RATE_PACKAGE_FAILURE"; +export const RESET_ERROR_MESSAGE = "RESET_ERROR_MESSAGE"; + +export const ratePackage = (data, access_token) => async (dispatch) => { + let formData = new FormData(); + formData.append("rating", data.rating); + + let packageName = data.package; + let namespaceName = data.namespace; + + try { + dispatch({ + type: RATE_PACKAGE_REQUEST, + }); + + const result = await axios({ + method: "post", + url: `${process.env.REACT_APP_REGISTRY_API_URL}/ratings/${namespaceName}/${packageName}`, + data: formData, + headers: { + Authorization: `Bearer ${access_token}`, + }, + }); + + dispatch({ + type: RATE_PACKAGE_SUCCESS, + payload: { + message: result.data.message, + statuscode: result.data.code, + }, + }); + } catch (error) { + dispatch({ + type: RATE_PACKAGE_FAILURE, + payload: { + message: error.response.data.message, + statuscode: error.response.data.code, + }, + }); + } +}; + +export const resetErrorMessage = () => (dispatch) => { + dispatch({ + type: RESET_ERROR_MESSAGE, + }); +}; diff --git a/frontend/src/store/reducers/ratePackageReducer.js b/frontend/src/store/reducers/ratePackageReducer.js new file mode 100644 index 00000000..c4a055be --- /dev/null +++ b/frontend/src/store/reducers/ratePackageReducer.js @@ -0,0 +1,48 @@ +import { + RATE_PACKAGE_REQUEST, + RATE_PACKAGE_SUCCESS, + RATE_PACKAGE_FAILURE, + RESET_ERROR_MESSAGE, +} from "../actions/ratePackageActions"; + +const initialState = { + isLoading: false, + error: null, + message: null, + statuscode: 0, +}; + +const ratePackageReducer = (state = initialState, action) => { + switch (action.type) { + case RATE_PACKAGE_REQUEST: + return { + ...state, + isLoading: true, + }; + case RATE_PACKAGE_SUCCESS: + return { + ...state, + isLoading: false, + message: action.payload.message, + statuscode: action.payload.statuscode, + }; + case RATE_PACKAGE_FAILURE: + return { + ...state, + isLoading: false, + message: action.payload.message, + statuscode: action.payload.statuscode, + }; + case RESET_ERROR_MESSAGE: + return { + ...state, + error: null, + message: null, + statuscode: 0, + }; + default: + return state; + } +}; + +export default ratePackageReducer; diff --git a/frontend/src/store/reducers/rootReducer.js b/frontend/src/store/reducers/rootReducer.js index 5d8f1858..58dd6fb0 100644 --- a/frontend/src/store/reducers/rootReducer.js +++ b/frontend/src/store/reducers/rootReducer.js @@ -19,6 +19,7 @@ import verifyEmailReducer from "./verifyEmailReducer"; import userListReducer from "./userListReducer"; import reportPackageReducer from "./reportPackageReducer"; import viewMalicousReportsReducer from "./viewMalicousReportsReducer"; +import ratePackageReducer from "./ratePackageReducer"; const rootReducer = combineReducers({ auth: authReducer, @@ -41,6 +42,7 @@ const rootReducer = combineReducers({ archives: archivesReducer, reportPackage: reportPackageReducer, malicousReport: viewMalicousReportsReducer, + ratePackage: ratePackageReducer, }); export default rootReducer;