diff --git a/cypress.json b/cypress.json deleted file mode 100644 index c2eeebdf884..00000000000 --- a/cypress.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "projectId": "wu5g1v" -} diff --git a/package.json b/package.json index de805453469..37bcb78ee9b 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,6 @@ "@fullhuman/postcss-purgecss": "^2.1.2", "@types/mocha": "^5.2.7", "css-loader": "^3.2.0", - "cypress": "^4.4.0", "eslint": "^6.4.0", "eslint-config-airbnb": "^18.0.1", "eslint-config-react-app": "^5.0.2", diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index 215eccdfc72..9348c791748 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -55,7 +55,8 @@ export const FACILITY_TYPES: Array = [ { id: 940, text: "Women and Child Health Centres" }, { id: 950, text: "General hospitals" }, { id: 960, text: "District Hospitals" }, - {id: 1000, text: "Corona Care Centre"} + {id: 1000, text: "Corona Care Centre"}, + {id: 1100, text: "First Line Treatment Centre"} ]; export const BED_TYPES: Array = [ diff --git a/src/Components/Auth/ForgetPassword.tsx b/src/Components/Auth/ForgetPassword.tsx new file mode 100644 index 00000000000..b8937113b41 --- /dev/null +++ b/src/Components/Auth/ForgetPassword.tsx @@ -0,0 +1,128 @@ +import { Button, Card, CardActions, CardContent, CardHeader, Grid } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import { A } from 'hookrouter'; +import React, { useEffect, useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { postForgotPassword } from '../../Redux/actions'; +import { TextInputField } from '../Common/HelperInputFields'; +import * as Notification from "../../Utils/Notifications.js"; +import { Loading } from "../Common/Loading"; + +const useStyles = makeStyles(theme => ({ + formTop: { + marginTop: '100px', + }, +})); + +export const ForgotPassword = () => { + const classes = useStyles(); + const dispatch: any = useDispatch(); + const initForm: any = { + username: '', + }; + const initErr: any = {}; + const [form, setForm] = useState(initForm); + const [errors, setErrors] = useState(initErr); + const [disableBtn, setDisableBtn] = useState(false); + const [showLoader, setShowLoader] = useState(false); + + const handleChange = (e: any) => { + const { value, name } = e.target; + const fieldValue = Object.assign({}, form); + const errorField = Object.assign({}, errors); + if (errorField[name]) { + errorField[name] = null; + setErrors(errorField); + } + fieldValue[name] = value; + setForm(fieldValue); + }; + + const validateData = () => { + let hasError = false; + const err = Object.assign({}, errors); + Object.keys(form).forEach((key) => { + if (typeof (form[key]) === 'string') { + if (!form[key].match(/\w/)) { + hasError = true; + err[key] = 'This field is required'; + } + } + if (!form[key]) { + hasError = true; + err[key] = 'This field is required'; + } + }); + if (hasError) { + setErrors(err); + return false; + } + return form; + }; + + const handleSubmit = (e: any) => { + e.preventDefault(); + const valid = validateData(); + if (valid) { + setShowLoader(true) + setDisableBtn(true); + dispatch(postForgotPassword(valid)).then((resp: any) => { + setShowLoader(false) + const res = resp && resp.data; + if (res && res.status === 'OK') { + Notification.Success({ + msg: "Password Reset Email Sent" + }); + } else if (res && res.data) { + setErrors(res.data); + } else { + Notification.Error({ + msg: "Something went wrong try again later " + }); + } + setDisableBtn(false); + }); + } + }; + if (showLoader) { + return ; + } + return ( +
+
+
{ + handleSubmit(e); + }}> +
+ Forgot Password +
+ + Enter your username and we will send you a link to reset your password. + + + + + + + + Already a member? Login + +
+
+
+ ); +}; diff --git a/src/Components/Auth/Login.tsx b/src/Components/Auth/Login.tsx index fcbc4c34a92..9c8db64c0d4 100644 --- a/src/Components/Auth/Login.tsx +++ b/src/Components/Auth/Login.tsx @@ -150,7 +150,6 @@ export const Login = () => { /> - {/*Forgot password ?*/} {isCaptchaEnabled && ( @@ -161,20 +160,12 @@ export const Login = () => { {errors.captcha} )} - - - - - - - +
+ Forgot password? + +
diff --git a/src/Components/Auth/ResetPassword.tsx b/src/Components/Auth/ResetPassword.tsx new file mode 100644 index 00000000000..56a12416c4c --- /dev/null +++ b/src/Components/Auth/ResetPassword.tsx @@ -0,0 +1,172 @@ +import React, { useState } from 'react'; +import { Typography, Grid, Card, CardHeader, CardContent, CardActions, Button } from '@material-ui/core'; +import { TextInputField, ErrorHelperText } from '../Common/HelperInputFields'; +import { useDispatch } from 'react-redux'; +import * as Notification from "../../Utils/Notifications.js"; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import ExpansionPanel from '@material-ui/core/ExpansionPanel'; +import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails'; +import {postResetPassword} from "../../Redux/actions"; +import {navigate} from "hookrouter"; + +const useStyles = makeStyles(theme => ({ + formTop:{ + marginTop: '100px', + } +})); + +const panelStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%' + }, + heading: { + fontSize: theme.typography.pxToRem(15), + fontWeight: theme.typography.fontWeightRegular + } + }) +); + + +export const ResetPassword = (props: any) => { + const classes = useStyles(); + const panel = panelStyles(); + + const dispatch: any = useDispatch(); + const initForm: any = { + password: '', + confirm: '' + }; + + const initErr: any = {}; + const [ form, setForm ] = useState(initForm); + const [ errors, setErrors ] = useState(initErr); + const [passReg, setPassReg] = useState(0); + + const handleChange = (e: any) => { + const { value, name } = e.target; + const fieldValue = Object.assign({}, form); + const errorField = Object.assign({}, errors); + if (errorField[name]) { + errorField[name] = null; + setErrors(errorField); + } + fieldValue[name] = value; + setPassReg(0) + setForm(fieldValue); + }; + + const validateData = () => { + let hasError = false; + const err = Object.assign({}, errors); + if (form.password !== form.confirm) { + hasError = true; + setPassReg(1) + err.confirm = "Password and confirm password must be same." + } + + const regex = /^(?=.*[a-z]+)(?=.*[A-Z]+)(?=.*[0-9]+)(?=.*[!@#$%^&*]).{8,}$/; + if (!regex.test(form.password)) { + hasError = true; + err.password = 'Password Doesnt meet the requirements'; + } + + Object.keys(form).forEach((key) => { + if (!form[key]) { + hasError = true; + err[key] = 'This field is required'; + } + }); + if (hasError) { + setErrors(err); + return false; + } + return form; + }; + + + const handleSubmit = (e: any) => { + e.preventDefault(); + const valid = validateData(); + if (valid) { + valid.token = props.token; + dispatch(postResetPassword(valid)).then((resp: any) => { + const res = resp && resp.data; + if (res && res.status === 'OK') { + localStorage.removeItem('care_access_token'); + Notification.Success({ + msg: "Password Reset successfully" + }); + navigate('/login'); + } else if (res && res.data) { + setErrors(res.data); + } else { + Notification.Error({ + msg: "Password Reset Failed" + }); + } + }); + } + } + return ( +
+ + + +
{handleSubmit(e)}}> + + + + { + passReg === 0 && +
+ + + +
  • Minimum password length 8
  • +
  • Require at least one digit
  • +
  • Require at least one upper case
  • +
  • Require at least one lower case letter
  • +
  • Require at least one symbol
  • +
    +
    +
    +
    + } + + +
    + + + + + +
    +
    +
    +
    + ); +} diff --git a/src/Components/Auth/index.tsx b/src/Components/Auth/index.tsx index 5f20c5fe18b..59dffff4c30 100644 --- a/src/Components/Auth/index.tsx +++ b/src/Components/Auth/index.tsx @@ -1,2 +1,4 @@ export {Login} from './Login'; export {Register} from './Register'; +export {ResetPassword} from './ResetPassword'; +export {ForgotPassword} from './ForgetPassword'; diff --git a/src/Components/Common/Header.tsx b/src/Components/Common/Header.tsx index 4ffd2653b20..be93067f5f0 100644 --- a/src/Components/Common/Header.tsx +++ b/src/Components/Common/Header.tsx @@ -31,7 +31,7 @@ import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown"; import LocalHospitalIcon from "@material-ui/icons/LocalHospital"; import CancelIcon from "@material-ui/icons/Cancel"; const img = - "https://care-staging-coronasafe.s3.amazonaws.com/static/images/logos/black-logo.svg"; + "https://cdn.coronasafe.network/black-logo.svg"; const drawerWidth = 240; const useStyles = makeStyles({ flexGrow: { diff --git a/src/Components/Common/TopBar.tsx b/src/Components/Common/TopBar.tsx index bd252cf78ba..e2a6ef0da51 100644 --- a/src/Components/Common/TopBar.tsx +++ b/src/Components/Common/TopBar.tsx @@ -1,7 +1,7 @@ import React from "react"; const img = - "https://care-staging-coronasafe.s3.amazonaws.com/static/images/logos/black-logo.svg"; + "https://cdn.coronasafe.network/black-logo.svg"; const TopBar = () => { return (
    diff --git a/src/Redux/actions.tsx b/src/Redux/actions.tsx index 0919769df79..cb9b0fc6872 100644 --- a/src/Redux/actions.tsx +++ b/src/Redux/actions.tsx @@ -14,6 +14,13 @@ export const addUser = (params: object) => { return fireRequest("addUser", [], params) }; +export const postResetPassword = (form: object) => { + return fireRequest('resetPassword', [], form); +}; + +export const postForgotPassword = (form: object) => { + return fireRequest('forgotPassword', [], form); +}; // Ambulance export const postAmbulance = (params: object) => { diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 86265ad2eb0..a9f19e87fd7 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -17,6 +17,14 @@ export default { method: 'POST' }, + resetPassword: { + path: '/api/v1/password_reset/confirm/', + method: 'POST', + }, + forgotPassword: { + path: '/api/v1/password_reset/', + method: 'POST', + }, // User Endpoints currentUser: { path: '/api/v1/users/getcurrentuser', diff --git a/src/Router/AppRouter.tsx b/src/Router/AppRouter.tsx index 01bab966ea8..d50a9f4501b 100644 --- a/src/Router/AppRouter.tsx +++ b/src/Router/AppRouter.tsx @@ -32,9 +32,9 @@ import { UserAdd } from "../Components/Users/UserAdd"; import AmbulanceOnboarding from "../Components/Ambulance/AmbulanceOnboarding"; import InventoryList from "../Components/Inventory/InventoryList"; const img = - "https://care-staging-coronasafe.s3.amazonaws.com/static/images/logos/light-logo.svg"; + "https://cdn.coronasafe.network/light-logo.svg"; const logoBlack = - "https://care-staging-coronasafe.s3.amazonaws.com/static/images/logos/black-logo.svg"; + "https://cdn.coronasafe.network/black-logo.svg"; const routes = { "/": () => , diff --git a/src/Router/SessionRouter.tsx b/src/Router/SessionRouter.tsx index 271fee4f84a..9949b2d8f0e 100644 --- a/src/Router/SessionRouter.tsx +++ b/src/Router/SessionRouter.tsx @@ -1,16 +1,16 @@ import React from "react"; -import { Login, Register } from "../Components/Auth"; +import {ForgotPassword, Login, Register, ResetPassword} from "../Components/Auth"; import { useRoutes } from "hookrouter"; import TopBar from "../Components/Common/TopBar"; import { PublicDashboard } from "../Components/Dashboard/PublicDashboard"; -import AmbulanceOnboarding from "../Components/Ambulance/AmbulanceOnboarding"; - const routes = { "/": () => , "/login": () => , "/dashboard": () => , - "/register": () => + "/register": () => , + "/forgot-password": () => , + "/password_reset/:token": ({ token }: any) => , }; const SessionRouter = () => { @@ -25,7 +25,7 @@ const SessionRouter = () => {
    Care Logo
    diff --git a/src/index.html b/src/index.html index 19cfc9ea3e3..206869d1aef 100644 --- a/src/index.html +++ b/src/index.html @@ -11,57 +11,31 @@ - + content="https://cdn.coronasafe.network/ksdma_logo.png" /> + + content="https://cdn.coronasafe.network/ksdma_logo.png" /> - - - Corona Care - - + + - - - - + + -
    - diff --git a/src/index.tsx b/src/index.tsx index fbe57c75592..5fbe36ad410 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -32,8 +32,4 @@ ReactDOM.render( , document.getElementById("root") ); - -// If you want your app to work offline and load faster, you can change -// unregister() to register() below. Note this comes with some pitfalls. -// Learn more about service workers: https://bit.ly/CRA-PWA -serviceWorker.register(); +serviceWorker.unregister(); diff --git a/src/serviceWorker.ts b/src/serviceWorker.ts index e67945e92c2..742a5c8fc2a 100644 --- a/src/serviceWorker.ts +++ b/src/serviceWorker.ts @@ -11,12 +11,12 @@ // opt-in, read https://bit.ly/CRA-PWA const isLocalhost = Boolean( - window.location.hostname === 'localhost' || + window.location.hostname === 'localhost' || // [::1] is the IPv6 localhost address. window.location.hostname === '[::1]' || // 127.0.0.0/8 are considered localhost for IPv4. window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ ) ); @@ -29,8 +29,8 @@ export function register(config?: Config) { if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { // The URL constructor is available in all browsers that support SW. const publicUrl = new URL( - process.env.PUBLIC_URL || '', - window.location.href + process.env.PUBLIC_URL || '', + window.location.href ); if (publicUrl.origin !== window.location.origin) { // Our service worker won't work if PUBLIC_URL is on a different origin @@ -50,7 +50,7 @@ export function register(config?: Config) { // service worker/PWA documentation. navigator.serviceWorker.ready.then(() => { console.log( - 'Service worker' + 'Service worker' ); }); } else { @@ -63,46 +63,46 @@ export function register(config?: Config) { function registerValidSW(swUrl: string, config?: Config) { navigator.serviceWorker - .register(swUrl) - .then(registration => { - registration.onupdatefound = () => { - const installingWorker = registration.installing; - if (installingWorker == null) { - return; - } - installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { - if (navigator.serviceWorker.controller) { - // At this point, the updated precached content has been fetched, - // but the previous service worker will still serve the older - // content until all client tabs are closed. - console.log( - 'New content is available and will be used when all ' + - 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' - ); + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (installingWorker == null) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' + ); - // Execute callback - if (config && config.onUpdate) { - config.onUpdate(registration); - } - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log('Content is cached for offline use.'); + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); - // Execute callback - if (config && config.onSuccess) { - config.onSuccess(registration); + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } } } - } + }; }; - }; - }) - .catch(error => { - console.error('Error during service worker registration:', error); - }); + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); } function checkValidServiceWorker(swUrl: string, config?: Config) { @@ -110,39 +110,39 @@ function checkValidServiceWorker(swUrl: string, config?: Config) { fetch(swUrl, { headers: { 'Service-Worker': 'script' } }) - .then(response => { - // Ensure service worker exists, and that we really are getting a JS file. - const contentType = response.headers.get('content-type'); - if ( - response.status === 404 || - (contentType != null && contentType.indexOf('javascript') === -1) - ) { - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then(registration => { - registration.unregister().then(() => { - window.location.reload(); + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get('content-type'); + if ( + response.status === 404 || + (contentType != null && contentType.indexOf('javascript') === -1) + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); }); - }); - } else { - // Service worker found. Proceed as normal. - registerValidSW(swUrl, config); - } - }) - .catch(() => { - console.log( - 'No internet connection found. App is running in offline mode.' - ); - }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + }); } export function unregister() { if ('serviceWorker' in navigator) { navigator.serviceWorker.ready - .then(registration => { - registration.unregister(); - }) - .catch(error => { - console.error(error.message); - }); + .then(registration => { + registration.unregister(); + }) + .catch(error => { + console.error(error.message); + }); } }