Skip to content

Commit 4ebcbc6

Browse files
committed
Mech signup from UI
1 parent b3d3cba commit 4ebcbc6

File tree

9 files changed

+165
-4
lines changed

9 files changed

+165
-4
lines changed

services/web/src/actions/userActions.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ interface SignUpPayload extends ActionPayload {
3939
name: string;
4040
email: string;
4141
number: string;
42+
mechanic_code?: string;
4243
password: string;
4344
}
4445

@@ -108,6 +109,20 @@ export const signUpUserAction = ({
108109
};
109110
};
110111

112+
export const signUpMechanicAction = ({
113+
name,
114+
email,
115+
number,
116+
mechanic_code,
117+
password,
118+
callback,
119+
}: SignUpPayload) => {
120+
return {
121+
type: actionTypes.SIGN_UP_MECHANIC,
122+
payload: { name, email, number, mechanic_code, password, callback },
123+
};
124+
};
125+
111126
// clear store data and local storage and log user out
112127
export const logOutUserAction = ({ callback }: ActionPayload) => {
113128
return {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
.user-type-toggle {
2+
display: flex;
3+
gap: 0;
4+
margin-bottom: var(--spacing-xl);
5+
background: var(--bg-primary);
6+
border-radius: 50px;
7+
padding: 4px;
8+
border: 2px solid var(--secondary-color);
9+
box-shadow: var(--shadow-light);
10+
}
11+
12+
.toggle-button {
13+
flex: 1;
14+
padding: 12px 24px;
15+
border: none;
16+
background: transparent;
17+
color: var(--secondary-color);
18+
font-size: var(--font-size-md);
19+
font-weight: var(--font-weight-semibold);
20+
border-radius: 50px;
21+
cursor: pointer;
22+
transition: all var(--transition-normal);
23+
position: relative;
24+
z-index: 1;
25+
}
26+
27+
.toggle-button:hover {
28+
background: rgba(114, 46, 209, 0.1);
29+
}
30+
31+
.toggle-button.active {
32+
background: linear-gradient(135deg, #8b5cf6 0%, #a855f7 100%);
33+
color: var(--text-inverse);
34+
box-shadow: var(--shadow-medium);
35+
}
36+
37+
.toggle-button:focus {
38+
outline: none;
39+
}

services/web/src/components/signup/signup.tsx

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
*/
1515

1616
import { Button, Form, Input, Card } from "antd";
17-
import React from "react";
17+
import React, { useState } from "react";
1818
import { useNavigate } from "react-router-dom";
1919
import {
2020
EMAIL_REQUIRED,
@@ -24,36 +24,71 @@ import {
2424
CONFIRM_PASSWORD,
2525
PASSWORD_DO_NOT_MATCH,
2626
INVALID_PASSWORD,
27+
MECHANIC_CODE_REQUIRED,
2728
} from "../../constants/messages";
2829
import {
2930
EMAIL_VALIDATION,
3031
NAME_VALIDATION,
3132
PHONE_VALIDATION,
3233
PASSWORD_VALIDATION,
34+
MECHANIC_CODE_VALIDATION,
3335
} from "../../constants/constants";
36+
import "./signup.css";
3437

3538
interface SignupProps {
3639
hasErrored: boolean;
3740
errorMessage: string;
3841
onFinish: (values: any) => void;
42+
onMechanicFinish: (values: any) => void;
3943
}
4044

45+
type UserType = "user" | "mechanic";
46+
4147
const Signup: React.FC<SignupProps> = ({
4248
hasErrored = false,
4349
errorMessage = "",
4450
onFinish,
51+
onMechanicFinish,
4552
}) => {
4653
const navigate = useNavigate();
54+
const [userType, setUserType] = useState<UserType>("user");
55+
56+
const handleUserTypeChange = (type: UserType) => {
57+
setUserType(type);
58+
};
59+
const handleFormSubmit = (values: any) => {
60+
if (userType === "user") {
61+
onFinish(values);
62+
} else {
63+
onMechanicFinish(values);
64+
}
65+
};
4766

4867
return (
4968
<div className="container">
5069
<Card title="Sign Up" bordered={false} className="form-card">
70+
<div className="user-type-toggle">
71+
<button
72+
type="button"
73+
className={`toggle-button ${userType === "user" ? "active" : ""}`}
74+
onClick={() => handleUserTypeChange("user")}
75+
>
76+
User
77+
</button>
78+
<button
79+
type="button"
80+
className={`toggle-button ${userType === "mechanic" ? "active" : ""}`}
81+
onClick={() => handleUserTypeChange("mechanic")}
82+
>
83+
Mechanic
84+
</button>
85+
</div>
5186
<Form
5287
name="basic"
5388
initialValues={{
5489
remember: true,
5590
}}
56-
onFinish={onFinish}
91+
onFinish={handleFormSubmit}
5792
>
5893
<Form.Item
5994
name="name"
@@ -90,6 +125,20 @@ const Signup: React.FC<SignupProps> = ({
90125
>
91126
<Input placeholder="Phone No." />
92127
</Form.Item>
128+
{userType === "mechanic" && (
129+
<Form.Item
130+
name="mechanic_code"
131+
rules={[
132+
{ required: true, message: MECHANIC_CODE_REQUIRED },
133+
{
134+
pattern: MECHANIC_CODE_VALIDATION,
135+
message: MECHANIC_CODE_REQUIRED,
136+
},
137+
]}
138+
>
139+
<Input placeholder="Mechanic Code" />
140+
</Form.Item>
141+
)}
93142
<Form.Item
94143
name="password"
95144
rules={[

services/web/src/constants/APIConstant.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export const requestURLS: RequestURLSType = {
3838
UNLOCK: "api/auth/unlock",
3939
GET_USER: "api/v2/user/dashboard",
4040
SIGNUP: "api/auth/signup",
41+
SIGNUP_MECHANIC: "api/mechanic/signup",
4142
RESET_PASSWORD: "api/v2/user/reset-password",
4243
FORGOT_PASSWORD: "api/auth/forget-password",
4344
VERIFY_OTP: "api/auth/v3/check-otp",

services/web/src/constants/actionTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const actionTypes = {
1818
FETCHED_DATA: "FETCHED_DATA",
1919

2020
SIGN_UP: "SIGN_UP",
21+
SIGN_UP_MECHANIC: "SIGN_UP_MECHANIC",
2122
FORGOT_PASSWORD: "FORGOT_PASSWORD",
2223
VERIFY_OTP: "VERIFY_OTP",
2324
LOG_IN: "LOG_IN",

services/web/src/constants/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const EMAIL_VALIDATION: RegExp =
2020
/^(([^<>()[\]\\.,;:\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,}))$/;
2121
export const PHONE_VALIDATION: RegExp =
2222
/^(\+\d{1,2}\s?)?1?-?\.?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/;
23+
export const MECHANIC_CODE_VALIDATION: RegExp = /^MECH_[A-Za-z]+$/;
2324
export const PASSWORD_VALIDATION: RegExp =
2425
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$@!%&*?])[A-Za-z\d#$@!%&*?]{8,16}$/;
2526
export const NAME_VALIDATION: RegExp = /^[a-zA-Z ]+$/;

services/web/src/constants/messages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const EMAIL_REQUIRED: string = "Please enter a valid email!";
2121
export const PHONE_NO_REQUIRED: string = "Please enter phone number";
2222
export const INVALID_PHONE: string =
2323
"Contact number should only contain digits, (, ), + or spaces.";
24+
export const MECHANIC_CODE_REQUIRED: string = "Please enter a valid mechanic code starting with 'MECH_'!";
2425
export const PASSWORD_REQUIRED: string = "Please enter your password";
2526
export const INVALID_PASSWORD: React.ReactElement = React.createElement(
2627
"span",

services/web/src/containers/signup/signup.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ import { connect } from "react-redux";
2020
import { useNavigate } from "react-router-dom";
2121
import Signup from "../../components/signup/signup";
2222

23-
import { signUpUserAction } from "../../actions/userActions";
23+
import { signUpUserAction, signUpMechanicAction } from "../../actions/userActions";
2424
import responseTypes from "../../constants/responseTypes";
2525
import { SUCCESS_MESSAGE } from "../../constants/messages";
2626

2727
const SignupContainer = (props) => {
28-
const { signUpUser } = props;
28+
const { signUpUser, signUpMechanic } = props;
2929
const navigate = useNavigate();
3030

3131
const [hasErrored, setHasErrored] = useState(false);
@@ -47,22 +47,28 @@ const SignupContainer = (props) => {
4747
const onFinish = (values) => {
4848
signUpUser({ ...values, callback });
4949
};
50+
const onMechanicFinish = (values) => {
51+
signUpMechanic({ ...values, callback });
52+
};
5053

5154
return (
5255
<Signup
5356
hasErrored={hasErrored}
5457
errorMessage={errorMessage}
5558
onFinish={onFinish}
59+
onMechanicFinish={onMechanicFinish}
5660
/>
5761
);
5862
};
5963

6064
const mapDispatchToProps = {
6165
signUpUser: signUpUserAction,
66+
signUpMechanic: signUpMechanicAction,
6267
};
6368

6469
SignupContainer.propTypes = {
6570
signUpUser: PropTypes.func,
71+
signUpMechanic: PropTypes.func,
6672
};
6773

6874
export default connect(null, mapDispatchToProps)(SignupContainer);

services/web/src/sagas/userSaga.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,53 @@ export function* signUp(action: MyAction): Generator<any, void, any> {
258258
}
259259
}
260260

261+
/**
262+
* Request for new mechanic signup
263+
264+
* @payload {string} payload.name - User name
265+
* @payload {string} payload.email - User email
266+
* @payload {string} payload.number - User number
267+
* @payload {string} payload.mechanic_code - User mechanic code
268+
* @payload {string} payload.password - User password
269+
* @payload {Function} payload.callback - Callback method
270+
*/
271+
export function* signUpMechanic(action: MyAction): Generator<any, void, any> {
272+
const { name, email, number, mechanic_code, password, callback } = action.payload;
273+
let receivedResponse: Partial<Response> = {};
274+
try {
275+
yield put({ type: actionTypes.FETCHING_DATA });
276+
277+
const postUrl = APIService.WORKSHOP_SERVICE + requestURLS.SIGNUP_MECHANIC;
278+
const headers = {
279+
"Content-Type": "application/json",
280+
};
281+
// remove special chars from number
282+
let cleanedNumber = number.replace(/[^0-9+]/g, "");
283+
console.log("number", cleanedNumber);
284+
const responseJSON = yield fetch(postUrl, {
285+
headers,
286+
method: "POST",
287+
body: JSON.stringify({
288+
name: name,
289+
email: email,
290+
number: cleanedNumber,
291+
mechanic_code: mechanic_code,
292+
password: password,
293+
}),
294+
}).then((response: Response) => {
295+
receivedResponse = response;
296+
return response.json();
297+
});
298+
299+
yield put({ type: actionTypes.FETCHED_DATA, payload: receivedResponse });
300+
if (receivedResponse.ok) callback(responseTypes.SUCCESS, responseJSON.message);
301+
else callback(responseTypes.FAILURE, responseJSON.message || SIGN_UP_FAILED);
302+
} catch (e) {
303+
yield put({ type: actionTypes.FETCHED_DATA, payload: receivedResponse });
304+
callback(responseTypes.FAILURE, SIGN_UP_FAILED);
305+
}
306+
}
307+
261308
/**
262309
* Send OTP for forgot password
263310
@@ -548,6 +595,7 @@ export function* userActionWatcher() {
548595
yield takeLatest(actionTypes.VALIDATE_ACCESS_TOKEN, validateAccessToken);
549596
yield takeLatest(actionTypes.UNLOCK_USER, unlock);
550597
yield takeLatest(actionTypes.SIGN_UP, signUp);
598+
yield takeLatest(actionTypes.SIGN_UP_MECHANIC, signUpMechanic);
551599
yield takeLatest(actionTypes.VERIFY_OTP, verifyOTP);
552600
yield takeLatest(actionTypes.FORGOT_PASSWORD, forgotPassword);
553601
yield takeLatest(actionTypes.RESET_PASSWORD, resetPassword);

0 commit comments

Comments
 (0)