Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions services/web/src/actions/userActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ interface SignUpPayload extends ActionPayload {
name: string;
email: string;
number: string;
mechanic_code?: string;
password: string;
}

Expand Down Expand Up @@ -108,6 +109,20 @@ export const signUpUserAction = ({
};
};

export const signUpMechanicAction = ({
name,
email,
number,
mechanic_code,
password,
callback,
}: SignUpPayload) => {
return {
type: actionTypes.SIGN_UP_MECHANIC,
payload: { name, email, number, mechanic_code, password, callback },
};
};

// clear store data and local storage and log user out
export const logOutUserAction = ({ callback }: ActionPayload) => {
return {
Expand Down
39 changes: 39 additions & 0 deletions services/web/src/components/signup/signup.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.user-type-toggle {
display: flex;
gap: 0;
margin-bottom: var(--spacing-xl);
background: var(--bg-primary);
border-radius: 50px;
padding: 4px;
border: 2px solid var(--secondary-color);
box-shadow: var(--shadow-light);
}

.toggle-button {
flex: 1;
padding: 12px 24px;
border: none;
background: transparent;
color: var(--secondary-color);
font-size: var(--font-size-md);
font-weight: var(--font-weight-semibold);
border-radius: 50px;
cursor: pointer;
transition: all var(--transition-normal);
position: relative;
z-index: 1;
}

.toggle-button:hover {
background: rgba(114, 46, 209, 0.1);
}

.toggle-button.active {
background: linear-gradient(135deg, #8b5cf6 0%, #a855f7 100%);
color: var(--text-inverse);
box-shadow: var(--shadow-medium);
}

.toggle-button:focus {
outline: none;
}
53 changes: 51 additions & 2 deletions services/web/src/components/signup/signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
*/

import { Button, Form, Input, Card } from "antd";
import React from "react";
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import {
EMAIL_REQUIRED,
Expand All @@ -24,36 +24,71 @@ import {
CONFIRM_PASSWORD,
PASSWORD_DO_NOT_MATCH,
INVALID_PASSWORD,
MECHANIC_CODE_REQUIRED,
} from "../../constants/messages";
import {
EMAIL_VALIDATION,
NAME_VALIDATION,
PHONE_VALIDATION,
PASSWORD_VALIDATION,
MECHANIC_CODE_VALIDATION,
} from "../../constants/constants";
import "./signup.css";

interface SignupProps {
hasErrored: boolean;
errorMessage: string;
onFinish: (values: any) => void;
onMechanicFinish: (values: any) => void;
}

type UserType = "user" | "mechanic";

const Signup: React.FC<SignupProps> = ({
hasErrored = false,
errorMessage = "",
onFinish,
onMechanicFinish,
}) => {
const navigate = useNavigate();
const [userType, setUserType] = useState<UserType>("user");

const handleUserTypeChange = (type: UserType) => {
setUserType(type);
};
const handleFormSubmit = (values: any) => {
if (userType === "user") {
onFinish(values);
} else {
onMechanicFinish(values);
}
};

return (
<div className="container">
<Card title="Sign Up" bordered={false} className="form-card">
<div className="user-type-toggle">
<button
type="button"
className={`toggle-button ${userType === "user" ? "active" : ""}`}
onClick={() => handleUserTypeChange("user")}
>
User
</button>
<button
type="button"
className={`toggle-button ${userType === "mechanic" ? "active" : ""}`}
onClick={() => handleUserTypeChange("mechanic")}
>
Mechanic
</button>
</div>
<Form
name="basic"
initialValues={{
remember: true,
}}
onFinish={onFinish}
onFinish={handleFormSubmit}
>
<Form.Item
name="name"
Expand Down Expand Up @@ -90,6 +125,20 @@ const Signup: React.FC<SignupProps> = ({
>
<Input placeholder="Phone No." />
</Form.Item>
{userType === "mechanic" && (
<Form.Item
name="mechanic_code"
rules={[
{ required: true, message: MECHANIC_CODE_REQUIRED },
{
pattern: MECHANIC_CODE_VALIDATION,
message: MECHANIC_CODE_REQUIRED,
},
]}
>
<Input placeholder="Mechanic Code" />
</Form.Item>
)}
<Form.Item
name="password"
rules={[
Expand Down
1 change: 1 addition & 0 deletions services/web/src/constants/APIConstant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const requestURLS: RequestURLSType = {
UNLOCK: "api/auth/unlock",
GET_USER: "api/v2/user/dashboard",
SIGNUP: "api/auth/signup",
SIGNUP_MECHANIC: "api/mechanic/signup",
RESET_PASSWORD: "api/v2/user/reset-password",
FORGOT_PASSWORD: "api/auth/forget-password",
VERIFY_OTP: "api/auth/v3/check-otp",
Expand Down
1 change: 1 addition & 0 deletions services/web/src/constants/actionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const actionTypes = {
FETCHED_DATA: "FETCHED_DATA",

SIGN_UP: "SIGN_UP",
SIGN_UP_MECHANIC: "SIGN_UP_MECHANIC",
FORGOT_PASSWORD: "FORGOT_PASSWORD",
VERIFY_OTP: "VERIFY_OTP",
LOG_IN: "LOG_IN",
Expand Down
1 change: 1 addition & 0 deletions services/web/src/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const EMAIL_VALIDATION: RegExp =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
export const PHONE_VALIDATION: RegExp =
/^(\+\d{1,2}\s?)?1?-?\.?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/;
export const MECHANIC_CODE_VALIDATION: RegExp = /^MECH_[A-Za-z]+$/;
export const PASSWORD_VALIDATION: RegExp =
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$@!%&*?])[A-Za-z\d#$@!%&*?]{8,16}$/;
export const NAME_VALIDATION: RegExp = /^[a-zA-Z ]+$/;
Expand Down
2 changes: 2 additions & 0 deletions services/web/src/constants/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export const EMAIL_REQUIRED: string = "Please enter a valid email!";
export const PHONE_NO_REQUIRED: string = "Please enter phone number";
export const INVALID_PHONE: string =
"Contact number should only contain digits, (, ), + or spaces.";
export const MECHANIC_CODE_REQUIRED: string =
"Please enter a valid mechanic code starting with 'MECH_'!";
export const PASSWORD_REQUIRED: string = "Please enter your password";
export const INVALID_PASSWORD: React.ReactElement = React.createElement(
"span",
Expand Down
13 changes: 11 additions & 2 deletions services/web/src/containers/signup/signup.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ import { connect } from "react-redux";
import { useNavigate } from "react-router-dom";
import Signup from "../../components/signup/signup";

import { signUpUserAction } from "../../actions/userActions";
import {
signUpUserAction,
signUpMechanicAction,
} from "../../actions/userActions";
import responseTypes from "../../constants/responseTypes";
import { SUCCESS_MESSAGE } from "../../constants/messages";

const SignupContainer = (props) => {
const { signUpUser } = props;
const { signUpUser, signUpMechanic } = props;
const navigate = useNavigate();

const [hasErrored, setHasErrored] = useState(false);
Expand All @@ -47,22 +50,28 @@ const SignupContainer = (props) => {
const onFinish = (values) => {
signUpUser({ ...values, callback });
};
const onMechanicFinish = (values) => {
signUpMechanic({ ...values, callback });
};

return (
<Signup
hasErrored={hasErrored}
errorMessage={errorMessage}
onFinish={onFinish}
onMechanicFinish={onMechanicFinish}
/>
);
};

const mapDispatchToProps = {
signUpUser: signUpUserAction,
signUpMechanic: signUpMechanicAction,
};

SignupContainer.propTypes = {
signUpUser: PropTypes.func,
signUpMechanic: PropTypes.func,
};

export default connect(null, mapDispatchToProps)(SignupContainer);
51 changes: 51 additions & 0 deletions services/web/src/sagas/userSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,56 @@ export function* signUp(action: MyAction): Generator<any, void, any> {
}
}

/**
* Request for new mechanic signup

* @payload {string} payload.name - User name
* @payload {string} payload.email - User email
* @payload {string} payload.number - User number
* @payload {string} payload.mechanic_code - User mechanic code
* @payload {string} payload.password - User password
* @payload {Function} payload.callback - Callback method
*/
export function* signUpMechanic(action: MyAction): Generator<any, void, any> {
const { name, email, number, mechanic_code, password, callback } =
action.payload;
let receivedResponse: Partial<Response> = {};
try {
yield put({ type: actionTypes.FETCHING_DATA });

const postUrl = APIService.WORKSHOP_SERVICE + requestURLS.SIGNUP_MECHANIC;
const headers = {
"Content-Type": "application/json",
};
// remove special chars from number
let cleanedNumber = number.replace(/[^0-9+]/g, "");
console.log("number", cleanedNumber);
const responseJSON = yield fetch(postUrl, {
headers,
method: "POST",
body: JSON.stringify({
name: name,
email: email,
number: cleanedNumber,
mechanic_code: mechanic_code,
password: password,
}),
}).then((response: Response) => {
receivedResponse = response;
return response.json();
});

yield put({ type: actionTypes.FETCHED_DATA, payload: receivedResponse });
if (receivedResponse.ok)
callback(responseTypes.SUCCESS, responseJSON.message);
else
callback(responseTypes.FAILURE, responseJSON.message || SIGN_UP_FAILED);
} catch (e) {
yield put({ type: actionTypes.FETCHED_DATA, payload: receivedResponse });
callback(responseTypes.FAILURE, SIGN_UP_FAILED);
}
}

/**
* Send OTP for forgot password

Expand Down Expand Up @@ -548,6 +598,7 @@ export function* userActionWatcher() {
yield takeLatest(actionTypes.VALIDATE_ACCESS_TOKEN, validateAccessToken);
yield takeLatest(actionTypes.UNLOCK_USER, unlock);
yield takeLatest(actionTypes.SIGN_UP, signUp);
yield takeLatest(actionTypes.SIGN_UP_MECHANIC, signUpMechanic);
yield takeLatest(actionTypes.VERIFY_OTP, verifyOTP);
yield takeLatest(actionTypes.FORGOT_PASSWORD, forgotPassword);
yield takeLatest(actionTypes.RESET_PASSWORD, resetPassword);
Expand Down
Loading