Skip to content

Commit aed4e30

Browse files
authored
th-90: Customer sign-up flow be/fe (#153)
* th-90: + various improvements * th-90: * fix the behaviour of header sign-in button and add default navigate after sign-in * th-90: * fix frontend issues after merge with development * th-90: * fix backend routing for customer sign-up * th-90: * fix dissapearing background image in sign-up * th-90: * prettify some types * th-90: + add frontend support of server errors for forms * th-90: * fix tsconfig in shared folder * th-90: + add sign-up button to header * th-90: * change backend auth handler to send distinct errors on violation of email/phone constraints * th-90: - remove unused import * th-90: * temporarily revert constraint constants from the tables-schema * th-90: * move database unique violation error handling to separate error, small fixes * th-90: * resolve problems after merge developement * th-90: * change user type inside auth slice, various minor fixes after code review * th-90: * more minor fixes * th-90: * change user.group to user.group.key * th-90: * fix issues after code-review * th-90: + add error popups near the form elements on validation error, more fixes according to code review * th-90: + repeat of the previous commit * th-90: * fix issue with error appearence control * th-90: - remove thunk serializer wrapper and use rejectWithValue instead * th-90: * rework error handling to use redux state instead of direct interception * th-90: * make proper universal error handling hook and extract logic from the form component * th-90: * cosmetic improvements * th-90: * add enum to filename * th-90: * make error in slice possible to be null instead of undefined, other minor fixes * th-90: * more minor improvements according to review * th-90: * update auth component logic * th-90: - remove redundant useEffect * th-90: + add knexfile back and fix dependencies inside input component
1 parent 8412e44 commit aed4e30

File tree

74 files changed

+693
-232
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+693
-232
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export { HttpCode } from './libs/enums/enums.js';
22
export { HttpMessage } from './libs/enums/enums.js';
3+
export { HttpHeader } from './libs/enums/enums.js';
34
export { HttpError } from './libs/exceptions/exceptions.js';
45
export { type HttpMethod } from './libs/types/types.js';
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
export { HttpCode } from 'shared/build/index.js';
2-
export { HttpMessage } from 'shared/build/index.js';
1+
export { HttpCode, HttpHeader, HttpMessage } from 'shared/build/index.js';

backend/src/libs/types/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ export {
66
type OperationResult,
77
type RequireProperty,
88
type ServerCommonErrorResponse,
9-
type ServerErrorResponse,
109
type ServerValidationErrorResponse,
1110
type ValidationSchema,
1211
type ValueOf,

backend/src/packages/auth/auth.app-plugin.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { type FastifyReply, type FastifyRequest } from 'fastify';
22
import fp from 'fastify-plugin';
33

4-
import { HttpMessage } from '~/libs/packages/http/http.js';
4+
import { HttpHeader, HttpMessage } from '~/libs/packages/http/http.js';
55
import { type ValueOf } from '~/libs/types/types.js';
66

77
import { AuthStrategy } from './auth.js';
@@ -23,7 +23,10 @@ const authPlugin = fp<AuthPluginOptions>((fastify, options, done) => {
2323
done: (error?: Error) => void,
2424
): Promise<void> => {
2525
try {
26-
const token = request.headers.authorization?.replace('Bearer ', '');
26+
const token = request.headers[HttpHeader.AUTHORIZATION]?.replace(
27+
'Bearer ',
28+
'',
29+
);
2730

2831
if (!token && isJwtRequired) {
2932
return done(createUnauthorizedError(HttpMessage.UNAUTHORIZED));

backend/src/packages/auth/auth.service.ts

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -63,37 +63,46 @@ class AuthService {
6363
private async checkIsExistingUser({
6464
email,
6565
phone,
66-
}: CustomerSignUpRequestDto | BusinessSignUpRequestDto): Promise<boolean> {
66+
}: CustomerSignUpRequestDto | BusinessSignUpRequestDto): Promise<void> {
6767
const existingUser = await this.userService.findByPhoneOrEmail(
6868
phone,
6969
email,
7070
);
7171

72-
return Boolean(existingUser);
72+
if (email === existingUser?.email) {
73+
throw new HttpError({
74+
message: HttpMessage.USER_EMAIL_EXISTS,
75+
status: HttpCode.CONFLICT,
76+
});
77+
}
78+
79+
if (phone === existingUser?.phone) {
80+
throw new HttpError({
81+
message: HttpMessage.USER_PHONE_EXISTS,
82+
status: HttpCode.CONFLICT,
83+
});
84+
}
7385
}
7486

7587
private async checkIsExistingBusiness({
7688
taxNumber,
77-
}: BusinessSignUpRequestDto): Promise<boolean> {
89+
}: BusinessSignUpRequestDto): Promise<void> {
7890
const existingBusiness = await this.businessService.checkIsExistingBusiness(
7991
{ taxNumber },
8092
);
8193

82-
return Boolean(existingBusiness);
83-
}
84-
85-
public async signUpCustomer(
86-
payload: CustomerSignUpRequestDto,
87-
): Promise<UserEntityObjectWithGroupT> {
88-
const isExistingUser = await this.checkIsExistingUser(payload);
89-
90-
if (isExistingUser) {
94+
if (existingBusiness) {
9195
throw new HttpError({
92-
message: HttpMessage.USER_EXISTS,
96+
message: HttpMessage.BUSINESS_EXISTS,
9397
status: HttpCode.CONFLICT,
9498
});
9599
}
100+
}
96101

102+
public async signUpCustomer(
103+
payload: CustomerSignUpRequestDto,
104+
): Promise<UserEntityObjectWithGroupT> {
105+
await this.checkIsExistingUser(payload);
97106
const group = await this.groupService.findByKey(UserGroupKey.CUSTOMER);
98107

99108
if (!group) {
@@ -106,7 +115,6 @@ class AuthService {
106115
...payload,
107116
groupId: group.id,
108117
});
109-
110118
const userWithToken = await this.generateAccessTokenAndUpdateUser(
111119
newUser.id,
112120
);
@@ -117,22 +125,8 @@ class AuthService {
117125
public async signUpBusiness(
118126
payload: BusinessSignUpRequestDto,
119127
): Promise<UserEntityObjectWithGroupAndBusinessT> {
120-
const isExistingUser = await this.checkIsExistingUser(payload);
121-
const isExistingBusiness = await this.checkIsExistingBusiness(payload);
122-
123-
if (isExistingUser) {
124-
throw new HttpError({
125-
message: HttpMessage.USER_EXISTS,
126-
status: HttpCode.CONFLICT,
127-
});
128-
}
129-
130-
if (isExistingBusiness) {
131-
throw new HttpError({
132-
message: HttpMessage.BUSINESS_EXISTS,
133-
status: HttpCode.CONFLICT,
134-
});
135-
}
128+
await this.checkIsExistingUser(payload);
129+
await this.checkIsExistingBusiness(payload);
136130

137131
const {
138132
phone,

backend/src/packages/groups/group.entity.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
import { type IEntity } from '~/libs/interfaces/interfaces.js';
22
import { type NullableProperties } from '~/libs/types/types.js';
33

4+
import {
5+
type UserGroupEntityT,
6+
type UserGroupKeyT,
7+
} from '../users/libs/types/types.js';
48
import { type GroupEntityT } from './libs/types/types.js';
59

610
class GroupEntity implements IEntity {
711
private 'id': number | null;
812

913
private 'name': string;
1014

11-
private 'key': string;
15+
private 'key': UserGroupKeyT;
1216

1317
private constructor({
1418
id,
@@ -17,7 +21,7 @@ class GroupEntity implements IEntity {
1721
}: NullableProperties<GroupEntityT, 'id'>) {
1822
this.id = id;
1923
this.name = name;
20-
this.key = key;
24+
this.key = key as UserGroupKeyT;
2125
}
2226

2327
public static initialize({
@@ -43,15 +47,15 @@ class GroupEntity implements IEntity {
4347
});
4448
}
4549

46-
public toObject(): GroupEntityT {
50+
public toObject(): UserGroupEntityT {
4751
return {
4852
id: this.id as number,
4953
name: this.name,
5054
key: this.key,
5155
};
5256
}
5357

54-
public toNewObject(): Omit<GroupEntityT, 'id'> {
58+
public toNewObject(): Omit<UserGroupEntityT, 'id'> {
5559
return {
5660
name: this.name,
5761
key: this.key,

backend/src/packages/users/libs/types/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,4 @@ export {
1616
type UserGetAllResponseDto,
1717
type UserGroupEntityT,
1818
type UserGroupKeyT,
19-
type UserGroupNameT,
2019
} from 'shared/build/index.js';

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"@rollup/pluginutils": "5.0.4",
2828
"@svgr/core": "8.1.0",
2929
"@tanstack/react-table": "8.9.3",
30+
"balloon-css": "1.2.0",
3031
"clsx": "2.0.0",
3132
"modern-normalize": "2.0.0",
3233
"react": "18.2.0",
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
@use "sass:color";
2+
3+
@import "../vars.scss";
4+
@import "balloon-css/src/balloon.scss";
5+
6+
:root [aria-label][data-balloon-pos] {
7+
--balloon-border-radius: 4px;
8+
--balloon-color: #{color.adjust($red-dark, $lightness: -10%)};
9+
--balloon-font-size: 16px;
10+
--balloon-move: 30px;
11+
--balloon-text-color: #{$white};
12+
13+
&::before {
14+
right: 8px;
15+
z-index: 11;
16+
}
17+
18+
&::after {
19+
right: 4px;
20+
max-width: calc(100% - 8px);
21+
font-weight: $font-weight-regular;
22+
font-family: $font-family;
23+
white-space: normal;
24+
box-shadow: 0 3px 6px 0 #{color.adjust(
25+
$red-dark,
26+
$lightness: - 30%,
27+
$alpha: - 0.5
28+
)};
29+
}
30+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import "./balloon-css.plugin.scss";

0 commit comments

Comments
 (0)