commit
48cc050c47
55 changed files with 10055 additions and 26 deletions
@ -1,30 +1,21 @@ |
||||
# ---> Node |
||||
# Logs |
||||
logs |
||||
*.log |
||||
npm-debug.log* |
||||
|
||||
# Runtime data |
||||
pids |
||||
*.pid |
||||
*.seed |
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover |
||||
lib-cov |
||||
# See https://help.github.com/ignore-files/ for more about ignoring files. |
||||
|
||||
# Coverage directory used by tools like istanbul |
||||
coverage |
||||
# dependencies |
||||
/node_modules |
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) |
||||
.grunt |
||||
# testing |
||||
/coverage |
||||
|
||||
# node-waf configuration |
||||
.lock-wscript |
||||
# production |
||||
/build |
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html) |
||||
build/Release |
||||
|
||||
# Dependency directory |
||||
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git |
||||
node_modules |
||||
# misc |
||||
.DS_Store |
||||
.env.local |
||||
.env.development.local |
||||
.env.test.local |
||||
.env.production.local |
||||
|
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
|
@ -1,2 +1,38 @@ |
||||
# caremyway-client |
||||
# CareMyWay Client |
||||
|
||||
Generated using [create-react-app](https://github.com/facebookincubator/create-react-app). |
||||
|
||||
## Quickstart (Development) |
||||
|
||||
* Ensure the server is up and running locally. |
||||
* [CareMyWay](https://gogs.tannercollin.com/tanner/caremyway) |
||||
* Install dependencies with `yarn install` |
||||
* Run the client with `yarn start` |
||||
|
||||
Now you can visit `localhost:3000` from your browser. |
||||
|
||||
## Environment Variables |
||||
|
||||
The environment variables are embedded during the build time. For more information, please refer to the [docs](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-custom-environment-variables). |
||||
|
||||
| Environment Variable | Default Value | Description | |
||||
| ------------------------- |:-------------------------:| -------------------------------:| |
||||
| `REACT_APP_API_ENDPOINT` | `"http://localhost:8000"` | Server API endpoint | |
||||
| `REACT_APP_REDUX_LOGGING` | ` ` | Set any value for Redux logging | |
||||
|
||||
|
||||
## Production |
||||
|
||||
To build the production instance of this application, run the following: |
||||
|
||||
```bash |
||||
export REACT_APP_API_ENDPOINT="http://dev.tannercollin.com:8000/" && yarn build |
||||
|
||||
# then, to serve the production site locally |
||||
yarn global add serve |
||||
serve -s build --port 3000 |
||||
``` |
||||
|
||||
## Testing |
||||
|
||||
To test the react app, call `yarn test`. |
||||
|
@ -0,0 +1,25 @@ |
||||
{ |
||||
"name": "caremyway", |
||||
"version": "0.1.0", |
||||
"private": true, |
||||
"dependencies": { |
||||
"axios": "^0.16.2", |
||||
"localStorage": "^1.0.3", |
||||
"react": "^15.6.1", |
||||
"react-dom": "^15.6.1", |
||||
"react-redux": "^5.0.6", |
||||
"react-router": "^4.2.0", |
||||
"react-router-dom": "^4.2.2", |
||||
"react-scripts": "1.0.12", |
||||
"redux": "^3.7.2", |
||||
"redux-logger": "^3.0.6", |
||||
"redux-saga": "^0.15.6", |
||||
"semantic-ui-react": "^0.72.0" |
||||
}, |
||||
"scripts": { |
||||
"start": "react-scripts start", |
||||
"build": "react-scripts build", |
||||
"test": "react-scripts test --env=jsdom", |
||||
"eject": "react-scripts eject" |
||||
} |
||||
} |
After Width: | Height: | Size: 3.8 KiB |
@ -0,0 +1,40 @@ |
||||
<!doctype html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
||||
<!-- |
||||
manifest.json provides metadata used when your web app is added to the |
||||
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/ |
||||
--> |
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json"> |
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> |
||||
<!-- |
||||
Notice the use of %PUBLIC_URL% in the tags above. |
||||
It will be replaced with the URL of the `public` folder during the build. |
||||
Only files inside the `public` folder can be referenced from the HTML. |
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will |
||||
work correctly both with client-side routing and a non-root public URL. |
||||
Learn how to configure a non-root public URL by running `npm run build`. |
||||
--> |
||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.13/semantic.min.css"></link> |
||||
<title>Caremyway</title> |
||||
</head> |
||||
<body> |
||||
<noscript> |
||||
You need to enable JavaScript to run this app. |
||||
</noscript> |
||||
<div id="root"></div> |
||||
<!-- |
||||
This HTML file is a template. |
||||
If you open it directly in the browser, you will see an empty page. |
||||
|
||||
You can add webfonts, meta tags, or analytics to this file. |
||||
The build step will place the bundled scripts into the <body> tag. |
||||
|
||||
To begin the development, run `npm start` or `yarn start`. |
||||
To create a production bundle, use `npm run build` or `yarn build`. |
||||
--> |
||||
</body> |
||||
</html> |
@ -0,0 +1,13 @@ |
||||
{ |
||||
"short_name": "Caremyway", |
||||
"name": "Caremyway", |
||||
"icons": [ |
||||
{ |
||||
"src": "favicon.ico", |
||||
"sizes": "192x192", |
||||
"type": "image/png" |
||||
} |
||||
], |
||||
"start_url": "./index.html", |
||||
"display": "standalone" |
||||
} |
@ -0,0 +1,139 @@ |
||||
import { |
||||
IS_SENDING_AUTH_REQUEST, |
||||
SET_AUTH_REQUEST_ERROR, |
||||
CLEAR_AUTH_REQUEST_ERROR, |
||||
SET_AUTH_REQUEST_SUCCESS, |
||||
CLEAR_AUTH_REQUEST_SUCCESS, |
||||
SET_SELF_USER_TOKEN, |
||||
SET_FORM_EMAIL, |
||||
SET_FORM_PASSWORD, |
||||
SET_FORM_PASSWORD_CONFIRMATION, |
||||
SET_FORM_EMAIL_VERIFICATION, |
||||
SET_FORM_OLD_PASSWORD |
||||
} from "../../constants/auth.constants"; |
||||
import { parseError } from "../common.actions"; |
||||
|
||||
export function isSendingAuthRequest(sendingRequest) { |
||||
return { |
||||
type: IS_SENDING_AUTH_REQUEST, |
||||
data: sendingRequest |
||||
}; |
||||
} |
||||
|
||||
export function setAuthRequestError(exception) { |
||||
let error = parseError(exception); |
||||
if (error.email) { |
||||
error["Email"] = error.email; |
||||
delete error["email"]; |
||||
} |
||||
if (error.password) { |
||||
error["Password"] = error.password; |
||||
delete error["password"]; |
||||
} |
||||
if (error.password1) { |
||||
error["Password"] = error.password1; |
||||
delete error["password1"]; |
||||
} |
||||
if (error.password2) { |
||||
error["Confirm Password"] = error.password2; |
||||
delete error["password2"]; |
||||
} |
||||
if (error.non_field_errors) { |
||||
error["Non Field Errors"] = error.non_field_errors; |
||||
delete error["non_field_errors"]; |
||||
} |
||||
if (error.detail) { |
||||
error["Detail"] = error.detail; |
||||
delete error["detail"]; |
||||
} |
||||
if (error.old_password) { |
||||
error["Old Password"] = error.old_password; |
||||
delete error["old_password"]; |
||||
} |
||||
if (error.new_password1) { |
||||
error["New Password"] = error.new_password1; |
||||
delete error["new_password1"]; |
||||
} |
||||
if (error.new_password2) { |
||||
error["Confirm New Password"] = error.new_password2; |
||||
delete error["new_password2"]; |
||||
} |
||||
if (error.token) { |
||||
error["Token"] = error.token; |
||||
delete error["token"]; |
||||
} |
||||
if (error.uid) { |
||||
error["UID"] = error.uid; |
||||
delete error["uid"]; |
||||
} |
||||
if (error.key) { |
||||
error["Email Verification Key"] = error.key; |
||||
delete error["key"]; |
||||
} |
||||
|
||||
return { |
||||
type: SET_AUTH_REQUEST_ERROR, |
||||
data: error |
||||
}; |
||||
} |
||||
|
||||
export function clearAuthRequestError() { |
||||
return { |
||||
type: CLEAR_AUTH_REQUEST_ERROR |
||||
}; |
||||
} |
||||
|
||||
export function setAuthRequestSuccess(response) { |
||||
return { |
||||
type: SET_AUTH_REQUEST_SUCCESS, |
||||
data: response.detail || response |
||||
}; |
||||
} |
||||
|
||||
export function clearAuthRequestSuccess() { |
||||
return { |
||||
type: CLEAR_AUTH_REQUEST_SUCCESS |
||||
}; |
||||
} |
||||
|
||||
export function setSelfUserToken(selfUser) { |
||||
return { |
||||
type: SET_SELF_USER_TOKEN, |
||||
data: selfUser |
||||
}; |
||||
} |
||||
|
||||
export function setFormEmail(email) { |
||||
return { |
||||
type: SET_FORM_EMAIL, |
||||
data: email |
||||
}; |
||||
} |
||||
|
||||
export function setFormPassword(password) { |
||||
return { |
||||
type: SET_FORM_PASSWORD, |
||||
data: password |
||||
}; |
||||
} |
||||
|
||||
export function setFormPasswordConfirmation(passwordConfirmation) { |
||||
return { |
||||
type: SET_FORM_PASSWORD_CONFIRMATION, |
||||
data: passwordConfirmation |
||||
}; |
||||
} |
||||
|
||||
export function setFormEmailVerification(emailKey) { |
||||
return { |
||||
type: SET_FORM_EMAIL_VERIFICATION, |
||||
data: emailKey |
||||
}; |
||||
} |
||||
|
||||
export function setFormOldPassword(oldPassword) { |
||||
return { |
||||
type: SET_FORM_OLD_PASSWORD, |
||||
data: oldPassword |
||||
}; |
||||
} |
@ -0,0 +1,57 @@ |
||||
import { |
||||
SEND_REGISTER_REQUEST, |
||||
SEND_EMAIL_VERIFICATION_REQUEST, |
||||
SEND_LOGIN_REQUEST, |
||||
SEND_LOGOUT_REQUEST, |
||||
SEND_CHANGE_PASSWORD_REQUEST, |
||||
SEND_FORGOT_PASSWORD_REQUEST, |
||||
SEND_RESET_PASSWORD_REQUEST |
||||
} from "../../constants/auth.constants"; |
||||
|
||||
export function sendRegisterRequest(postBody) { |
||||
return { |
||||
type: SEND_REGISTER_REQUEST, |
||||
data: postBody |
||||
}; |
||||
} |
||||
|
||||
export function sendEmailVerificationRequest(postBody) { |
||||
return { |
||||
type: SEND_EMAIL_VERIFICATION_REQUEST, |
||||
data: postBody |
||||
}; |
||||
} |
||||
|
||||
export function sendLoginRequest(postBody) { |
||||
return { |
||||
type: SEND_LOGIN_REQUEST, |
||||
data: postBody |
||||
}; |
||||
} |
||||
|
||||
export function sendLogoutRequest() { |
||||
return { |
||||
type: SEND_LOGOUT_REQUEST |
||||
}; |
||||
} |
||||
|
||||
export function sendChangePasswordRequest(postBody) { |
||||
return { |
||||
type: SEND_CHANGE_PASSWORD_REQUEST, |
||||
data: postBody |
||||
}; |
||||
} |
||||
|
||||
export function sendForgotPasswordRequest(postBody) { |
||||
return { |
||||
type: SEND_FORGOT_PASSWORD_REQUEST, |
||||
data: postBody |
||||
}; |
||||
} |
||||
|
||||
export function sendResetPasswordRequest(postBody) { |
||||
return { |
||||
type: SEND_RESET_PASSWORD_REQUEST, |
||||
data: postBody |
||||
}; |
||||
} |
@ -0,0 +1,13 @@ |
||||
/** |
||||
* Given an exception return the list of errors, the singular error, or generic error |
||||
* @param {object|string} exception - axios returned exception |
||||
*/ |
||||
export function parseError(exception) { |
||||
let response = exception.response || {}; |
||||
let data = response.data || {}; |
||||
let err = "" + exception; |
||||
if (response.status) { |
||||
err = `${response.status} ${response.statusText}`; |
||||
} |
||||
return data || err |
||||
} |
@ -0,0 +1,104 @@ |
||||
import { |
||||
IS_SENDING_USER_REQUEST, |
||||
SET_USER_REQUEST_ERROR, |
||||
CLEAR_USER_REQUEST_ERROR, |
||||
SET_USER_REQUEST_SUCCESS, |
||||
CLEAR_USER_REQUEST_SUCCESS, |
||||
SET_SELF_USER, |
||||
SET_COMPLETE_REGISTRATION_STEP, |
||||
SET_COMPLETE_REGISTRATION_CLIENT_OR_PROVIDER, |
||||
SET_FORM_PHONE_NUMBER, |
||||
SET_FORM_BUSINESS_NUMBER, |
||||
SET_FORM_SOCIAL_INSURANCE_NUMBER, |
||||
SET_EDIT_PROFILE_TAB_ACTIVE_INDEX |
||||
} from "../../constants/user.constants"; |
||||
import { parseError } from "../common.actions"; |
||||
|
||||
export function isSendingUserRequest(sendingRequest) { |
||||
return { |
||||
type: IS_SENDING_USER_REQUEST, |
||||
data: sendingRequest |
||||
}; |
||||
} |
||||
|
||||
export function setUserRequestError(exception) { |
||||
let error = parseError(exception); |
||||
|
||||
if (error.phone_number) { |
||||
error["Phone Number"] = error.phone_number; |
||||
delete error["phone_number"]; |
||||
} |
||||
|
||||
return { |
||||
type: SET_USER_REQUEST_ERROR, |
||||
data: error |
||||
}; |
||||
} |
||||
|
||||
export function clearUserRequestError() { |
||||
return { |
||||
type: CLEAR_USER_REQUEST_ERROR |
||||
}; |
||||
} |
||||
|
||||
export function setUserRequestSuccess(response) { |
||||
return { |
||||
type: SET_USER_REQUEST_SUCCESS, |
||||
data: response.detail || response |
||||
}; |
||||
} |
||||
|
||||
export function clearUserRequestSuccess() { |
||||
return { |
||||
type: CLEAR_USER_REQUEST_SUCCESS |
||||
}; |
||||
} |
||||
|
||||
export function setSelfUser(selfUser) { |
||||
return { |
||||
type: SET_SELF_USER, |
||||
data: selfUser |
||||
}; |
||||
} |
||||
|
||||
export function setCompleteRegistrationStep(step) { |
||||
return { |
||||
type: SET_COMPLETE_REGISTRATION_STEP, |
||||
data: step |
||||
}; |
||||
} |
||||
|
||||
export function setCompleteRegistrationClientOrProvider(clientOrProvider) { |
||||
return { |
||||
type: SET_COMPLETE_REGISTRATION_CLIENT_OR_PROVIDER, |
||||
data: clientOrProvider |
||||
}; |
||||
} |
||||
|
||||
export function setFormPhoneNumber(phoneNumber) { |
||||
return { |
||||
type: SET_FORM_PHONE_NUMBER, |
||||
data: phoneNumber |
||||
}; |
||||
} |
||||
|
||||
export function setFormBusinessNumber(businessNumber) { |
||||
return { |
||||
type: SET_FORM_BUSINESS_NUMBER, |
||||
data: businessNumber |
||||
}; |
||||
} |
||||
|
||||
export function setFormSocialInsuranceNumber(socialInsuranceNumber) { |
||||
return { |
||||
type: SET_FORM_SOCIAL_INSURANCE_NUMBER, |
||||
data: socialInsuranceNumber |
||||
}; |
||||
} |
||||
|
||||
export function setEditProfileTabActiveIndex(indexVal) { |
||||
return { |
||||
type: SET_EDIT_PROFILE_TAB_ACTIVE_INDEX, |
||||
data: indexVal |
||||
}; |
||||
} |
@ -0,0 +1,57 @@ |
||||
import { |
||||
GET_SELF_USER_REQUEST, |
||||
CREATE_USER_INFO_REQUEST, |
||||
UPDATE_USER_INFO_REQUEST, |
||||
CREATE_CLIENT_REQUEST, |
||||
UPDATE_CLIENT_REQUEST, |
||||
CREATE_PROVIDER_REQUEST, |
||||
UPDATE_PROVIDER_REQUEST |
||||
} from "../../constants/user.constants"; |
||||
|
||||
export function getSelfUserRequest() { |
||||
return { |
||||
type: GET_SELF_USER_REQUEST |
||||
}; |
||||
} |
||||
|
||||
export function createUserInfoRequest(postBody) { |
||||
return { |
||||
type: CREATE_USER_INFO_REQUEST, |
||||
data: postBody |
||||
}; |
||||
} |
||||
|
||||
export function updateUserInfoRequest(payload) { |
||||
return { |
||||
type: UPDATE_USER_INFO_REQUEST, |
||||
data: payload |
||||
}; |
||||
} |
||||
|
||||
export function createClientRequest(postBody) { |
||||
return { |
||||
type: CREATE_CLIENT_REQUEST, |
||||
data: postBody |
||||
}; |
||||
} |
||||
|
||||
export function updateClientRequest(payload) { |
||||
return { |
||||
type: UPDATE_CLIENT_REQUEST, |
||||
data: payload |
||||
}; |
||||
} |
||||
|
||||
export function createProviderRequest(postBody) { |
||||
return { |
||||
type: CREATE_PROVIDER_REQUEST, |
||||
data: postBody |
||||
}; |
||||
} |
||||
|
||||
export function updateProviderRequest(payload) { |
||||
return { |
||||
type: UPDATE_PROVIDER_REQUEST, |
||||
data: payload |
||||
}; |
||||
} |
@ -0,0 +1,83 @@ |
||||
import { post } from "./baseApi"; |
||||
|
||||
/** |
||||
* Function wrapping POST request for user registration |
||||
* @param {string} email - email of user to register |
||||
* @param {string} password1 - password of user to register |
||||
* @param {string} password2 - server side password confirmation |
||||
*/ |
||||
export function registerUser(email, password1, password2) { |
||||
return post("/rest-auth/registration/", { |
||||
email, |
||||
password1, |
||||
password2 |
||||
}).then(resp => Promise.resolve(resp)); |
||||
} |
||||
|
||||
/** |
||||
* Function wrapping POST request for email validation |
||||
* @param {string} emailKey - key for email validation |
||||
*/ |
||||
export function verifyEmail(emailKey) { |
||||
return post("/rest-auth/registration/verify-email/", { |
||||
key: emailKey |
||||
}).then(resp => Promise.resolve(resp)); |
||||
} |
||||
|
||||
/** |
||||
* Function wrapping POST request for user login |
||||
* @param {string} email - email of user to login |
||||
* @param {string} password - password of user to login |
||||
*/ |
||||
export function loginUser(email, password) { |
||||
return post("/rest-auth/login/", { email, password }).then(resp => |
||||
Promise.resolve(resp) |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Function wrapping POST request for user logout |
||||
*/ |
||||
export function logoutUser() { |
||||
return post("/rest-auth/logout/").then(resp => Promise.resolve(resp)); |
||||
} |
||||
|
||||
/** |
||||
* Function wrapping POST request for change password |
||||
* @param {string} new_password1 - user new password |
||||
* @param {string} new_password2 - user new password confirmation |
||||
* @param {string} old_password - old password of user |
||||
*/ |
||||
export function changePassword(new_password1, new_password2, old_password) { |
||||
return post("/rest-auth/password/change/", { |
||||
new_password1, |
||||
new_password2, |
||||
old_password |
||||
}).then(resp => Promise.resolve(resp)); |
||||
} |
||||
|
||||
/** |
||||
* Function wrapping POST request for a forget password email call |
||||
* @param {string} email - email of user for password reset |
||||
*/ |
||||
export function forgotPassword(email) { |
||||
return post("/rest-auth/password/reset/", { email }).then(resp => |
||||
Promise.resolve(resp) |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Function wrapping POST request for resetting a user's password |
||||
* @param {string} uid - uid supplied by forgot password email |
||||
* @param {string} token - token supplied by forgot password email |
||||
* @param {string} new_password1 - user new password |
||||
* @param {string} new_password2 - user new password confirmation |
||||
*/ |
||||
export function resetPassword(uid, token, new_password1, new_password2) { |
||||
return post("/rest-auth/password/reset/confirm/", { |
||||
uid, |
||||
token, |
||||
new_password1, |
||||
new_password2 |
||||
}).then(resp => Promise.resolve(resp)); |
||||
} |
@ -0,0 +1,54 @@ |
||||
import axios from "axios"; |
||||
|
||||
const API_ENDPOINT = process.env.REACT_APP_API_ENDPOINT; |
||||
|
||||
// If testing, use localStorage polyfill, else use browser localStorage
|
||||
const localStorage = global.process && process.env.NODE_ENV === "test" |
||||
? // eslint-disable-next-line import/no-extraneous-dependencies
|
||||
require("localStorage") |
||||
: global.window.localStorage; |
||||
|
||||
function headers() { |
||||
const token = localStorage.getItem("userToken") || ""; |
||||
let header = { |
||||
Accept: "application/json", |
||||
"Content-Type": "application/json" |
||||
}; |
||||
if (token) { |
||||
header["Authorization"] = `Token ${token}`; |
||||
} |
||||
return header; |
||||
} |
||||
|
||||
const apiInstance = axios.create({ |
||||
baseURL: API_ENDPOINT, |
||||
timeout: 3000 |
||||
}); |
||||
|
||||
export function get(url, params = {}) { |
||||
return apiInstance |
||||
.get(url, { params, headers: headers() }) |
||||
.then(response => response.data) |
||||
.catch(error => Promise.reject(error)); |
||||
} |
||||
|
||||
export function post(url, data) { |
||||
return apiInstance |
||||
.post(url, data, { headers: headers() }) |
||||
.then(response => response.data) |
||||
.catch(error => Promise.reject(error)); |
||||
} |
||||
|
||||
export function put(url, data) { |
||||
return apiInstance |
||||
.put(url, data, { headers: headers() }) |
||||
.then(response => response.data) |
||||
.catch(error => Promise.reject(error)); |
||||
} |
||||
|
||||
export function del(url) { |
||||
return apiInstance |
||||
.delete(url, { headers: headers() }) |
||||
.then(response => response.data) |
||||
.catch(error => Promise.reject(error)); |
||||
} |
@ -0,0 +1,69 @@ |
||||
import { get, post, put } from "./baseApi"; |
||||
|
||||
/** |
||||
* Function wrapping GET request for getting user data |
||||
*/ |
||||
export function getSelfUser() { |
||||
return get("/user/").then(resp => Promise.resolve(resp)); |
||||
} |
||||
|
||||
/** |
||||
* Function wrapping POST request for initializing User Info |
||||
* @param {string} phone_number - user's phone number |
||||
*/ |
||||
export function createUserInfo(phone_number) { |
||||
return post("/userinfo/", { phone_number }).then(resp => |
||||
Promise.resolve(resp) |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Function wrapping PUT request for updating User Info |
||||
* @param {string} username - username for user |
||||
* @param {string} phone_number - user's phone number |
||||
*/ |
||||
export function updateUserInfo(username, phone_number) { |
||||
return put(`/userinfo/${username}/`, { phone_number }).then(resp => |
||||
Promise.resolve(resp) |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Function wrapping POST request for creating a client |
||||
* @param {string} business_number - user's client business number |
||||
*/ |
||||
export function createClient(business_number) { |
||||
return post("/client/", { business_number }).then(resp => |
||||
Promise.resolve(resp) |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Function wrapping PUT request for updating a client |
||||
* @param {string} username - username for user |
||||
* @param {*} business_number - user's client business number |
||||
*/ |
||||
export function updateClient(username, business_number) { |
||||
return put(`/client/${username}/`, { business_number }).then(resp => |
||||
Promise.resolve(resp) |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Function wrapping POST request for creating a provider |
||||
* @param {string} sin - user's provider sin |
||||
*/ |
||||
export function createProvider(sin) { |
||||
return post("/provider/", { sin }).then(resp => Promise.resolve(resp)); |
||||
} |
||||
|
||||
/** |
||||
* Function wrapping PUT request for updating a provider |
||||
* @param {string} username - username for user |
||||
* @param {*} sin - user's provider sin |
||||
*/ |
||||
export function updateProvider(username, sin) { |
||||
return put(`/provider/${username}/`, { sin }).then(resp => |
||||
Promise.resolve(resp) |
||||
); |
||||
} |
@ -0,0 +1,63 @@ |
||||
import React, { Component } from "react"; |
||||
import { Route, Switch } from "react-router-dom"; |
||||
|
||||
import ForgotPassword from "./Auth/ForgotPassword"; |
||||
import Login from "./Auth/Login"; |
||||
import Register from "./Auth/Register"; |
||||
import ResetPassword from "./Auth/ResetPassword"; |
||||
import Settings from "./Auth/Settings"; |
||||
import VerifyEmail from "./Auth/VerifyEmail"; |
||||
import CompleteRegistration from "./User/CompleteRegistration"; |
||||
import EditProfile from "./User/EditProfile"; |
||||
import Profile from "./User/Profile"; |
||||
import PrivateRoute from "./Shared/PrivateRoute"; |
||||
import About from "./Static/About"; |
||||
import Footer from "./Static/Footer"; |
||||
import Home from "./Static/Home"; |
||||
import NoMatch from "./Static/NoMatch"; |
||||
import Navbar from "./Navbar"; |
||||
|
||||
class App extends Component { |
||||
render() { |
||||
return ( |
||||
<div> |
||||
<Navbar /> |
||||
<div |
||||
style={{ |
||||
display: "flex", |
||||
minHeight: "calc(100vh - 1px)", |
||||
flexDirection: "column" |
||||
}} |
||||
> |
||||
<div style={{ flex: "1" }}> |
||||
<Switch> |
||||
<Route exact path="/" component={Home} /> |
||||
<Route path="/about" component={About} /> |
||||
<Route path="/auth/login" component={Login} /> |
||||
<Route path="/auth/register" component={Register} /> |
||||
<Route |
||||
path="/auth/verify-email/:emailKey" |
||||
component={VerifyEmail} |
||||
/> |
||||
<Route path="/auth/verify-email" component={VerifyEmail} /> |
||||
<PrivateRoute path="/auth/settings" component={Settings} /> |
||||
<Route path="/auth/forgot-password" component={ForgotPassword} /> |
||||
<Route |
||||
path="/auth/reset-password/:uid/:token" |
||||
component={ResetPassword} |
||||
/> |
||||
<PrivateRoute path="/user/profile/edit" component={EditProfile} /> |
||||
<PrivateRoute path="/user/profile" component={Profile} /> |
||||
<Route path='/user/complete-registration' component={Profile} /> |
||||
<Route component={NoMatch} /> |
||||
</Switch> |
||||
<Route path='/user/complete-registration' component={CompleteRegistration} /> |
||||
</div> |
||||
<Footer /> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default App; |
@ -0,0 +1,8 @@ |
||||
import React from 'react'; |
||||
import ReactDOM from 'react-dom'; |
||||
import App from './App'; |
||||
|
||||
it('renders without crashing', () => { |
||||
const div = document.createElement('div'); |
||||
ReactDOM.render(<App />, div); |
||||
}); |
@ -0,0 +1,98 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
import { Link, Redirect } from "react-router-dom"; |
||||
import { Button, Container, Form, Header, Message } from "semantic-ui-react"; |
||||
|
||||
import { |
||||
clearAuthRequestError, |
||||
clearAuthRequestSuccess, |
||||
setFormEmail |
||||
} from "../../actions/auth/reducer.actions"; |
||||
import { sendForgotPasswordRequest } from "../../actions/auth/saga.actions"; |
||||
import Error from "../Shared/Error"; |
||||
|
||||
class ForgotPassword extends Component { |
||||
componentWillMount() { |
||||
this.props.dispatch(clearAuthRequestError()); |
||||
this.props.dispatch(clearAuthRequestSuccess()); |
||||
} |
||||
|
||||
changeEmail = event => { |
||||
this.props.dispatch(setFormEmail(event.target.value)); |
||||
}; |
||||
|
||||
onSubmitForgotPassword = event => { |
||||
event.preventDefault(); |
||||
const { dispatch, email } = this.props; |
||||
dispatch(sendForgotPasswordRequest({ email })); |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
isSendingAuthRequest, |
||||
authRequestError, |
||||
authRequestSuccess, |
||||
email, |
||||
userToken |
||||
} = this.props; |
||||
if (userToken) return <Redirect to={"/"} />; |
||||
return ( |
||||
<ForgotPasswordView |
||||
isSendingAuthRequest={isSendingAuthRequest} |
||||
authRequestError={authRequestError} |
||||
authRequestSuccess={authRequestSuccess} |
||||
email={email} |
||||
changeEmail={this.changeEmail} |
||||
onSubmitForgotPassword={this.onSubmitForgotPassword} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.auth }; |
||||
} |
||||
|
||||
const ForgotPasswordView = ({ |
||||
isSendingAuthRequest, |
||||
authRequestError, |
||||
authRequestSuccess, |
||||
email, |
||||
changeEmail, |
||||
onSubmitForgotPassword |
||||
}) => ( |
||||
<Container> |
||||
<Header>Forgot Password</Header> |
||||
<p>Enter your email and we will send you a link to reset your password.</p> |
||||
<Form |
||||
loading={isSendingAuthRequest} |
||||
onSubmit={onSubmitForgotPassword} |
||||
error={!!authRequestError} |
||||
success={!!authRequestSuccess} |
||||
> |
||||
<Form.Field> |
||||
<label>Email</label> |
||||
<input |
||||
placeholder="bob@gmail.com" |
||||
type="email" |
||||
value={email} |
||||
onChange={changeEmail} |
||||
/> |
||||
</Form.Field> |
||||
<Error |
||||
header="Forgot Password Request failed!" |
||||
error={authRequestError} |
||||
/> |
||||
<Message success> |
||||
<Message.Header>Password Reset Sent!</Message.Header> |
||||
<p>If the email exists, a password reset link will be sent to it.</p> |
||||
</Message> |
||||
<Button.Group> |
||||
<Button type="submit" primary>Request Password Reset</Button> |
||||
<Button as={Link} to="/auth/login" secondary>Login</Button> |
||||
</Button.Group> |
||||
</Form> |
||||
</Container> |
||||
); |
||||
|
||||
export default connect(mapStateToProps)(ForgotPassword); |
@ -0,0 +1,115 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
import { Link, Redirect } from "react-router-dom"; |
||||
import { Button, Container, Form, Header, Message } from "semantic-ui-react"; |
||||
|
||||
import { |
||||
clearAuthRequestError, |
||||
clearAuthRequestSuccess, |
||||
setFormEmail, |
||||
setFormPassword |
||||
} from "../../actions/auth/reducer.actions"; |
||||
import { sendLoginRequest } from "../../actions/auth/saga.actions"; |
||||
import Error from "../Shared/Error"; |
||||
|
||||
class Login extends Component { |
||||
componentWillMount() { |
||||
this.props.dispatch(clearAuthRequestError()); |
||||
this.props.dispatch(clearAuthRequestSuccess()); |
||||
} |
||||
|
||||
changeEmail = event => { |
||||
this.props.dispatch(setFormEmail(event.target.value)); |
||||
}; |
||||
|
||||
changePassword = event => { |
||||
this.props.dispatch(setFormPassword(event.target.value)); |
||||
}; |
||||
|
||||
onSubmitLogin = event => { |
||||
event.preventDefault(); |
||||
const { dispatch, email, password } = this.props; |
||||
dispatch(sendLoginRequest({ email, password })); |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
isSendingAuthRequest, |
||||
authRequestError, |
||||
authRequestSuccess, |
||||
email, |
||||
password, |
||||
userToken |
||||
} = this.props; |
||||
if (userToken) return <Redirect to={"/user/profile"} />; |
||||
return ( |
||||
<LoginView |
||||
isSendingAuthRequest={isSendingAuthRequest} |
||||
authRequestError={authRequestError} |
||||
authRequestSuccess={authRequestSuccess} |
||||
email={email} |
||||
password={password} |
||||
changeEmail={this.changeEmail} |
||||
changePassword={this.changePassword} |
||||
onSubmitLogin={this.onSubmitLogin} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.auth }; |
||||
} |
||||
|
||||
const LoginView = ({ |
||||
isSendingAuthRequest, |
||||
authRequestError, |
||||
authRequestSuccess, |
||||
email, |
||||
password, |
||||
changeEmail, |
||||
changePassword, |
||||
onSubmitLogin |
||||
}) => ( |
||||
<Container> |
||||
<Header>Login</Header> |
||||
<Form |
||||
loading={isSendingAuthRequest} |
||||
onSubmit={onSubmitLogin} |
||||
error={!!authRequestError} |
||||
success={!!authRequestSuccess} |
||||
> |
||||
<Form.Field> |
||||
<label>Email</label> |
||||
<input |
||||
placeholder="bob@gmail.com" |
||||
type="email" |
||||
value={email} |
||||
onChange={changeEmail} |
||||
/> |
||||
</Form.Field> |
||||
<Form.Field> |
||||
<label>Password</label> |
||||
<input |
||||
placeholder="••••••••" |
||||
type="password" |
||||
value={password} |
||||
onChange={changePassword} |
||||
/> |
||||
</Form.Field> |
||||
<Error header="Login failed!" error={authRequestError} /> |
||||
<Message success> |
||||
<Message.Header>Login successful!</Message.Header> |
||||
<p>Redirecting you now...</p> |
||||
</Message> |
||||
<Button.Group> |
||||
<Button type="submit" primary>Login</Button> |
||||
<Button as={Link} to="/auth/forgot-password" secondary> |
||||
Forgot Password |
||||
</Button> |
||||
</Button.Group> |
||||
</Form> |
||||
</Container> |
||||
); |
||||
|
||||
export default connect(mapStateToProps)(Login); |
@ -0,0 +1,138 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
import { Redirect } from "react-router-dom"; |
||||
import { Container, Form, Header, Message } from "semantic-ui-react"; |
||||
|
||||
import { |
||||
clearAuthRequestError, |
||||
clearAuthRequestSuccess, |
||||
setFormEmail, |
||||
setFormPassword, |
||||
setFormPasswordConfirmation |
||||
} from "../../actions/auth/reducer.actions"; |
||||
import { sendRegisterRequest } from "../../actions/auth/saga.actions"; |
||||
import Error from "../Shared/Error"; |
||||
|
||||
class Register extends Component { |
||||
componentWillMount() { |
||||
this.props.dispatch(clearAuthRequestError()); |
||||
this.props.dispatch(clearAuthRequestSuccess()); |
||||
} |
||||
|
||||
changeEmail = event => { |
||||
this.props.dispatch(setFormEmail(event.target.value)); |
||||
}; |
||||
|
||||
changePassword = event => { |
||||
this.props.dispatch(setFormPassword(event.target.value)); |
||||
}; |
||||
|
||||
changePasswordConfirmation = event => { |
||||
this.props.dispatch(setFormPasswordConfirmation(event.target.value)); |
||||
}; |
||||
|
||||
onSubmitRegistration = event => { |
||||
event.preventDefault(); |
||||
const { dispatch, email, password, passwordConfirmation } = this.props; |
||||
dispatch( |
||||
sendRegisterRequest({ |
||||
email, |
||||
password1: password, |
||||
password2: passwordConfirmation |
||||
}) |
||||
); |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
isSendingAuthRequest, |
||||
authRequestError, |
||||
authRequestSuccess, |
||||
email, |
||||
password, |
||||
passwordConfirmation, |
||||
userToken |
||||
} = this.props; |
||||
if (userToken) return <Redirect to={"/"} />; |
||||
return ( |
||||
<RegisterView |
||||
isSendingAuthRequest={isSendingAuthRequest} |
||||
authRequestError={authRequestError} |
||||
authRequestSuccess={authRequestSuccess} |
||||
email={email} |
||||
password={password} |
||||
passwordConfirmation={passwordConfirmation} |
||||
changeEmail={this.changeEmail} |
||||
changePassword={this.changePassword} |
||||
changePasswordConfirmation={this.changePasswordConfirmation} |
||||
onSubmitRegistration={this.onSubmitRegistration} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.auth }; |
||||
} |
||||
|
||||
/** |
||||
* Functional view component for Register logic |
||||
*/ |
||||
const RegisterView = ({ |
||||
isSendingAuthRequest, |
||||
authRequestError, |
||||
authRequestSuccess, |
||||
email, |
||||
password, |
||||
passwordConfirmation, |
||||
changeEmail, |
||||
changePassword, |
||||
changePasswordConfirmation, |
||||
onSubmitRegistration |
||||
}) => ( |
||||
<Container> |
||||
<Header>Register</Header> |
||||
<Form |
||||
loading={isSendingAuthRequest} |
||||
onSubmit={onSubmitRegistration} |
||||
error={!!authRequestError} |
||||
success={!!authRequestSuccess} |
||||
> |
||||
<Form.Field> |
||||
<label>Email</label> |
||||
<input |
||||
placeholder="bob@gmail.com" |
||||
type="email" |
||||
value={email} |
||||
onChange={changeEmail} |
||||
/> |
||||
</Form.Field> |
||||
<Form.Field> |
||||
<label>Password</label> |
||||
<input |
||||
placeholder="••••••••" |
||||
type="password" |
||||
value={password} |
||||
onChange={changePassword} |
||||
/> |
||||
</Form.Field> |
||||
<Form.Field> |
||||
<label>Confirm Password</label> |
||||
<input |
||||
placeholder="••••••••" |
||||
type="password" |
||||
value={passwordConfirmation} |
||||
onChange={changePasswordConfirmation} |
||||
/> |
||||
</Form.Field> |
||||
<Error header="Register failed!" error={authRequestError} /> |
||||
<Message success> |
||||
<Message.Header>Confirmation Email Sent!</Message.Header> |
||||
<p>Please check your email to confirm your registration.</p> |
||||
</Message> |
||||
<Form.Button primary>Register</Form.Button> |
||||
</Form> |
||||
</Container> |
||||
); |
||||
|
||||
export default connect(mapStateToProps)(Register); |
@ -0,0 +1,118 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
import { Link, Redirect } from "react-router-dom"; |
||||
import { Container, Form, Header, Message } from "semantic-ui-react"; |
||||
|
||||
import { |
||||
clearAuthRequestError, |
||||
clearAuthRequestSuccess, |
||||
setFormPassword, |
||||
setFormPasswordConfirmation |
||||
} from "../../actions/auth/reducer.actions"; |
||||
import { sendResetPasswordRequest } from "../../actions/auth/saga.actions"; |
||||
import Error from "../Shared/Error"; |
||||
|
||||
class ResetPassword extends Component { |
||||
componentWillMount() { |
||||
this.props.dispatch(clearAuthRequestError()); |
||||
this.props.dispatch(clearAuthRequestSuccess()); |
||||
} |
||||
|
||||
changePassword = event => { |
||||
this.props.dispatch(setFormPassword(event.target.value)); |
||||
}; |
||||
|
||||
changePasswordConfirmation = event => { |
||||
this.props.dispatch(setFormPasswordConfirmation(event.target.value)); |
||||
}; |
||||
|
||||
onSubmitResetPassword = event => { |
||||
event.preventDefault(); |
||||
const { dispatch, password, passwordConfirmation } = this.props; |
||||
const { uid, token } = this.props.match.params; |
||||
dispatch( |
||||
sendResetPasswordRequest({ |
||||
uid, |
||||
token, |
||||
new_password1: password, |
||||
new_password2: passwordConfirmation |
||||
}) |
||||
); |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
isSendingAuthRequest, |
||||
authRequestError, |
||||
authRequestSuccess, |
||||
password, |
||||
passwordConfirmation, |
||||
userToken |
||||
} = this.props; |
||||
if (userToken) return <Redirect to={"/"} />; |
||||
return ( |
||||
<ResetPasswordView |
||||
isSendingAuthRequest={isSendingAuthRequest} |
||||
authRequestError={authRequestError} |
||||
authRequestSuccess={authRequestSuccess} |
||||
password={password} |
||||
passwordConfirmation={passwordConfirmation} |
||||
changePassword={this.changePassword} |
||||
changePasswordConfirmation={this.changePasswordConfirmation} |
||||
onSubmitResetPassword={this.onSubmitResetPassword} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.auth }; |
||||
} |
||||
|
||||
const ResetPasswordView = ({ |
||||
isSendingAuthRequest, |
||||
authRequestError, |
||||
authRequestSuccess, |
||||
password, |
||||
passwordConfirmation, |
||||
changePassword, |
||||
changePasswordConfirmation, |
||||
onSubmitResetPassword |
||||
}) => ( |
||||
<Container> |
||||
<Header>Reset Password</Header> |
||||
<Form |
||||
loading={isSendingAuthRequest} |
||||
onSubmit={onSubmitResetPassword} |
||||
error={!!authRequestError} |
||||
success={!!authRequestSuccess} |
||||
> |
||||
<Form.Field> |
||||
<label>New Password</label> |
||||
<input |
||||
placeholder="••••••••" |
||||
type="password" |
||||
value={password} |
||||
onChange={changePassword} |
||||
/> |
||||
</Form.Field> |
||||
<Form.Field> |
||||
<label>Confirm New Password</label> |
||||
<input |
||||
placeholder="••••••••" |
||||
type="password" |
||||
value={passwordConfirmation} |
||||
onChange={changePasswordConfirmation} |
||||
/> |
||||
</Form.Field> |
||||
<Error header="Reset Password failed!" error={authRequestError} /> |
||||
<Message success> |
||||
<Message.Header>Password Reset!</Message.Header> |
||||
<p>You may now <Link to="/auth/login">log in</Link> with the new credentials.</p> |
||||
</Message> |
||||
<Form.Button primary>Reset Password</Form.Button>{" "} |
||||
</Form> |
||||
</Container> |
||||
); |
||||
|
||||
export default connect(mapStateToProps)(ResetPassword); |
@ -0,0 +1,140 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
import { Container, Form, Header, Message, Segment } from "semantic-ui-react"; |
||||
|
||||
import { |
||||
clearAuthRequestError, |
||||
clearAuthRequestSuccess, |
||||
setFormPassword, |
||||
setFormPasswordConfirmation, |
||||
setFormOldPassword |
||||
} from "../../actions/auth/reducer.actions"; |
||||
import { sendChangePasswordRequest } from "../../actions/auth/saga.actions"; |
||||
import Error from "../Shared/Error"; |
||||
|
||||
class Settings extends Component { |
||||
componentWillMount() { |
||||
this.props.dispatch(clearAuthRequestError()); |
||||
this.props.dispatch(clearAuthRequestSuccess()); |
||||
} |
||||
|
||||
changePassword = event => { |
||||
this.props.dispatch(setFormPassword(event.target.value)); |
||||
}; |
||||
|
||||
changePasswordConfirmation = event => { |
||||
this.props.dispatch(setFormPasswordConfirmation(event.target.value)); |
||||
}; |
||||
|
||||
changeOldPassword = event => { |
||||
this.props.dispatch(setFormOldPassword(event.target.value)); |
||||
}; |
||||
|
||||
onSubmitChangePassword = event => { |
||||
event.preventDefault(); |
||||
const { |
||||
dispatch, |
||||
password, |
||||
passwordConfirmation, |
||||
oldPassword |
||||
} = this.props; |
||||
dispatch( |
||||
sendChangePasswordRequest({ |
||||
new_password1: password, |
||||
new_password2: passwordConfirmation, |
||||
old_password: oldPassword |
||||
}) |
||||
); |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
isSendingAuthRequest, |
||||
authRequestError, |
||||
authRequestSuccess, |
||||
password, |
||||
passwordConfirmation, |
||||
oldPassword |
||||
} = this.props; |
||||
return ( |
||||
<SettingsView |
||||
isSendingAuthRequest={isSendingAuthRequest} |
||||
authRequestError={authRequestError} |
||||
authRequestSuccess={authRequestSuccess} |
||||
password={password} |
||||
passwordConfirmation={passwordConfirmation} |
||||
oldPassword={oldPassword} |
||||
changePassword={this.changePassword} |
||||
changePasswordConfirmation={this.changePasswordConfirmation} |
||||
changeOldPassword={this.changeOldPassword} |
||||
onSubmitChangePassword={this.onSubmitChangePassword} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.auth }; |
||||
} |
||||
|
||||
const SettingsView = ({ |
||||
isSendingAuthRequest, |
||||
authRequestError, |
||||
authRequestSuccess, |
||||
password, |
||||
passwordConfirmation, |
||||
oldPassword, |
||||
changePassword, |
||||
changePasswordConfirmation, |
||||
changeOldPassword, |
||||
onSubmitChangePassword |
||||
}) => ( |
||||
<Container> |
||||
<Header>Settings</Header> |
||||
<Header attached="top"> Change Password </Header> |
||||
<Segment attached> |
||||
<Form |
||||
loading={isSendingAuthRequest} |
||||
onSubmit={onSubmitChangePassword} |
||||
error={!!authRequestError} |
||||
success={!!authRequestSuccess} |
||||
> |
||||
<Form.Field> |
||||
<label>Old Password</label> |
||||
<input |
||||
placeholder="••••••••" |
||||
type="password" |
||||
value={oldPassword} |
||||
onChange={changeOldPassword} |
||||
/> |
||||
</Form.Field> |
||||
<Form.Field> |
||||
<label>New Password</label> |
||||
<input |
||||
placeholder="••••••••" |
||||
type="password" |
||||
value={password} |
||||
onChange={changePassword} |
||||
/> |
||||
</Form.Field> |
||||
<Form.Field> |
||||
<label>Confirm New Password</label> |
||||
<input |
||||
placeholder="••••••••" |
||||
type="password" |
||||
value={passwordConfirmation} |
||||
onChange={changePasswordConfirmation} |
||||
/> |
||||
</Form.Field> |
||||
<Error header="Change Password failed!" error={authRequestError} /> |
||||
<Message success> |
||||
<Message.Header>Password Successfully Changed!</Message.Header> |
||||
<p>New password has been set.</p> |
||||
</Message> |
||||
<Form.Button primary>Change Password</Form.Button> |
||||
</Form> |
||||
</Segment> |
||||
</Container> |
||||
); |
||||
|
||||
export default connect(mapStateToProps)(Settings); |
@ -0,0 +1,98 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
import { Link, Redirect } from "react-router-dom"; |
||||
import { Container, Form, Header, Message } from "semantic-ui-react"; |
||||
|
||||
import { |
||||
clearAuthRequestError, |
||||
clearAuthRequestSuccess, |
||||
setFormEmailVerification |
||||
} from "../../actions/auth/reducer.actions"; |
||||
import { sendEmailVerificationRequest } from "../../actions/auth/saga.actions"; |
||||
import Error from "../Shared/Error"; |
||||
|
||||
class VerifyEmail extends Component { |
||||
componentWillMount() { |
||||
const emailKey = this.props.match.params.emailKey; |
||||
this.props.dispatch(clearAuthRequestError()); |
||||
this.props.dispatch(clearAuthRequestSuccess()); |
||||
this.props.dispatch(setFormEmailVerification(emailKey)); |
||||
if (emailKey) { |
||||
this.props.dispatch(sendEmailVerificationRequest({ emailKey })); |
||||
} |
||||
} |
||||
|
||||
changeEmailKey = event => { |
||||
this.props.dispatch(setFormEmailVerification(event.target.value)); |
||||
}; |
||||
|
||||
onSubmitEmailVerification = event => { |
||||
event.preventDefault(); |
||||
const { dispatch, emailVerificationString } = this.props; |
||||
dispatch( |
||||
sendEmailVerificationRequest({ emailKey: emailVerificationString }) |
||||
); |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
isSendingAuthRequest, |
||||
authRequestError, |
||||
authRequestSuccess, |
||||
emailVerificationString, |
||||
userToken |
||||
} = this.props; |
||||
if (userToken) return <Redirect to={"/"} />; |
||||
return ( |
||||
<VerifyEmailView |
||||
isSendingAuthRequest={isSendingAuthRequest} |
||||
authRequestError={authRequestError} |
||||
authRequestSuccess={authRequestSuccess} |
||||
emailVerificationString={emailVerificationString} |
||||
changeEmailKey={this.changeEmailKey} |
||||
onSubmitEmailVerification={this.onSubmitEmailVerification} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.auth }; |
||||
} |
||||
|
||||
const VerifyEmailView = ({ |
||||
isSendingAuthRequest, |
||||
authRequestError, |
||||
authRequestSuccess, |
||||
emailVerificationString, |
||||
changeEmailKey, |
||||
onSubmitEmailVerification |
||||
}) => ( |
||||
<Container> |
||||
<Header>Verify Email</Header> |
||||
<Form |
||||
loading={isSendingAuthRequest} |
||||
onSubmit={onSubmitEmailVerification} |
||||
error={!!authRequestError} |
||||
success={!!authRequestSuccess} |
||||
> |
||||
<Form.Field> |
||||
<label>Email Verification Key</label> |
||||
<input |
||||
placeholder="" |
||||
type="text" |
||||
value={emailVerificationString} |
||||
onChange={changeEmailKey} |
||||
/> |
||||
</Form.Field> |
||||
<Error header="Email Verification failed!" error={authRequestError} /> |
||||
<Message success> |
||||
<Message.Header>Email Verified!</Message.Header> |
||||
<p>Please proceed to <Link to="/auth/login">log in</Link>.</p> |
||||
</Message> |
||||
<Form.Button primary>Verify Email</Form.Button> |
||||
</Form> |
||||
</Container> |
||||
); |
||||
|
||||
export default connect(mapStateToProps)(VerifyEmail); |
@ -0,0 +1,77 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
import { Link } from "react-router-dom"; |
||||
import { Dropdown, Menu } from "semantic-ui-react"; |
||||
|
||||
import { getSelfUserRequest } from "../actions/user/saga.actions"; |
||||
import { sendLogoutRequest } from "../actions/auth/saga.actions"; |
||||
|
||||
class Navbar extends Component { |
||||
componentWillMount() { |
||||
const { dispatch, userToken, selfUser } = this.props; |
||||
// If the user token exists and the self user object isn't loaded, dispatch |
||||
if (userToken && Object.keys(selfUser).length === 0) { |
||||
dispatch(getSelfUserRequest()); |
||||
} |
||||
} |
||||
|
||||
dispatchLogoutRequest = () => { |
||||
this.props.dispatch(sendLogoutRequest()); |
||||
}; |
||||
|
||||
render() { |
||||
const { userToken } = this.props; |
||||
return ( |
||||
<NavbarView |
||||
isAuthenticated={!!userToken} |
||||
dispatchLogoutRequest={this.dispatchLogoutRequest} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { |
||||
userToken: state.auth.userToken, |
||||
selfUser: state.user.selfUser |
||||
}; |
||||
} |
||||
|
||||
const NavbarView = ({ isAuthenticated, dispatchLogoutRequest }) => ( |
||||
<Menu> |
||||
<Menu.Item as={Link} to="/"> |
||||
Caremyway |
||||
</Menu.Item> |
||||
<Menu.Item as={Link} to="/about"> |
||||
About |
||||
</Menu.Item> |
||||
{!isAuthenticated && |
||||
<Menu.Menu position="right"> |
||||
<Menu.Item as={Link} to="/auth/login"> |
||||
Login |
||||
</Menu.Item> |
||||
<Menu.Item as={Link} to="/auth/register"> |
||||
Register |
||||
</Menu.Item> |
||||
</Menu.Menu>} |
||||
{!!isAuthenticated && |
||||
<Menu.Menu position="right"> |
||||
<Dropdown item text="Account"> |
||||
<Dropdown.Menu> |
||||
<Dropdown.Item as={Link} to="/user/profile"> |
||||
My Profile |
||||
</Dropdown.Item> |
||||
<Dropdown.Item as={Link} to="/auth/settings"> |
||||
Settings |
||||
</Dropdown.Item> |
||||
<Dropdown.Divider /> |
||||
<Dropdown.Item onClick={dispatchLogoutRequest}> |
||||
Logout |
||||
</Dropdown.Item> |
||||
</Dropdown.Menu> |
||||
</Dropdown> |
||||
</Menu.Menu>} |
||||
</Menu> |
||||
); |
||||
|
||||
export default connect(mapStateToProps)(Navbar); |
@ -0,0 +1,41 @@ |
||||
import PropTypes from "prop-types"; |
||||
import React from "react"; |
||||
import { Message } from "semantic-ui-react"; |
||||
|
||||
const propTypes = { |
||||
error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), |
||||
header: PropTypes.string |
||||
}; |
||||
|
||||
const defaultProps = { |
||||
error: "", |
||||
header: "" |
||||
}; |
||||
|
||||
const Error = ({ error, header }) => { |
||||
if (typeof error === "string") { |
||||
const hasError = !!error; |
||||
if (hasError) { |
||||
return <Message error={hasError} header={header} content={error} />; |
||||
} |
||||
} else if (typeof error === "object" && Object.keys(error).length > 0) { |
||||
const hasError = !!Object.keys(error); |
||||
if (hasError) { |
||||
return ( |
||||
<Message |
||||
error={hasError} |
||||
header={header} |
||||
list={Object.keys(error).map(p => ( |
||||
<Message.Item key={p}> {p}: {error[p]}</Message.Item> |
||||
))} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
return null; |
||||
}; |
||||
|
||||
Error.propTypes = propTypes; |
||||
Error.defaultProps = defaultProps; |
||||
|
||||
export default Error; |
@ -0,0 +1,73 @@ |
||||
import PropTypes from "prop-types"; |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
import { Redirect, Route } from "react-router-dom"; |
||||
import { Loader } from "semantic-ui-react"; |
||||
|
||||
import { getSelfUserRequest } from "../../actions/user/saga.actions"; |
||||
|
||||
const propTypes = { |
||||
userToken: PropTypes.string.isRequired, |
||||
selfUser: PropTypes.object.isRequired, |
||||
isSendingUserRequest: PropTypes.bool.isRequired, |
||||
path: PropTypes.string.isRequired, |
||||
component: PropTypes.oneOfType([PropTypes.element, PropTypes.func]) |
||||
.isRequired, |
||||
dispatch: PropTypes.func.isRequired |
||||
}; |
||||
|
||||
class PrivateRoute extends Component { |
||||
componentWillMount() { |
||||
const { dispatch, userToken, selfUser } = this.props; |
||||
// If the user token exists and the self user object isn't loaded, dispatch |
||||
if (userToken && Object.keys(selfUser).length === 0) { |
||||
dispatch(getSelfUserRequest()); |
||||
} |
||||
} |
||||
|
||||
render() { |
||||
const { |
||||
userToken, |
||||
selfUser, |
||||
isSendingUserRequest, |
||||
component, |
||||
...rest |
||||
} = this.props; |
||||
// If the user token exists and |
||||
// * self user object isn't loaded yet or |
||||
// * we are still sending user request |
||||
// show loading spinner |
||||
if ( |
||||
userToken && (Object.keys(selfUser).length === 0 || isSendingUserRequest) |
||||
) { |
||||
return <Loader active />; |
||||
} |
||||
// If the user exists but they aren't a client or provider yet, confirm-registration |
||||
if ( |
||||
userToken && |
||||
Object.keys(selfUser).length && |
||||
(!selfUser.client && !selfUser.provider) |
||||
) { |
||||
return <Redirect to="/user/complete-registration" />; |
||||
} |
||||
return ( |
||||
<Route |
||||
{...rest} |
||||
render={props => { |
||||
if (!!userToken) return React.createElement(component, props); |
||||
return <Redirect to="/auth/login" />; |
||||
}} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
PrivateRoute.propTypes = propTypes; |
||||
|
||||
const mapStateToProps = state => ({ |
||||
userToken: state.auth.userToken, |
||||
selfUser: state.user.selfUser, |
||||
isSendingUserRequest: state.user.isSendingUserRequest |
||||
}); |
||||
|
||||
export default connect(mapStateToProps)(PrivateRoute); |
@ -0,0 +1,11 @@ |
||||
import React from "react"; |
||||
import { Container } from "semantic-ui-react"; |
||||
|
||||
const About = () => ( |
||||
<Container> |
||||
<h2>About</h2> |
||||
<p>About page static content goes here.</p> |
||||
</Container> |
||||
); |
||||
|
||||
export default About; |
@ -0,0 +1,14 @@ |
||||
import React from "react"; |
||||
import { Link } from "react-router-dom"; |
||||
import { List, Segment } from "semantic-ui-react"; |
||||
|
||||
const Footer = () => ( |
||||
<Segment textAlign="center" color="red" style={{ margin: "0" }}> |
||||
<List bulleted horizontal link> |
||||
<List.Item as={Link} to="/">Caremyway</List.Item> |
||||
<List.Item as={Link} to="/about">About</List.Item> |
||||
</List> |
||||
</Segment> |
||||
); |
||||
|
||||
export default Footer; |
@ -0,0 +1,11 @@ |
||||
import React from "react"; |
||||
import { Container } from "semantic-ui-react"; |
||||
|
||||
const Home = () => ( |
||||
<Container> |
||||
<h2>Home</h2> |
||||
<p>Home page static content goes here.</p> |
||||
</Container> |
||||
); |
||||
|
||||
export default Home; |
@ -0,0 +1,13 @@ |
||||
import React from "react"; |
||||
import { Link } from "react-router-dom"; |
||||
import { Container } from "semantic-ui-react"; |
||||
|
||||
const NoMatch = ({ location }) => ( |
||||
<Container> |
||||
<h3>Page not found!</h3> |
||||
<p>No match found for <code>{location.pathname}</code></p> |
||||
<p><Link to="/">Go to the home page →</Link></p> |
||||
</Container> |
||||
); |
||||
|
||||
export default NoMatch; |
@ -0,0 +1,41 @@ |
||||
import React from "react"; |
||||
import { Container, Form, Header, Message } from "semantic-ui-react"; |
||||
|
||||
import Error from "../Shared/Error"; |
||||
|
||||
const ClientFormView = ({ |
||||
isSendingUserRequest, |
||||
userRequestError, |
||||
userRequestSuccess, |
||||
businessNumber, |
||||
changeBusinessNumber, |
||||
onSubmitClient |
||||
}) => ( |
||||
<Container> |
||||
<Header>Complete Client Information</Header> |
||||
<Form |
||||
loading={isSendingUserRequest} |
||||
onSubmit={onSubmitClient} |
||||
error={!!userRequestError} |
||||
success={!!userRequestSuccess} |
||||
> |
||||
<Form.Field> |
||||
<label>Business Number</label> |
||||
<input |
||||
placeholder="12345" |
||||
type="text" |
||||
value={businessNumber} |
||||
onChange={changeBusinessNumber} |
||||
/> |
||||
</Form.Field> |
||||
<Error header="Modify Client failed!" error={userRequestError} /> |
||||
<Message success> |
||||
<Message.Header>Modify Client successful!</Message.Header> |
||||
<p>Set client successfully.</p> |
||||
</Message> |
||||
<Form.Button>Submit Client</Form.Button> |
||||
</Form> |
||||
</Container> |
||||
); |
||||
|
||||
export default ClientFormView; |
@ -0,0 +1,64 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
import { Container, Form, Header } from "semantic-ui-react"; |
||||
|
||||
import { |
||||
setCompleteRegistrationClientOrProvider, |
||||
setCompleteRegistrationStep |
||||
} from "../../actions/user/reducer.actions"; |
||||
import { CLIENT, PROVIDER, COMPLETE_INFORMATION_STEP } from "../../constants/user.constants"; |
||||
|
||||
class ClientOrProviderForm extends Component { |
||||
changeClientOrProvider = (event, { value }) => { |
||||
this.props.dispatch(setCompleteRegistrationClientOrProvider(value)); |
||||
} |
||||
|
||||
onSelectClientOrProvider = event => { |
||||
this.props.dispatch(setCompleteRegistrationStep(COMPLETE_INFORMATION_STEP)); |
||||
} |
||||
|
||||
render() { |
||||
const { completeRegistrationClientOrProvider } = this.props; |
||||
return ( |
||||
<ClientOrProviderFormView |
||||
clientOrProvider={completeRegistrationClientOrProvider} |
||||
changeClientOrProvider={this.changeClientOrProvider} |
||||
onSelectClientOrProvider={this.onSelectClientOrProvider} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.user }; |
||||
} |
||||
|
||||
const ClientOrProviderFormView = ({ |
||||
clientOrProvider, |
||||
changeClientOrProvider, |
||||
onSelectClientOrProvider |
||||
}) => ( |
||||
<Container> |
||||
<Header>Client or Provider</Header> |
||||
<Form onSubmit={onSelectClientOrProvider}> |
||||
<Form.Group inline> |
||||
<label>Client or Provider</label> |
||||
<Form.Radio |
||||
label="Client" |
||||
value={CLIENT} |
||||
checked={clientOrProvider === CLIENT} |
||||
onChange={changeClientOrProvider} |
||||
/> |
||||
<Form.Radio |
||||
label="Provider" |
||||
value={PROVIDER} |
||||
checked={clientOrProvider === PROVIDER} |
||||
onChange={changeClientOrProvider} |
||||
/> |
||||
</Form.Group> |
||||
<Form.Button>Continue</Form.Button> |
||||
</Form> |
||||
</Container> |
||||
); |
||||
|
||||
export default connect(mapStateToProps)(ClientOrProviderForm); |
@ -0,0 +1,118 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
import { Redirect } from "react-router-dom"; |
||||
import { Dimmer, Grid, Loader, Modal, Step } from "semantic-ui-react"; |
||||
|
||||
import { |
||||
setCompleteRegistrationStep |
||||
} from "../../actions/user/reducer.actions"; |
||||
import { |
||||
USER_INFO_STEP, |
||||
CLIENT_OR_PROVIDER_STEP, |
||||
COMPLETE_INFORMATION_STEP, |
||||
CLIENT, |
||||
PROVIDER |
||||
} from "../../constants/user.constants"; |
||||
import ClientOrProviderForm from "./ClientOrProviderForm"; |
||||
import InitializeClientForm from "./InitializeClientForm"; |
||||
import InitializeProviderForm from "./InitializeProviderForm"; |
||||
import InitializeUserInfoForm from "./InitializeUserInfoForm"; |
||||
|
||||
class CompleteRegistration extends Component { |
||||
onChangeStep = (event, data) => { |
||||
event.preventDefault(); |
||||
this.props.dispatch(setCompleteRegistrationStep(data.step)); |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
userToken, |
||||
selfUser, |
||||
completeRegistrationCurrentStep, |
||||
completeRegistrationMaxStep, |
||||
completeRegistrationClientOrProvider |
||||
} = this.props; |
||||
if (!userToken) return <Redirect to="/auth/login" />; |
||||
if (Object.keys(selfUser).length === 0) |
||||
return <Dimmer active><Loader /></Dimmer>; |
||||
if (selfUser.client || selfUser.provider) |
||||
return <Redirect to="/user/profile" />; |
||||
return ( |
||||
<CompleteRegistrationView |
||||
currentStep={completeRegistrationCurrentStep} |
||||
maxStep={completeRegistrationMaxStep} |
||||
completeRegistrationClientOrProvider={ |
||||
completeRegistrationClientOrProvider |
||||
} |
||||
onChangeStep={this.onChangeStep} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { |
||||
...state.user, |
||||
userToken: state.auth.userToken |
||||
}; |
||||
} |
||||
|
||||
const CompleteRegistrationView = ({ |
||||
currentStep, |
||||
maxStep, |
||||
completeRegistrationClientOrProvider, |
||||
onChangeStep |
||||
}) => ( |
||||
<Modal defaultOpen={true} closeOnDimmerClick={false} size="large"> |
||||
<Modal.Header>Complete Registration</Modal.Header> |
||||
<Modal.Content> |
||||
<Grid> |
||||
<Grid.Row> |
||||
<Grid.Column stretched={true}> |
||||
<Step.Group stackable="tablet" ordered> |
||||
<Step |
||||
completed={maxStep > USER_INFO_STEP} |
||||
active={currentStep === USER_INFO_STEP} |
||||
title="Basic User Info" |
||||
description="Fill out your user information" |
||||
onClick={onChangeStep} |
||||
disabled={maxStep < USER_INFO_STEP} |
||||
step={USER_INFO_STEP} |
||||
/> |
||||
<Step |
||||
completed={maxStep > CLIENT_OR_PROVIDER_STEP} |
||||
active={currentStep === CLIENT_OR_PROVIDER_STEP} |
||||
title="Client or Provider" |
||||
description="Choose to be a Client or a Provider" |
||||
onClick={onChangeStep} |
||||
disabled={maxStep < CLIENT_OR_PROVIDER_STEP} |
||||
step={CLIENT_OR_PROVIDER_STEP} |
||||
/> |
||||
<Step |
||||
completed={maxStep > COMPLETE_INFORMATION_STEP} |
||||
active={currentStep === COMPLETE_INFORMATION_STEP} |
||||
title="Complete Information" |
||||
description="Fill out the client or provider information" |
||||
onClick={onChangeStep} |
||||
disabled={maxStep < COMPLETE_INFORMATION_STEP} |
||||
step={COMPLETE_INFORMATION_STEP} |
||||
/> |
||||
</Step.Group> |
||||
</Grid.Column> |
||||
</Grid.Row> |
||||
<Grid.Row> |
||||
{currentStep === USER_INFO_STEP && <InitializeUserInfoForm />} |
||||
{currentStep === CLIENT_OR_PROVIDER_STEP && <ClientOrProviderForm />} |
||||
{currentStep === COMPLETE_INFORMATION_STEP && |
||||
completeRegistrationClientOrProvider === CLIENT && |
||||
<InitializeClientForm />} |
||||
{currentStep === COMPLETE_INFORMATION_STEP && |
||||
completeRegistrationClientOrProvider === PROVIDER && |
||||
<InitializeProviderForm />} |
||||
</Grid.Row> |
||||
</Grid> |
||||
</Modal.Content> |
||||
</Modal> |
||||
); |
||||
|
||||
export default connect(mapStateToProps)(CompleteRegistration); |
@ -0,0 +1,59 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
|
||||
import { |
||||
clearUserRequestError, |
||||
clearUserRequestSuccess, |
||||
setFormBusinessNumber |
||||
} from "../../actions/user/reducer.actions"; |
||||
import { updateClientRequest } from "../../actions/user/saga.actions"; |
||||
import ClientFormView from "./ClientFormView"; |
||||
|
||||
class EditClientForm extends Component { |
||||
changeBusinessNumber = event => { |
||||
this.props.dispatch(setFormBusinessNumber(event.target.value)); |
||||
this.props.dispatch(clearUserRequestError()); |
||||
this.props.dispatch(clearUserRequestSuccess()); |
||||
}; |
||||
|
||||
onSubmitClient = event => { |
||||
event.preventDefault(); |
||||
const { selfUser, businessNumber } = this.props; |
||||
const businessNumberVal = |
||||
businessNumber || (selfUser.client || {}).business_number; |
||||
this.props.dispatch( |
||||
updateClientRequest({ |
||||
username: selfUser.username, |
||||
business_number: businessNumberVal |
||||
}) |
||||
); |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
isSendingUserRequest, |
||||
userRequestError, |
||||
userRequestSuccess, |
||||
businessNumber, |
||||
selfUser |
||||
} = this.props; |
||||
const businessNumberVal = |
||||
businessNumber || (selfUser.client || {}).business_number; |
||||
return ( |
||||
<ClientFormView |
||||
isSendingUserRequest={isSendingUserRequest} |
||||
userRequestError={userRequestError} |
||||
userRequestSuccess={userRequestSuccess} |
||||
businessNumber={businessNumberVal} |
||||
changeBusinessNumber={this.changeBusinessNumber} |
||||
onSubmitClient={this.onSubmitClient} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.user }; |
||||
} |
||||
|
||||
export default connect(mapStateToProps)(EditClientForm); |
@ -0,0 +1,59 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
import { Container, Header, Tab } from "semantic-ui-react"; |
||||
|
||||
import { |
||||
setEditProfileTabActiveIndex, |
||||
clearUserRequestError, |
||||
clearUserRequestSuccess |
||||
} from "../../actions/user/reducer.actions"; |
||||
import EditClientForm from "./EditClientForm"; |
||||
import EditProviderForm from "./EditProviderForm"; |
||||
import EditUserInfoForm from "./EditUserInfoForm"; |
||||
|
||||
class EditProfile extends Component { |
||||
onTabChange = (event, { activeIndex }) => { |
||||
if (this.props.editProfileActiveIndex !== activeIndex) { |
||||
this.props.dispatch(clearUserRequestError()); |
||||
this.props.dispatch(clearUserRequestSuccess()); |
||||
} |
||||
this.props.dispatch(setEditProfileTabActiveIndex(activeIndex)); |
||||
}; |
||||
|
||||
render() { |
||||
const { selfUser, editProfileActiveIndex } = this.props; |
||||
const panes = [ |
||||
{ |
||||
menuItem: "User Information", |
||||
render: () => <Tab.Pane><EditUserInfoForm /></Tab.Pane> |
||||
} |
||||
]; |
||||
if (selfUser.client) { |
||||
panes.push({ |
||||
menuItem: "Client Information", |
||||
render: () => <Tab.Pane><EditClientForm /></Tab.Pane> |
||||
}); |
||||
} else if (selfUser.provider) { |
||||
panes.push({ |
||||
menuItem: "Provider Information", |
||||
render: () => <Tab.Pane><EditProviderForm /></Tab.Pane> |
||||
}); |
||||
} |
||||
return ( |
||||
<Container> |
||||
<Header>Edit Profile</Header> |
||||
<Tab |
||||
panes={panes} |
||||
onTabChange={this.onTabChange} |
||||
activeIndex={editProfileActiveIndex} |
||||
/> |
||||
</Container> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.user }; |
||||
} |
||||
|
||||
export default connect(mapStateToProps)(EditProfile); |
@ -0,0 +1,56 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
|
||||
import { |
||||
clearUserRequestError, |
||||
clearUserRequestSuccess, |
||||
setFormSocialInsuranceNumber |
||||
} from "../../actions/user/reducer.actions"; |
||||
import { updateProviderRequest } from "../../actions/user/saga.actions"; |
||||
import ProviderFormView from "./ProviderFormView"; |
||||
|
||||
class EditProviderForm extends Component { |
||||
changeSocialInsuranceNumber = event => { |
||||
this.props.dispatch(setFormSocialInsuranceNumber(event.target.value)); |
||||
this.props.dispatch(clearUserRequestError()); |
||||
this.props.dispatch(clearUserRequestSuccess()); |
||||
}; |
||||
|
||||
onSubmitProvider = event => { |
||||
event.preventDefault(); |
||||
const { selfUser, socialInsuranceNumber } = this.props; |
||||
const socialInsuranceNumberVal = |
||||
socialInsuranceNumber || (selfUser.provider || {}).sin; |
||||
this.props.dispatch( |
||||
updateProviderRequest({ username: selfUser.username, sin: socialInsuranceNumberVal }) |
||||
); |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
isSendingUserRequest, |
||||
userRequestError, |
||||
userRequestSuccess, |
||||
socialInsuranceNumber, |
||||
selfUser |
||||
} = this.props; |
||||
const socialInsuranceNumberVal = |
||||
socialInsuranceNumber || (selfUser.provider || {}).sin; |
||||
return ( |
||||
<ProviderFormView |
||||
isSendingUserRequest={isSendingUserRequest} |
||||
userRequestError={userRequestError} |
||||
userRequestSuccess={userRequestSuccess} |
||||
socialInsuranceNumber={socialInsuranceNumberVal} |
||||
changeSocialInsuranceNumber={this.changeSocialInsuranceNumber} |
||||
onSubmitProvider={this.onSubmitProvider} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.user }; |
||||
} |
||||
|
||||
export default connect(mapStateToProps)(EditProviderForm); |
@ -0,0 +1,62 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
|
||||
import { |
||||
clearUserRequestError, |
||||
clearUserRequestSuccess, |
||||
setFormPhoneNumber |
||||
} from "../../actions/user/reducer.actions"; |
||||
import { updateUserInfoRequest } from "../../actions/user/saga.actions"; |
||||
|
||||
import UserInfoFormView from "./UserInfoFormView"; |
||||
|
||||
class EditUserInfoForm extends Component { |
||||
changePhoneNumber = event => { |
||||
this.props.dispatch(setFormPhoneNumber(event.target.value)); |
||||
this.props.dispatch(clearUserRequestError()); |
||||
this.props.dispatch(clearUserRequestSuccess()); |
||||
}; |
||||
|
||||
onSubmitUserInfo = event => { |
||||
event.preventDefault(); |
||||
const { selfUser, phoneNumber } = this.props; |
||||
const phoneNumberVal = |
||||
phoneNumber || (selfUser.userinfo || {}).phone_number; |
||||
|
||||
this.props.dispatch( |
||||
updateUserInfoRequest({ |
||||
...selfUser.userinfo, |
||||
username: selfUser.username, |
||||
phone_number: phoneNumberVal |
||||
}) |
||||
); |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
isSendingUserRequest, |
||||
userRequestError, |
||||
userRequestSuccess, |
||||
selfUser, |
||||
phoneNumber |
||||
} = this.props; |
||||
const phoneNumberVal = |
||||
phoneNumber || (selfUser.userinfo || {}).phone_number; |
||||
return ( |
||||
<UserInfoFormView |
||||
isSendingUserRequest={isSendingUserRequest} |
||||
userRequestError={userRequestError} |
||||
userRequestSuccess={userRequestSuccess} |
||||
phoneNumber={phoneNumberVal} |
||||
changePhoneNumber={this.changePhoneNumber} |
||||
onSubmitUserInfo={this.onSubmitUserInfo} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.user }; |
||||
} |
||||
|
||||
export default connect(mapStateToProps)(EditUserInfoForm); |
@ -0,0 +1,54 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
|
||||
import { |
||||
clearUserRequestError, |
||||
clearUserRequestSuccess, |
||||
setFormBusinessNumber |
||||
} from "../../actions/user/reducer.actions"; |
||||
import { createClientRequest } from "../../actions/user/saga.actions"; |
||||
import ClientFormView from "./ClientFormView"; |
||||
|
||||
class InitializeClientForm extends Component { |
||||
componentWillMount() { |
||||
this.props.dispatch(clearUserRequestError()); |
||||
this.props.dispatch(clearUserRequestSuccess()); |
||||
} |
||||
|
||||
changeBusinessNumber = event => { |
||||
this.props.dispatch(setFormBusinessNumber(event.target.value)); |
||||
} |
||||
|
||||
onSubmitClient = event => { |
||||
event.preventDefault(); |
||||
const { businessNumber } = this.props; |
||||
this.props.dispatch( |
||||
createClientRequest({ business_number: businessNumber }) |
||||
); |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
isSendingUserRequest, |
||||
userRequestError, |
||||
userRequestSuccess, |
||||
businessNumber |
||||
} = this.props; |
||||
return ( |
||||
<ClientFormView |
||||
isSendingUserRequest={isSendingUserRequest} |
||||
userRequestError={userRequestError} |
||||
userRequestSuccess={userRequestSuccess} |
||||
businessNumber={businessNumber} |
||||
changeBusinessNumber={this.changeBusinessNumber} |
||||
onSubmitClient={this.onSubmitClient} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.user }; |
||||
} |
||||
|
||||
export default connect(mapStateToProps)(InitializeClientForm); |
@ -0,0 +1,54 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
|
||||
import { |
||||
clearUserRequestError, |
||||
clearUserRequestSuccess, |
||||
setFormSocialInsuranceNumber |
||||
} from "../../actions/user/reducer.actions"; |
||||
import { createProviderRequest } from "../../actions/user/saga.actions"; |
||||
import ProviderFormView from "./ProviderFormView"; |
||||
|
||||
class InitializeProviderForm extends Component { |
||||
componentWillMount() { |
||||
this.props.dispatch(clearUserRequestError()); |
||||
this.props.dispatch(clearUserRequestSuccess()); |
||||
} |
||||
|
||||
changeSocialInsuranceNumber = event => { |
||||
this.props.dispatch(setFormSocialInsuranceNumber(event.target.value)); |
||||
} |
||||
|
||||
onSubmitProvider = event => { |
||||
event.preventDefault(); |
||||
const { socialInsuranceNumber } = this.props; |
||||
this.props.dispatch( |
||||
createProviderRequest({ sin: socialInsuranceNumber }) |
||||
); |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
isSendingUserRequest, |
||||
userRequestError, |
||||
userRequestSuccess, |
||||
socialInsuranceNumber |
||||
} = this.props; |
||||
return ( |
||||
<ProviderFormView |
||||
isSendingUserRequest={isSendingUserRequest} |
||||
userRequestError={userRequestError} |
||||
userRequestSuccess={userRequestSuccess} |
||||
socialInsuranceNumber={socialInsuranceNumber} |
||||
changeSocialInsuranceNumber={this.changeSocialInsuranceNumber} |
||||
onSubmitProvider={this.onSubmitProvider} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.user }; |
||||
} |
||||
|
||||
export default connect(mapStateToProps)(InitializeProviderForm); |
@ -0,0 +1,76 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
|
||||
import { |
||||
clearUserRequestError, |
||||
clearUserRequestSuccess, |
||||
setFormPhoneNumber |
||||
} from "../../actions/user/reducer.actions"; |
||||
import { |
||||
createUserInfoRequest, |
||||
updateUserInfoRequest |
||||
} from "../../actions/user/saga.actions"; |
||||
|
||||
import UserInfoFormView from "./UserInfoFormView"; |
||||
|
||||
class InitializeUserInfoForm extends Component { |
||||
componentWillMount() { |
||||
this.props.dispatch(clearUserRequestError()); |
||||
this.props.dispatch(clearUserRequestSuccess()); |
||||
} |
||||
|
||||
changePhoneNumber = event => { |
||||
this.props.dispatch(setFormPhoneNumber(event.target.value)); |
||||
}; |
||||
|
||||
onSubmitUserInfo = event => { |
||||
event.preventDefault(); |
||||
const { selfUser, phoneNumber } = this.props; |
||||
const phoneNumberVal = |
||||
phoneNumber || (selfUser.userinfo || {}).phone_number; |
||||
|
||||
if (selfUser.userinfo) { |
||||
this.props.dispatch( |
||||
updateUserInfoRequest({ |
||||
...selfUser.userinfo, |
||||
username: selfUser.username, |
||||
phone_number: phoneNumberVal |
||||
}) |
||||
); |
||||
} else { |
||||
this.props.dispatch( |
||||
createUserInfoRequest({ |
||||
phone_number: phoneNumberVal |
||||
}) |
||||
); |
||||
} |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
isSendingUserRequest, |
||||
userRequestError, |
||||
userRequestSuccess, |
||||
selfUser, |
||||
phoneNumber |
||||
} = this.props; |
||||
const phoneNumberVal = |
||||
phoneNumber || (selfUser.userinfo || {}).phone_number; |
||||
return ( |
||||
<UserInfoFormView |
||||
isSendingUserRequest={isSendingUserRequest} |
||||
userRequestError={userRequestError} |
||||
userRequestSuccess={userRequestSuccess} |
||||
phoneNumber={phoneNumberVal} |
||||
changePhoneNumber={this.changePhoneNumber} |
||||
onSubmitUserInfo={this.onSubmitUserInfo} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.user }; |
||||
} |
||||
|
||||
export default connect(mapStateToProps)(InitializeUserInfoForm); |
@ -0,0 +1,73 @@ |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
import { Link } from "react-router-dom"; |
||||
import { Card, Container, Header, Label, List } from "semantic-ui-react"; |
||||
|
||||
class Profile extends Component { |
||||
render() { |
||||
const { selfUser } = this.props; |
||||
return <ProfileView user={selfUser} />; |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.user }; |
||||
} |
||||
|
||||
const ProfileView = ({ user }) => ( |
||||
<Container> |
||||
<Header>Profile</Header> |
||||
<Card fluid={true}> |
||||
<Card.Content> |
||||
<Card.Header>{user.username || "No username!"}</Card.Header> |
||||
<Card.Meta> |
||||
{user.client && <Label color="blue" size="tiny">Client</Label>} |
||||
{user.provider && <Label color="orange" size="tiny">Provider</Label>} |
||||
<Label color={user.is_active ? "teal" : "red"} size="tiny"> |
||||
{user.is_active ? "Active" : "Deactivated"} |
||||
</Label> |
||||
{" "}{user.email || "No email!"} |
||||
</Card.Meta> |
||||
<Card.Description> |
||||
<span> |
||||
{!user.client && |
||||
!user.provider && |
||||
"User Registration Not Completed"} |
||||
</span> |
||||
{user.first_name} {user.last_name} |
||||
<List> |
||||
{user.userinfo && |
||||
Object.keys(user.userinfo).map(function(key) { |
||||
return ( |
||||
<List.Item key={key}> |
||||
{key}: {user.userinfo[key]} |
||||
</List.Item> |
||||
); |
||||
})} |
||||
{user.client && |
||||
Object.keys(user.client).map(function(key) { |
||||
return ( |
||||
<List.Item key={key}> |
||||
{key}: {user.client[key]} |
||||
</List.Item> |
||||
); |
||||
})} |
||||
{user.provider && |
||||
Object.keys(user.provider).map(function(key) { |
||||
return ( |
||||
<List.Item key={key}> |
||||
{key}: {user.provider[key]} |
||||
</List.Item> |
||||
); |
||||
})} |
||||
</List> |
||||
</Card.Description> |
||||
</Card.Content> |
||||
<Card.Content extra> |
||||
<Link to="/user/profile/edit">Edit Profile</Link> |
||||
</Card.Content> |
||||
</Card> |
||||
</Container> |
||||
); |
||||
|
||||
export default connect(mapStateToProps)(Profile); |
@ -0,0 +1,41 @@ |
||||
import React from "react"; |
||||
import { Container, Form, Header, Message } from "semantic-ui-react"; |
||||
|
||||
import Error from "../Shared/Error"; |
||||
|
||||
const ProviderFormView = ({ |
||||
isSendingUserRequest, |
||||
userRequestError, |
||||
userRequestSuccess, |
||||
socialInsuranceNumber, |
||||
changeSocialInsuranceNumber, |
||||
onSubmitProvider |
||||
}) => ( |
||||
<Container> |
||||
<Header>Complete Provider Information</Header> |
||||
<Form |
||||
loading={isSendingUserRequest} |
||||
onSubmit={onSubmitProvider} |
||||
error={!!userRequestError} |
||||
success={!!userRequestSuccess} |
||||
> |
||||
<Form.Field> |
||||
<label>Social Insurance Number</label> |
||||
<input |
||||
placeholder="98765" |
||||
type="text" |
||||
value={socialInsuranceNumber} |
||||
onChange={changeSocialInsuranceNumber} |
||||
/> |
||||
</Form.Field> |
||||
<Error header="Modify Provider failed!" error={userRequestError} /> |
||||
<Message success> |
||||
<Message.Header>Modify Provider successful!</Message.Header> |
||||
<p>Set provider successfully.</p> |
||||
</Message> |
||||
<Form.Button>Submit Provider</Form.Button> |
||||
</Form> |
||||
</Container> |
||||
); |
||||
|
||||
export default ProviderFormView; |
@ -0,0 +1,41 @@ |
||||
import React from "react"; |
||||
import { Container, Form, Header, Message } from "semantic-ui-react"; |
||||
|
||||
import Error from "../Shared/Error"; |
||||
|
||||
const UserInfoFormView = ({ |
||||
isSendingUserRequest, |
||||
userRequestError, |
||||
userRequestSuccess, |
||||
phoneNumber, |
||||
changePhoneNumber, |
||||
onSubmitUserInfo |
||||
}) => ( |
||||
<Container> |
||||
<Header>User Info</Header> |
||||
<Form |
||||
loading={isSendingUserRequest} |
||||
onSubmit={onSubmitUserInfo} |
||||
error={!!userRequestError} |
||||
success={!!userRequestSuccess} |
||||
> |
||||
<Form.Field> |
||||
<label>Phone Number</label> |
||||
<input |
||||
placeholder="7809999999" |
||||
type="tel" |
||||
value={phoneNumber} |
||||
onChange={changePhoneNumber} |
||||
/> |
||||
</Form.Field> |
||||
<Error header="Modify User Info failed!" error={userRequestError} /> |
||||
<Message success> |
||||
<Message.Header>Modify User Info successful!</Message.Header> |
||||
<p>Set user info successfully.</p> |
||||
</Message> |
||||
<Form.Button>Submit User Info</Form.Button> |
||||
</Form> |
||||
</Container> |
||||
); |
||||
|
||||
export default UserInfoFormView; |
@ -0,0 +1,22 @@ |
||||
// Reducer Auth Action Constants
|
||||
export const IS_SENDING_AUTH_REQUEST = "IS_SENDING_AUTH_REQUEST"; |
||||
export const SET_AUTH_REQUEST_ERROR = "SET_AUTH_REQUEST_ERROR"; |
||||
export const CLEAR_AUTH_REQUEST_ERROR = "CLEAR_AUTH_REQUEST_ERROR"; |
||||
export const SET_AUTH_REQUEST_SUCCESS = "SET_AUTH_REQUEST_SUCCESS"; |
||||
export const CLEAR_AUTH_REQUEST_SUCCESS = "CLEAR_AUTH_REQUEST_SUCCESS"; |
||||
export const SET_SELF_USER_TOKEN = "SET_SELF_USER_TOKEN"; |
||||
export const SET_FORM_EMAIL = "SET_FORM_EMAIL"; |
||||
export const SET_FORM_PASSWORD = "SET_FORM_PASSWORD"; |
||||
export const SET_FORM_PASSWORD_CONFIRMATION = "SET_FORM_PASSWORD_CONFIRMATION"; |
||||
export const SET_FORM_EMAIL_VERIFICATION = "SET_FORM_EMAIL_VERIFICATION"; |
||||
export const SET_FORM_OLD_PASSWORD = "SET_FORM_OLD_PASSWORD"; |
||||
|
||||
// Saga Auth Action Constants
|
||||
export const SEND_REGISTER_REQUEST = "SEND_REGISTER_REQUEST"; |
||||
export const SEND_EMAIL_VERIFICATION_REQUEST = |
||||
"SEND_EMAIL_VERIFICATION_REQUEST"; |
||||
export const SEND_LOGIN_REQUEST = "SEND_LOGIN_REQUEST"; |
||||
export const SEND_LOGOUT_REQUEST = "SEND_LOGOUT_REQUEST"; |
||||
export const SEND_CHANGE_PASSWORD_REQUEST = "SEND_CHANGE_PASSWORD_REQUEST"; |
||||
export const SEND_FORGOT_PASSWORD_REQUEST = "SEND_FORGOT_PASSWORD_REQUEST"; |
||||
export const SEND_RESET_PASSWORD_REQUEST = "SEND_RESET_PASSWORD_REQUEST"; |
@ -0,0 +1,32 @@ |
||||
// Reducer User Action Constants
|
||||
export const IS_SENDING_USER_REQUEST = "IS_SENDING_USER_REQUEST"; |
||||
export const SET_USER_REQUEST_ERROR = "SET_USER_REQUEST_ERROR"; |
||||
export const CLEAR_USER_REQUEST_ERROR = "CLEAR_USER_REQUEST_ERROR"; |
||||
export const SET_USER_REQUEST_SUCCESS = "SET_USER_REQUEST_SUCCESS"; |
||||
export const CLEAR_USER_REQUEST_SUCCESS = "CLEAR_USER_REQUEST_SUCCESS"; |
||||
export const SET_SELF_USER = "SET_SELF_USER"; |
||||
export const SET_COMPLETE_REGISTRATION_STEP = "SET_COMPLETE_REGISTRATION_STEP"; |
||||
export const SET_COMPLETE_REGISTRATION_CLIENT_OR_PROVIDER = |
||||
"SET_COMPLETE_REGISTRATION_CLIENT_OR_PROVIDER"; |
||||
export const SET_FORM_PHONE_NUMBER = "SET_FORM_PHONE_NUMBER"; |
||||
export const SET_FORM_BUSINESS_NUMBER = "SET_FORM_BUSINESS_NUMBER"; |
||||
export const SET_FORM_SOCIAL_INSURANCE_NUMBER = |
||||
"SET_FORM_SOCIAL_INSURANCE_NUMBER"; |
||||
export const SET_EDIT_PROFILE_TAB_ACTIVE_INDEX = |
||||
"SET_EDIT_PROFILE_TAB_ACTIVE_INDEX"; |
||||
|
||||
// Saga User Action Constants
|
||||
export const GET_SELF_USER_REQUEST = "GET_SELF_USER_REQUEST"; |
||||
export const CREATE_USER_INFO_REQUEST = "CREATE_USER_INFO_REQUEST"; |
||||
export const UPDATE_USER_INFO_REQUEST = "UPDATE_USER_INFO_REQUEST"; |
||||
export const CREATE_CLIENT_REQUEST = "CREATE_CLIENT_REQUEST"; |
||||
export const UPDATE_CLIENT_REQUEST = "UPDATE_CLIENT_REQUEST"; |
||||
export const CREATE_PROVIDER_REQUEST = "CREATE_PROVIDER_REQUEST"; |
||||
export const UPDATE_PROVIDER_REQUEST = "UPDATE_PROVIDER_REQUEST"; |
||||
|
||||
// Misc. User Constants (int so we can mark prior as completed)
|
||||
export const USER_INFO_STEP = 1; |
||||
export const CLIENT_OR_PROVIDER_STEP = 2; |
||||
export const COMPLETE_INFORMATION_STEP = 3; |
||||
export const CLIENT = "CLIENT"; |
||||
export const PROVIDER = "PROVIDER"; |
@ -0,0 +1,40 @@ |
||||
import React from "react"; |
||||
import ReactDOM from "react-dom"; |
||||
import { BrowserRouter } from "react-router-dom"; |
||||
import { Provider } from "react-redux"; |
||||
|
||||
import configureStore from "./store"; |
||||
import App from "./components/App"; |
||||
import { unregister } from "./registerServiceWorker"; |
||||
|
||||
const store = configureStore(); |
||||
const supportsHistory = "pushState" in window.history; |
||||
|
||||
const rootElement = document.getElementById("root"); |
||||
|
||||
ReactDOM.render( |
||||
<Provider store={store}> |
||||
<BrowserRouter forceRefresh={!supportsHistory}> |
||||
<App /> |
||||
</BrowserRouter> |
||||
</Provider>, |
||||
rootElement |
||||
); |
||||
|
||||
// hot module reloading
|
||||
if (module.hot) { |
||||
module.hot.accept("./components/App", () => { |
||||
const NextApp = require("./components/App").default; |
||||
ReactDOM.render( |
||||
<Provider store={store}> |
||||
<BrowserRouter forceRefresh={!supportsHistory}> |
||||
<NextApp /> |
||||
</BrowserRouter> |
||||
</Provider>, |
||||
rootElement |
||||
); |
||||
}); |
||||
} |
||||
|
||||
// no service worker functionality for now
|
||||
unregister(); |
@ -0,0 +1,101 @@ |
||||
import { |
||||
IS_SENDING_AUTH_REQUEST, |
||||
SET_AUTH_REQUEST_ERROR, |
||||
CLEAR_AUTH_REQUEST_ERROR, |
||||
SET_AUTH_REQUEST_SUCCESS, |
||||
CLEAR_AUTH_REQUEST_SUCCESS, |
||||
SET_SELF_USER_TOKEN, |
||||
SET_FORM_EMAIL, |
||||
SET_FORM_PASSWORD, |
||||
SET_FORM_PASSWORD_CONFIRMATION, |
||||
SET_FORM_EMAIL_VERIFICATION, |
||||
SET_FORM_OLD_PASSWORD |
||||
} from "../constants/auth.constants"; |
||||
|
||||
/** |
||||
* Set the user's auth token, and return the value |
||||
* @param {string} newToken
|
||||
*/ |
||||
function me(newToken) { |
||||
if (typeof newToken === "string") { |
||||
localStorage.setItem("userToken", newToken); |
||||
} |
||||
const userToken = localStorage.getItem("userToken"); |
||||
return userToken ? userToken : ""; |
||||
} |
||||
|
||||
const initialState = { |
||||
isSendingAuthRequest: false, |
||||
authRequestError: "", |
||||
authRequestSuccess: "", |
||||
userToken: me(null), |
||||
email: "", |
||||
password: "", |
||||
passwordConfirmation: "", |
||||
emailVerificationString: "", |
||||
oldPassword: "" // used for change password functionality
|
||||
}; |
||||
|
||||
function authReducer(state = initialState, action) { |
||||
switch (action.type) { |
||||
case IS_SENDING_AUTH_REQUEST: |
||||
return { |
||||
...state, |
||||
isSendingAuthRequest: action.data |
||||
}; |
||||
case SET_AUTH_REQUEST_ERROR: |
||||
return { |
||||
...state, |
||||
authRequestError: action.data |
||||
}; |
||||
case CLEAR_AUTH_REQUEST_ERROR: |
||||
return { |
||||
...state, |
||||
authRequestError: "" |
||||
}; |
||||
case SET_AUTH_REQUEST_SUCCESS: |
||||
return { |
||||
...state, |
||||
authRequestSuccess: action.data |
||||
}; |
||||
case CLEAR_AUTH_REQUEST_SUCCESS: |
||||
return { |
||||
...state, |
||||
authRequestSuccess: "" |
||||
}; |
||||
case SET_SELF_USER_TOKEN: |
||||
return { |
||||
...state, |
||||
userToken: me(action.data) |
||||
}; |
||||
case SET_FORM_EMAIL: |
||||
return { |
||||
...state, |
||||
email: action.data |
||||
}; |
||||
case SET_FORM_PASSWORD: |
||||
return { |
||||
...state, |
||||
password: action.data |
||||
}; |
||||
case SET_FORM_PASSWORD_CONFIRMATION: |
||||
return { |
||||
...state, |
||||
passwordConfirmation: action.data |
||||
}; |
||||
case SET_FORM_EMAIL_VERIFICATION: |
||||
return { |
||||
...state, |
||||
emailVerificationString: action.data |
||||
}; |
||||
case SET_FORM_OLD_PASSWORD: |
||||
return { |
||||
...state, |
||||
oldPassword: action.data |
||||
} |
||||
default: |
||||
return state; |
||||
} |
||||
} |
||||
|
||||
export default authReducer; |
@ -0,0 +1,10 @@ |
||||
import { combineReducers } from "redux"; |
||||
import authReducer from "./authReducer"; |
||||
import userReducer from "./userReducer"; |
||||
|
||||
const reducer = combineReducers({ |
||||
auth: authReducer, |
||||
user: userReducer |
||||
}); |
||||
|
||||
export default reducer; |
@ -0,0 +1,103 @@ |
||||
import { |
||||
IS_SENDING_USER_REQUEST, |
||||
SET_USER_REQUEST_ERROR, |
||||
CLEAR_USER_REQUEST_ERROR, |
||||
SET_USER_REQUEST_SUCCESS, |
||||
CLEAR_USER_REQUEST_SUCCESS, |
||||
SET_SELF_USER, |
||||
SET_COMPLETE_REGISTRATION_STEP, |
||||
SET_COMPLETE_REGISTRATION_CLIENT_OR_PROVIDER, |
||||
SET_FORM_PHONE_NUMBER, |
||||
SET_FORM_BUSINESS_NUMBER, |
||||
SET_FORM_SOCIAL_INSURANCE_NUMBER, |
||||
SET_EDIT_PROFILE_TAB_ACTIVE_INDEX, |
||||
USER_INFO_STEP, |
||||
CLIENT |
||||
} from "../constants/user.constants"; |
||||
|
||||
const initialState = { |
||||
isSendingUserRequest: false, |
||||
userRequestError: "", |
||||
userRequestSuccess: "", |
||||
selfUser: {}, |
||||
completeRegistrationCurrentStep: USER_INFO_STEP, |
||||
completeRegistrationMaxStep: USER_INFO_STEP, |
||||
completeRegistrationClientOrProvider: CLIENT, |
||||
phoneNumber: "", |
||||
businessNumber: "", |
||||
socialInsuranceNumber: "", |
||||
editProfileActiveIndex: 0 |
||||
}; |
||||
|
||||
function userReducer(state = initialState, action) { |
||||
switch (action.type) { |
||||
case IS_SENDING_USER_REQUEST: |
||||
return { |
||||
...state, |
||||
isSendingUserRequest: action.data |
||||
}; |
||||
case SET_USER_REQUEST_ERROR: |
||||
return { |
||||
...state, |
||||
userRequestError: action.data |
||||
}; |
||||
case CLEAR_USER_REQUEST_ERROR: |
||||
return { |
||||
...state, |
||||
userRequestError: "" |
||||
}; |
||||
case SET_USER_REQUEST_SUCCESS: |
||||
return { |
||||
...state, |
||||
userRequestSuccess: action.data |
||||
}; |
||||
case CLEAR_USER_REQUEST_SUCCESS: |
||||
return { |
||||
...state, |
||||
userRequestSuccess: "" |
||||
}; |
||||
case SET_SELF_USER: |
||||
return { |
||||
...state, |
||||
selfUser: action.data |
||||
}; |
||||
case SET_COMPLETE_REGISTRATION_STEP: |
||||
return { |
||||
...state, |
||||
completeRegistrationCurrentStep: action.data, |
||||
completeRegistrationMaxStep: Math.max( |
||||
action.data, |
||||
state.completeRegistrationMaxStep |
||||
) |
||||
}; |
||||
case SET_COMPLETE_REGISTRATION_CLIENT_OR_PROVIDER: |
||||
return { |
||||
...state, |
||||
completeRegistrationClientOrProvider: action.data |
||||
}; |
||||
case SET_FORM_PHONE_NUMBER: |
||||
return { |
||||
...state, |
||||
phoneNumber: action.data |
||||
}; |
||||
case SET_FORM_BUSINESS_NUMBER: |
||||
return { |
||||
...state, |
||||
businessNumber: action.data |
||||
}; |
||||
case SET_FORM_SOCIAL_INSURANCE_NUMBER: |
||||
return { |
||||
...state, |
||||
socialInsuranceNumber: action.data |
||||
}; |
||||
case SET_EDIT_PROFILE_TAB_ACTIVE_INDEX: |
||||
return { |
||||
...state, |
||||
editProfileActiveIndex: action.data |
||||
}; |
||||
default: |
||||
return state; |
||||
} |
||||
} |
||||
|
||||
export default userReducer; |
@ -0,0 +1,108 @@ |
||||
// In production, we register a service worker to serve assets from local cache.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on the "N+1" visit to a page, since previously
|
||||
// cached resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
|
||||
// This link also includes instructions on opting out of this behavior.
|
||||
|
||||
const isLocalhost = Boolean( |
||||
window.location.hostname === 'localhost' || |
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' || |
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match( |
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ |
||||
) |
||||
); |
||||
|
||||
export default function register() { |
||||
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); |
||||
if (publicUrl.origin !== window.location.origin) { |
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
|
||||
return; |
||||
} |
||||
|
||||
window.addEventListener('load', () => { |
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; |
||||
|
||||
if (!isLocalhost) { |
||||
// Is not local host. Just register service worker
|
||||
registerValidSW(swUrl); |
||||
} else { |
||||
// This is running on localhost. Lets check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
function registerValidSW(swUrl) { |
||||
navigator.serviceWorker |
||||
.register(swUrl) |
||||
.then(registration => { |
||||
registration.onupdatefound = () => { |
||||
const installingWorker = registration.installing; |
||||
installingWorker.onstatechange = () => { |
||||
if (installingWorker.state === 'installed') { |
||||
if (navigator.serviceWorker.controller) { |
||||
// At this point, the old content will have been purged and
|
||||
// the fresh content will have been added to the cache.
|
||||
// It's the perfect time to display a "New content is
|
||||
// available; please refresh." message in your web app.
|
||||
console.log('New content is available; please refresh.'); |
||||
} 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.'); |
||||
} |
||||
} |
||||
}; |
||||
}; |
||||
}) |
||||
.catch(error => { |
||||
console.error('Error during service worker registration:', error); |
||||
}); |
||||
} |
||||
|
||||
function checkValidServiceWorker(swUrl) { |
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl) |
||||
.then(response => { |
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
if ( |
||||
response.status === 404 || |
||||
response.headers.get('content-type').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); |
||||
} |
||||
}) |
||||
.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(); |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,196 @@ |
||||
import { effects } from "redux-saga"; |
||||
import { |
||||
isSendingAuthRequest, |
||||
setAuthRequestError, |
||||
setAuthRequestSuccess, |
||||
clearAuthRequestError, |
||||
clearAuthRequestSuccess, |
||||
setSelfUserToken, |
||||
setFormEmail, |
||||
setFormPassword, |
||||
setFormPasswordConfirmation, |
||||
setFormOldPassword |
||||
} from "../actions/auth/reducer.actions"; |
||||
import { setSelfUser } from "../actions/user/reducer.actions"; |
||||
import { |
||||
registerUser, |
||||
verifyEmail, |
||||
loginUser, |
||||
logoutUser, |
||||
changePassword, |
||||
forgotPassword, |
||||
resetPassword |
||||
} from "../api/auth.api"; |
||||
|
||||
function* registerUserCall(postBody) { |
||||
yield effects.put(isSendingAuthRequest(true)); |
||||
const { email, password1, password2 } = postBody; |
||||
try { |
||||
return yield effects.call(registerUser, email, password1, password2); |
||||
} catch (exception) { |
||||
yield effects.put(setAuthRequestError(exception)); |
||||
return false; |
||||
} finally { |
||||
yield effects.put(isSendingAuthRequest(false)); |
||||
} |
||||
} |
||||
|
||||
function* verifyEmailCall(postBody) { |
||||
yield effects.put(isSendingAuthRequest(true)); |
||||
const { emailKey } = postBody; |
||||
try { |
||||
return yield effects.call(verifyEmail, emailKey); |
||||
} catch (exception) { |
||||
yield effects.put(setAuthRequestError(exception)); |
||||
return false; |
||||
} finally { |
||||
yield effects.put(isSendingAuthRequest(false)); |
||||
} |
||||
} |
||||
|
||||
function* loginUserCall(postBody) { |
||||
yield effects.put(isSendingAuthRequest(true)); |
||||
const { email, password } = postBody; |
||||
try { |
||||
return yield effects.call(loginUser, email, password); |
||||
} catch (exception) { |
||||
yield effects.put(setAuthRequestError(exception)); |
||||
return false; |
||||
} finally { |
||||
yield effects.put(isSendingAuthRequest(false)); |
||||
} |
||||
} |
||||
|
||||
function* logoutUserCall() { |
||||
yield effects.call(logoutUser); |
||||
} |
||||
|
||||
function* changePasswordCall(postBody) { |
||||
yield effects.put(isSendingAuthRequest(true)); |
||||
const { new_password1, new_password2, old_password } = postBody; |
||||
try { |
||||
return yield effects.call( |
||||
changePassword, |
||||
new_password1, |
||||
new_password2, |
||||
old_password |
||||
); |
||||
} catch (exception) { |
||||
yield effects.put(setAuthRequestError(exception)); |
||||
return false; |
||||
} finally { |
||||
yield effects.put(isSendingAuthRequest(false)); |
||||
} |
||||
} |
||||
|
||||
function* forgotPasswordCall(postBody) { |
||||
yield effects.put(isSendingAuthRequest(true)); |
||||
const { email } = postBody; |
||||
try { |
||||
return yield effects.call(forgotPassword, email); |
||||
} catch (exception) { |
||||
yield effects.put(setAuthRequestError(exception)); |
||||
return false; |
||||
} finally { |
||||
yield effects.put(isSendingAuthRequest(false)); |
||||
} |
||||
} |
||||
|
||||
function* resetPasswordCall(postBody) { |
||||
yield effects.put(isSendingAuthRequest(true)); |
||||
const { uid, token, new_password1, new_password2 } = postBody; |
||||
try { |
||||
return yield effects.call( |
||||
resetPassword, |
||||
uid, |
||||
token, |
||||
new_password1, |
||||
new_password2 |
||||
); |
||||
} catch (exception) { |
||||
yield effects.put(setAuthRequestError(exception)); |
||||
return false; |
||||
} finally { |
||||
yield effects.put(isSendingAuthRequest(false)); |
||||
} |
||||
} |
||||
|
||||
export function* registerUserFlow(request) { |
||||
yield effects.put(clearAuthRequestSuccess()); |
||||
yield effects.put(clearAuthRequestError()); |
||||
const wasSuccessful = yield effects.call(registerUserCall, request.data); |
||||
if (wasSuccessful) { |
||||
yield effects.put(setAuthRequestSuccess(wasSuccessful)); |
||||
yield effects.put(clearAuthRequestError()); |
||||
yield effects.put(setFormEmail("")); |
||||
yield effects.put(setFormPassword("")); |
||||
yield effects.put(setFormPasswordConfirmation("")); |
||||
} |
||||
} |
||||
|
||||
export function* verifyEmailFlow(request) { |
||||
yield effects.put(clearAuthRequestSuccess()); |
||||
yield effects.put(clearAuthRequestError()); |
||||
const wasSuccessful = yield effects.call(verifyEmailCall, request.data); |
||||
if (wasSuccessful) { |
||||
yield effects.put(setAuthRequestSuccess(wasSuccessful)); |
||||
yield effects.put(clearAuthRequestError()); |
||||
} |
||||
} |
||||
|
||||
export function* loginUserFlow(request) { |
||||
yield effects.put(clearAuthRequestSuccess()); |
||||
yield effects.put(clearAuthRequestError()); |
||||
const wasSuccessful = yield effects.call(loginUserCall, request.data); |
||||
if (wasSuccessful) { |
||||
yield effects.put(setSelfUserToken(wasSuccessful.key)); |
||||
yield effects.put(setAuthRequestSuccess(wasSuccessful)); |
||||
yield effects.put(clearAuthRequestError()); |
||||
yield effects.put(setFormEmail("")); |
||||
yield effects.put(setFormPassword("")); |
||||
yield effects.put(setFormPasswordConfirmation("")); |
||||
} |
||||
} |
||||
|
||||
export function* logoutUserFlow(request) { |
||||
yield effects.put(clearAuthRequestSuccess()); |
||||
yield effects.put(clearAuthRequestError()); |
||||
yield effects.call(logoutUserCall); |
||||
yield effects.put(setSelfUserToken("")); |
||||
yield effects.put(setSelfUser({})); |
||||
} |
||||
|
||||
export function* changePasswordFlow(request) { |
||||
yield effects.put(clearAuthRequestSuccess()); |
||||
yield effects.put(clearAuthRequestError()); |
||||
const wasSuccessful = yield effects.call(changePasswordCall, request.data); |
||||
if (wasSuccessful) { |
||||
yield effects.put(setAuthRequestSuccess(wasSuccessful)); |
||||
yield effects.put(clearAuthRequestError()); |
||||
yield effects.put(setFormOldPassword("")); |
||||
yield effects.put(setFormPassword("")); |
||||
yield effects.put(setFormPasswordConfirmation("")); |
||||
} |
||||
} |
||||
|
||||
export function* forgotPasswordFlow(request) { |
||||
yield effects.put(clearAuthRequestSuccess()); |
||||
yield effects.put(clearAuthRequestError()); |
||||
const wasSuccessful = yield effects.call(forgotPasswordCall, request.data); |
||||
if (wasSuccessful) { |
||||
yield effects.put(setAuthRequestSuccess(wasSuccessful)); |
||||
yield effects.put(clearAuthRequestError()); |
||||
} |
||||
} |
||||
|
||||
export function* resetPasswordFlow(request) { |
||||
yield effects.put(clearAuthRequestSuccess()); |
||||
yield effects.put(clearAuthRequestError()); |
||||
const wasSuccessful = yield effects.call(resetPasswordCall, request.data); |
||||
if (wasSuccessful) { |
||||
yield effects.put(setAuthRequestSuccess(wasSuccessful)); |
||||
yield effects.put(clearAuthRequestError()); |
||||
yield effects.put(setFormPassword("")); |
||||
yield effects.put(setFormPasswordConfirmation("")); |
||||
} |
||||
} |
@ -0,0 +1,54 @@ |
||||
import { takeLatest } from "redux-saga/effects"; |
||||
import { |
||||
SEND_REGISTER_REQUEST, |
||||
SEND_EMAIL_VERIFICATION_REQUEST, |
||||
SEND_LOGIN_REQUEST, |
||||
SEND_LOGOUT_REQUEST, |
||||
SEND_CHANGE_PASSWORD_REQUEST, |
||||
SEND_FORGOT_PASSWORD_REQUEST, |
||||
SEND_RESET_PASSWORD_REQUEST |
||||
} from "../constants/auth.constants"; |
||||
import { |
||||
registerUserFlow, |
||||
verifyEmailFlow, |
||||
loginUserFlow, |
||||
logoutUserFlow, |
||||
changePasswordFlow, |
||||
forgotPasswordFlow, |
||||
resetPasswordFlow, |
||||
} from "./auth.sagas"; |
||||
import { |
||||
GET_SELF_USER_REQUEST, |
||||
CREATE_USER_INFO_REQUEST, |
||||
UPDATE_USER_INFO_REQUEST, |
||||
CREATE_CLIENT_REQUEST, |
||||
UPDATE_CLIENT_REQUEST, |
||||
CREATE_PROVIDER_REQUEST, |
||||
UPDATE_PROVIDER_REQUEST |
||||
} from "../constants/user.constants"; |
||||
import { |
||||
getSelfUserFlow, |
||||
createUserInfoFlow, |
||||
updateUserInfoFlow, |
||||
createClientFlow, |
||||
updateClientFlow, |
||||
createProviderFlow, |
||||
updateProviderFlow |
||||
} from "./user.sagas"; |
||||
|
||||
export default function* rootSaga() { |
||||
yield takeLatest(SEND_REGISTER_REQUEST, registerUserFlow); |
||||
yield takeLatest(SEND_EMAIL_VERIFICATION_REQUEST, verifyEmailFlow); |
||||
yield takeLatest(SEND_LOGIN_REQUEST, loginUserFlow); |
||||
yield takeLatest(SEND_LOGOUT_REQUEST, logoutUserFlow); |
||||
yield takeLatest(SEND_CHANGE_PASSWORD_REQUEST, changePasswordFlow); |
||||
yield takeLatest(SEND_FORGOT_PASSWORD_REQUEST, forgotPasswordFlow); |
||||
yield takeLatest(SEND_RESET_PASSWORD_REQUEST, resetPasswordFlow); |
||||
yield takeLatest(GET_SELF_USER_REQUEST, getSelfUserFlow); |
||||
yield takeLatest(CREATE_USER_INFO_REQUEST, createUserInfoFlow); |
||||
yield takeLatest(UPDATE_USER_INFO_REQUEST, updateUserInfoFlow); |
||||
yield takeLatest(CREATE_CLIENT_REQUEST, createClientFlow); |
||||
yield takeLatest(UPDATE_CLIENT_REQUEST, updateClientFlow); |
||||
yield takeLatest(CREATE_PROVIDER_REQUEST, createProviderFlow); |
||||
yield takeLatest(UPDATE_PROVIDER_REQUEST, updateProviderFlow); |
||||
} |
@ -0,0 +1,186 @@ |
||||
import { effects } from "redux-saga"; |
||||
import { setSelfUserToken } from "../actions/auth/reducer.actions"; |
||||
import { |
||||
isSendingUserRequest, |
||||
setUserRequestError, |
||||
setUserRequestSuccess, |
||||
clearUserRequestError, |
||||
clearUserRequestSuccess, |
||||
setSelfUser, |
||||
setCompleteRegistrationStep |
||||
} from "../actions/user/reducer.actions"; |
||||
import { getSelfUserRequest } from "../actions/user/saga.actions"; |
||||
import { CLIENT_OR_PROVIDER_STEP } from "../constants/user.constants"; |
||||
import { getSelfUser, createUserInfo, updateUserInfo, createClient, updateClient, createProvider, updateProvider } from "../api/user.api"; |
||||
|
||||
function* getSelfUserCall() { |
||||
yield effects.put(isSendingUserRequest(true)); |
||||
try { |
||||
const wasSuccessful = yield effects.call(getSelfUser); |
||||
yield effects.put(clearUserRequestError()); |
||||
// Check if the user exists, if yes set the user, otherwise force logout
|
||||
if (wasSuccessful.results && wasSuccessful.results.length) { |
||||
yield effects.put(setSelfUser(wasSuccessful.results[0])); |
||||
} else { |
||||
yield effects.put(setSelfUserToken("")); |
||||
yield effects.put(setSelfUser({})); |
||||
} |
||||
return wasSuccessful; |
||||
} catch (exception) { |
||||
yield effects.put(setUserRequestError(exception)); |
||||
return false; |
||||
} finally { |
||||
yield effects.put(isSendingUserRequest(false)); |
||||
} |
||||
} |
||||
|
||||
function* createUserInfoCall(postBody) { |
||||
yield effects.put(isSendingUserRequest(true)); |
||||
const { phone_number } = postBody; |
||||
try { |
||||
return yield effects.call(createUserInfo, phone_number); |
||||
} catch (exception) { |
||||
yield effects.put(setUserRequestError(exception)); |
||||
return false; |
||||
} finally { |
||||
yield effects.put(isSendingUserRequest(false)); |
||||
} |
||||
} |
||||
|
||||
function* updateUserInfoCall(payload) { |
||||
yield effects.put(isSendingUserRequest(true)); |
||||
const { username, phone_number } = payload; |
||||
try { |
||||
return yield effects.call(updateUserInfo, username, phone_number); |
||||
} catch (exception) { |
||||
yield effects.put(setUserRequestError(exception)); |
||||
return false; |
||||
} finally { |
||||
yield effects.put(isSendingUserRequest(false)); |
||||
} |
||||
} |
||||
|
||||
function* createClientCall(postBody) { |
||||
yield effects.put(isSendingUserRequest(true)); |
||||
const { business_number } = postBody; |
||||
try { |
||||
return yield effects.call(createClient, business_number); |
||||
} catch (exception) { |
||||
yield effects.put(setUserRequestError(exception)); |
||||
return false; |
||||
} finally { |
||||
yield effects.put(isSendingUserRequest(false)); |
||||
} |
||||
} |
||||
|
||||
function* updateClientCall(payload) { |
||||
yield effects.put(isSendingUserRequest(true)); |
||||
const { username, business_number } = payload; |
||||
try { |
||||
return yield effects.call(updateClient, username, business_number); |
||||
} catch (exception) { |
||||
yield effects.put(setUserRequestError(exception)); |
||||
return false; |
||||
} finally { |
||||
yield effects.put(isSendingUserRequest(false)); |
||||
} |
||||
} |
||||
|
||||
function* createProviderCall(postBody) { |
||||
yield effects.put(isSendingUserRequest(true)); |
||||
const { sin } = postBody; |
||||
try { |
||||
return yield effects.call(createProvider, sin); |
||||
} catch (exception) { |
||||
yield effects.put(setUserRequestError(exception)); |
||||
return false; |
||||
} finally { |
||||
yield effects.put(isSendingUserRequest(false)); |
||||
} |
||||
} |
||||
|
||||
function* updateProviderCall(payload) { |
||||
yield effects.put(isSendingUserRequest(true)); |
||||
const { username, sin } = payload; |
||||
try { |
||||
return yield effects.call(updateProvider, username, sin); |
||||
} catch (exception) { |
||||
yield effects.put(setUserRequestError(exception)); |
||||
return false; |
||||
} finally { |
||||
yield effects.put(isSendingUserRequest(false)); |
||||
} |
||||
} |
||||
|
||||
export function* getSelfUserFlow(request) { |
||||
const wasSuccessful = yield effects.call(getSelfUserCall); |
||||
if (!wasSuccessful) { |
||||
yield effects.put(setSelfUserToken("")); |
||||
yield effects.put(setSelfUser({})); |
||||
} |
||||
} |
||||
|
||||
export function* createUserInfoFlow(request) { |
||||
yield effects.put(clearUserRequestSuccess()); |
||||
yield effects.put(clearUserRequestError()); |
||||
const wasSuccessful = yield effects.call(createUserInfoCall, request.data); |
||||
if (wasSuccessful) { |
||||
yield effects.put(clearUserRequestError()); |
||||
yield effects.put(setCompleteRegistrationStep(CLIENT_OR_PROVIDER_STEP)); |
||||
yield effects.put(getSelfUserRequest()); |
||||
} |
||||
} |
||||
|
||||
export function* updateUserInfoFlow(request) { |
||||
yield effects.put(clearUserRequestSuccess()); |
||||
yield effects.put(clearUserRequestError()); |
||||
const wasSuccessful = yield effects.call(updateUserInfoCall, request.data); |
||||
if (wasSuccessful) { |
||||
yield effects.put(setUserRequestSuccess(wasSuccessful)); |
||||
yield effects.put(clearUserRequestError()); |
||||
yield effects.put(setCompleteRegistrationStep(CLIENT_OR_PROVIDER_STEP)); |
||||
yield effects.put(getSelfUserRequest()); |
||||
} |
||||
} |
||||
|
||||
export function* createClientFlow(request) { |
||||
yield effects.put(clearUserRequestSuccess()); |
||||
yield effects.put(clearUserRequestError()); |
||||
const wasSuccessful = yield effects.call(createClientCall, request.data); |
||||
if (wasSuccessful) { |
||||
yield effects.put(clearUserRequestError()); |
||||
yield effects.put(getSelfUserRequest()); |
||||
} |
||||
} |
||||
|
||||
export function* updateClientFlow(request) { |
||||
yield effects.put(clearUserRequestSuccess()); |
||||
yield effects.put(clearUserRequestError()); |
||||
const wasSuccessful = yield effects.call(updateClientCall, request.data); |
||||
if (wasSuccessful) { |
||||
yield effects.put(setUserRequestSuccess(wasSuccessful)); |
||||
yield effects.put(clearUserRequestError()); |
||||
yield effects.put(getSelfUserRequest()); |
||||
} |
||||
} |
||||
|
||||
export function* createProviderFlow(request) { |
||||
yield effects.put(clearUserRequestSuccess()); |
||||
yield effects.put(clearUserRequestError()); |
||||
const wasSuccessful = yield effects.call(createProviderCall, request.data); |
||||
if (wasSuccessful) { |
||||
yield effects.put(clearUserRequestError()); |
||||
yield effects.put(getSelfUserRequest()); |
||||
} |
||||
} |
||||
|
||||
export function* updateProviderFlow(request) { |
||||
yield effects.put(clearUserRequestSuccess()); |
||||
yield effects.put(clearUserRequestError()); |
||||
const wasSuccessful = yield effects.call(updateProviderCall, request.data); |
||||
if (wasSuccessful) { |
||||
yield effects.put(setUserRequestSuccess(wasSuccessful)); |
||||
yield effects.put(clearUserRequestError()); |
||||
yield effects.put(getSelfUserRequest()); |
||||
} |
||||
} |
@ -0,0 +1,22 @@ |
||||
import { createStore, applyMiddleware, compose } from "redux"; |
||||
import createSagaMiddleware from "redux-saga"; |
||||
import { createLogger } from "redux-logger"; |
||||
import rootReducer from "../reducers"; |
||||
import rootSaga from "../sagas"; |
||||
|
||||
export default function configureStore(initialState = {}) { |
||||
const sagaMiddleware = createSagaMiddleware(); |
||||
const middlewares = [sagaMiddleware]; |
||||
if (process.env.NODE_ENV === "development" && process.env.REACT_APP_REDUX_LOGGING) { |
||||
middlewares.push(createLogger()); |
||||
} |
||||
|
||||
const enhancers = [applyMiddleware(...middlewares)]; |
||||
const store = createStore(rootReducer, initialState, compose(...enhancers)); |
||||
|
||||
// Extensions
|
||||
store.asyncReducers = {}; // Async reducer registry
|
||||
sagaMiddleware.run(rootSaga); |
||||
|
||||
return store; |
||||
} |
Loading…
Reference in new issue