From ec561159c2b4baa9914f205643bdc6f5812c61c9 Mon Sep 17 00:00:00 2001 From: Radhika Patel Date: Tue, 8 Oct 2024 14:35:27 +0530 Subject: [PATCH] Production deployment v1.0.1 --- package.json | 2 +- src/components/Alerts/Alerts.js | 6 +- src/components/StripeCard/StripeCard.js | 2 +- src/pages/Login/Login.js | 6 +- src/pages/ProductRoadmap/components/Kanban.js | 1 + src/pages/ProductRoadmap/forms/StatusBoard.js | 134 +++++++++++++++--- src/pages/Register/Register.js | 6 +- .../RegistrationFinish/RegistrationFinish.js | 16 ++- .../mutations/authUser/loginMutation.js | 32 +++-- .../mutations/authUser/registerMutation.js | 10 +- 10 files changed, 167 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index e9db1138d..08da3bdb1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "buildly-react-template", - "version": "v1.0.0", + "version": "v1.0.1", "description": "Frontend Template from Buildly built using the React framework", "main": "src/index.js", "private": true, diff --git a/src/components/Alerts/Alerts.js b/src/components/Alerts/Alerts.js index deb913e5e..8d59e2f8b 100644 --- a/src/components/Alerts/Alerts.js +++ b/src/components/Alerts/Alerts.js @@ -14,18 +14,22 @@ const useStyles = makeStyles((theme) => ({ success: { backgroundColor: '#009900', color: '#000', + whiteSpace: 'pre-wrap', }, info: { backgroundColor: '#0099CC', color: '#000', + whiteSpace: 'pre-wrap', }, warning: { backgroundColor: '#FFCC33', color: '#000', + whiteSpace: 'pre-wrap', }, error: { backgroundColor: '#FF0033', color: '#000', + whiteSpace: 'pre-wrap', }, })); @@ -48,7 +52,7 @@ const Alerts = () => { { - {cardError} + {cardError && cardError.message} ); diff --git a/src/pages/Login/Login.js b/src/pages/Login/Login.js index f3b0fb901..b7ec63b15 100644 --- a/src/pages/Login/Login.js +++ b/src/pages/Login/Login.js @@ -14,7 +14,7 @@ import { } from '@mui/material'; import logo from '@assets/buildly-product-labs-logo.png'; import Copyright from '@components/Copyright/Copyright'; -import GithubLogin from '@components/SocialLogin/GithubLogin'; +// import GithubLogin from '@components/SocialLogin/GithubLogin'; import Loader from '@components/Loader/Loader'; import { useInput } from '@hooks/useInput'; import useAlert from '@hooks/useAlert'; @@ -202,7 +202,7 @@ const Login = ({ history }) => { - + {/* ----OR---- @@ -211,7 +211,7 @@ const Login = ({ history }) => { history={history} disabled={isLoginLoading || isPasswordCheckLoading || isSocialLoginLoading} /> - + */} { let cols = {}; if (statuses && !_.isEmpty(statuses)) { + statuses.sort((a, b) => (a.order_id > b.order_id ? 1 : -1)); _.forEach(statuses, (sts) => { const feats = _.filter(features, { status: sts.status_uuid }); const iss = _.filter(issues, { status: sts.status_uuid }); diff --git a/src/pages/ProductRoadmap/forms/StatusBoard.js b/src/pages/ProductRoadmap/forms/StatusBoard.js index 1279dc23c..5e8c1da3c 100644 --- a/src/pages/ProductRoadmap/forms/StatusBoard.js +++ b/src/pages/ProductRoadmap/forms/StatusBoard.js @@ -11,7 +11,7 @@ import { Button, Autocomplete, Chip, - MenuItem, + MenuItem, Typography, } from '@mui/material'; import FormModal from '@components/Modal/FormModal'; import Loader from '@components/Loader/Loader'; @@ -21,6 +21,7 @@ import { useCreateStatusMutation } from '@react-query/mutations/release/createSt import { useUpdateStatusMutation } from '@react-query/mutations/release/updateStatusMutation'; import { useDeleteStatusMutation } from '@react-query/mutations/release/deleteStatusMutation'; import { STATUSTYPES } from '../ProductRoadmapConstants'; +import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'; const useStyles = makeStyles((theme) => ({ form: { @@ -40,6 +41,17 @@ const useStyles = makeStyles((theme) => ({ marginTop: '1em', textAlign: 'center', }, + orderLanesContainer: { + // width: '100%', + // display: 'Flex', + // flexDirection: 'Row', + flexWrap: 'Wrap', + border: '1.5px solid lightgray', + padding: '16px 8px', + }, + laneChip: { + margin: '8px', + }, })); const StatusBoard = ({ history, location }) => { @@ -55,6 +67,7 @@ const StatusBoard = ({ history, location }) => { const [openFormModal, setFormModal] = useState(true); const [openConfirmModal, setConfirmModal] = useState(false); const [status, setStatus] = useState([]); + const [orderedLanes, setOrderedLanes] = useState([]); const [defaultStatus, setDefaultStatus] = useState(''); const [formError, setFormError] = useState({}); @@ -67,7 +80,12 @@ const StatusBoard = ({ history, location }) => { useEffect(() => { const filteredStatus = _.filter(statuses, { product_uuid }); const statusDefault = _.find(filteredStatus, (s) => s.is_default_status); - setStatus(_.map(filteredStatus, 'name')); + const initialStatuses = _.map(filteredStatus, 'name'); + setStatus(initialStatuses); + + // Initialize the ordered lanes/statuses + const lanesCopy = JSON.parse(JSON.stringify(filteredStatus)); + setOrderedLanes(lanesCopy.sort((a, b) => (a.order_id > b.order_id ? 1 : -1))); setDefaultStatus((!!statusDefault && statusDefault.name) || ''); }, [statuses]); @@ -105,10 +123,12 @@ const StatusBoard = ({ history, location }) => { const onStatusChange = (value) => { switch (true) { case (value.length > status.length): + setOrderedLanes([...orderedLanes, { name: _.last(value) }]); setStatus([...status, _.last(value)]); break; case (value.length < status.length): + setOrderedLanes(orderedLanes.filter((lane) => value.includes(lane.name))); setStatus(value); break; @@ -124,13 +144,17 @@ const StatusBoard = ({ history, location }) => { const handleSubmit = (event) => { event.preventDefault(); if (!editStatus) { - const statusData = _.map(status, (col) => ({ - product_uuid, - name: col, - description: col, - status_tracking_id: null, - is_default_status: _.isEqual(col, defaultStatus), - })); + const statusData = status.map((col) => { + const index = orderedLanes.findIndex((lane) => lane.name === col); + return { + product_uuid, + name: col, + description: col, + status_tracking_id: null, + is_default_status: _.isEqual(col, defaultStatus), + order_id: index, + }; + }); createStatusMutation(statusData); } else { @@ -153,10 +177,11 @@ const StatusBoard = ({ history, location }) => { }); _.forEach(status, (st) => { + const index = orderedLanes.findIndex((lane) => lane.name === st); if (_.includes(nameList, st)) { const existingStatus = _.find(filteredStatus, { name: st }); if (!_.isEmpty(existingStatus)) { - editStatusData = [...editStatusData, { ...existingStatus, is_default_status: _.isEqual(st, defaultStatus) }]; + editStatusData = [...editStatusData, { ...existingStatus, is_default_status: _.isEqual(st, defaultStatus), order_id: index }]; } } else { createStatusData = [ @@ -167,6 +192,7 @@ const StatusBoard = ({ history, location }) => { description: st, status_tracking_id: null, is_default_status: _.isEqual(st, defaultStatus), + order_id: index, }, ]; } @@ -199,6 +225,40 @@ const StatusBoard = ({ history, location }) => { return errorExists; }; + const onDragEnd = (result) => { + if (!result.destination) return; + + const items = Array.from(orderedLanes); + const [reorderedItem] = items.splice(result.source.index, 1); + items.splice(result.destination.index, 0, reorderedItem); + + setOrderedLanes(items); + }; + + // a little function to help us with reordering the result + const reorder = (list, startIndex, endIndex) => { + const result = Array.from(list); + const [removed] = result.splice(startIndex, 1); + result.splice(endIndex, 0, removed); + + return result; + }; + + const getItemStyle = (draggableStyle, isDragging) => ({ + // some basic styles to make the items look a bit nicer + userSelect: 'none', + padding: 16, + marginBottom: '0 8px 0 0', + + // styles we need to apply on draggables + ...draggableStyle, + }); + const getListStyle = (isDraggingOver) => ({ + display: 'flex', + padding: 8, + width: '100%', + }); + return ( <> {openFormModal && ( @@ -246,7 +306,45 @@ const StatusBoard = ({ history, location }) => { )} /> + {!_.isEmpty(status) && ( + <> + + Drag to re-order columns + + + {(provided, snapshot) => ( +
+ {orderedLanes.map((lane, index) => ( + + {/* eslint-disable-next-line no-shadow */} + {(provided, snapshot) => ( +
+ +
+ )} +
+ ))} + {provided.placeholder} +
+ )} +
+
+
+ { ))} + )}
+ diff --git a/src/pages/Register/Register.js b/src/pages/Register/Register.js index be781f3b7..6520ce43a 100644 --- a/src/pages/Register/Register.js +++ b/src/pages/Register/Register.js @@ -15,7 +15,7 @@ import { } from '@mui/material'; import logo from '@assets/buildly-product-labs-logo.png'; import Copyright from '@components/Copyright/Copyright'; -import GithubLogin from '@components/SocialLogin/GithubLogin'; +// import GithubLogin from '@components/SocialLogin/GithubLogin'; import { useInput } from '@hooks/useInput'; import useAlert from '@hooks/useAlert'; import { routes } from '@routes/routesConstants'; @@ -460,7 +460,7 @@ const Register = ({ history }) => { - + {/* ----OR---- @@ -469,7 +469,7 @@ const Register = ({ history }) => { history={history} disabled={isRegisterLoading || isSocialLoginLoading || isInviteTokenCheckLoading} /> - + */} Already have an account? Sign in diff --git a/src/pages/RegistrationFinish/RegistrationFinish.js b/src/pages/RegistrationFinish/RegistrationFinish.js index f399ed1c5..17dbbf16f 100644 --- a/src/pages/RegistrationFinish/RegistrationFinish.js +++ b/src/pages/RegistrationFinish/RegistrationFinish.js @@ -1,4 +1,4 @@ -import React, { useContext, useState } from 'react'; +import React, { useState } from 'react'; import { Button, Card, CardActions, @@ -17,22 +17,22 @@ import StripeCard from '@components/StripeCard/StripeCard'; import { getStripeProductQuery } from '@react-query/queries/authUser/getStripeProductQuery'; import { useAddSubscriptionMutation } from '../../react-query/mutations/authUser/addSubscriptionMutation'; import './RegistrationFinish.css'; -import { UserContext } from '../../context/User.context'; +import { getUser } from '../../context/User.context'; import { useInput } from '../../hooks/useInput'; import useAlert from '../../hooks/useAlert'; import { hasAdminRights, hasGlobalAdminRights } from '../../utils/permissions'; import { validators } from '../../utils/validators'; import { routes } from '../../routes/routesConstants'; +import Loader from '../../components/Loader/Loader'; const RegistrationFinish = ({ history }) => { - const user = useContext(UserContext); + const user = getUser(); const { displayAlert } = useAlert(); const isAdmin = hasAdminRights(user) || hasGlobalAdminRights(user); const [organization, setOrganization] = useState(null); const stripe = useStripe(); const elements = useElements(); - const cardError = () => { }; - const setCardError = () => { }; + const [cardError, setCardError] = useState(false); const { data: stripeProductData, isLoading: isStripeProductsLoading } = useQuery( ['stripeProducts'], @@ -40,7 +40,7 @@ const RegistrationFinish = ({ history }) => { { refetchOnWindowFocus: false }, ); - const { mutate: addSubscriptionMutation } = useAddSubscriptionMutation(displayAlert, history, routes.PRODUCT_PORTFOLIO); + const { mutate: addSubscriptionMutation, isLoading: isAddSubscriptionLoading } = useAddSubscriptionMutation(displayAlert, history, routes.PRODUCT_PORTFOLIO); if (user) { if (!organization) { @@ -89,11 +89,14 @@ const RegistrationFinish = ({ history }) => { }; if (!validationError) { addSubscriptionMutation(formValue); + } else { + setCardError(validationError); } }; return ( <> + {(isStripeProductsLoading || isAddSubscriptionLoading) && } {isAdmin ? ( <> @@ -150,6 +153,7 @@ const RegistrationFinish = ({ history }) => { variant="contained" color="primary" className="btn-space" + disabled={!product.value || cardError || isStripeProductsLoading || isAddSubscriptionLoading} onClick={handleSubmit} > Submit diff --git a/src/react-query/mutations/authUser/loginMutation.js b/src/react-query/mutations/authUser/loginMutation.js index 072133aff..e6d8c2136 100644 --- a/src/react-query/mutations/authUser/loginMutation.js +++ b/src/react-query/mutations/authUser/loginMutation.js @@ -9,19 +9,23 @@ export const useLoginMutation = ( displayAlert, ) => useMutation( async (loginData) => { - const token = await oauthService.authenticateWithPasswordFlow(loginData); - oauthService.setAccessToken(token.data); - const user = await httpService.makeRequest( - 'get', - `${window.env.API_URL}coreuser/me/`, - ); - oauthService.setOauthUser(user, { loginData }); - const coreuser = await httpService.makeRequest( - 'get', - `${window.env.API_URL}coreuser/`, - ); - oauthService.setCurrentCoreUser(coreuser, user); - return user; + try { + const token = await oauthService.authenticateWithPasswordFlow(loginData); + oauthService.setAccessToken(token.data); + const user = await httpService.makeRequest( + 'get', + `${window.env.API_URL}coreuser/me/`, + ); + oauthService.setOauthUser(user, { loginData }); + const coreuser = await httpService.makeRequest( + 'get', + `${window.env.API_URL}coreuser/`, + ); + oauthService.setCurrentCoreUser(coreuser, user); + return user; + } catch (e) { + displayAlert('error', 'Make sure you have verified your email address to access the platform.\nIf yes, either your account is not approved or you provided invalid username/password'); + } }, { onSuccess: async (response) => { @@ -36,7 +40,7 @@ export const useLoginMutation = ( }, { onError: () => { - displayAlert('error', 'Either your account is not approved or you provided invalid username/password'); + displayAlert('error', 'Make sure you have verified your email address to access the platform.\nIf yes, either your account is not approved or you provided invalid username/password'); }, }, ); diff --git a/src/react-query/mutations/authUser/registerMutation.js b/src/react-query/mutations/authUser/registerMutation.js index d8715ab39..71986e6b0 100644 --- a/src/react-query/mutations/authUser/registerMutation.js +++ b/src/react-query/mutations/authUser/registerMutation.js @@ -1,4 +1,5 @@ import { useMutation } from 'react-query'; +import moment from 'moment-timezone'; import { httpService } from '@modules/http/http.service'; export const useRegisterMutation = ( @@ -15,8 +16,13 @@ export const useRegisterMutation = ( return response; }, { - onSuccess: async () => { - displayAlert('success', 'Please verify the email address to access the platform.'); + onSuccess: async (response, variables, context) => { + const timeDiff = moment().diff(moment(response.data.organization.create_date), 'minutes'); + if (timeDiff < 5) { + displayAlert('success', `Your are the first person in the Organization ${variables.organization_name}.\nPlease verify the email address to access the platform.`); + } else { + displayAlert('success', 'Please verify the email address to access the platform.'); + } history.push(redirectTo); }, onError: () => {