Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LIME-69: Add Profile Settings #151

Merged
merged 62 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
c0974e5
LIME-69: + add profile settings
valery-chumak Feb 16, 2024
e15a9ab
LIME-69: + add styles for profile
valery-chumak Feb 16, 2024
32e6191
LIME-69: + fix styles for profile
valery-chumak Feb 16, 2024
284b119
LIME-69: * create basic layout
valery-chumak Feb 16, 2024
7f86ff6
LIME-69: * edit styles
valery-chumak Feb 17, 2024
ca7ee04
LIME-69: + merge development
valery-chumak Feb 17, 2024
3fcd363
LIME-69: * fixed validation gender
valery-chumak Feb 17, 2024
87de412
LIME-69: + add updateUser action
valery-chumak Feb 19, 2024
a637d6b
LIME-69: + solve merge conflicts
valery-chumak Feb 19, 2024
ab344fd
LIME-69: + avatar component
valery-chumak Feb 19, 2024
a9daac0
LIME-69: * add userId to payload
valery-chumak Feb 19, 2024
1ce32dd
LIME-69: * edit username
valery-chumak Feb 20, 2024
70d3e64
LIME-69: + backend logic for userUpdate
valery-chumak Feb 21, 2024
76f0d87
LIME-69: * edit request
valery-chumak Feb 21, 2024
a31057a
LIME-69: * edit validation and styles
valery-chumak Feb 21, 2024
d494db6
LIME-69: + crop spaces and leave digits
valery-chumak Feb 21, 2024
d614b9e
LIME-69: * edit user details type
valery-chumak Feb 21, 2024
fa09227
LIME-69: + token check
valery-chumak Feb 22, 2024
271bb57
LIME-69: * edit radioCard component
valery-chumak Feb 22, 2024
94eed62
LIME-69: * edit default values
valery-chumak Feb 22, 2024
f93c242
LIME-69: * button styles
valery-chumak Feb 22, 2024
106bf64
LIME-69: * solve error with label eslint
valery-chumak Feb 22, 2024
957f051
LIME-69: * fix error messages
valery-chumak Feb 22, 2024
b3b8845
LIME-69: + solve merge conflicts
valery-chumak Feb 22, 2024
0b30a31
LIME-69: * fix eslint error
valery-chumak Feb 22, 2024
19038b8
LIME-69: * edit radio component and styles
valery-chumak Feb 22, 2024
4ad70ea
LIME-69: * solve merge conflicts
valery-chumak Feb 23, 2024
b60ace7
LIME-69: * solve merge conflicts
valery-chumak Feb 23, 2024
e529c9a
LIME-69: * solve package errors
valery-chumak Feb 23, 2024
54420f6
LIME-69: * edit validation mode
valery-chumak Feb 23, 2024
84b73e3
LIME-69: + responsive design
valery-chumak Feb 24, 2024
fd0d8b8
LIME-69: * edit update service type
valery-chumak Feb 26, 2024
b686c35
LIME-69: * edit radio type
valery-chumak Feb 26, 2024
63dfd78
LIME-69: + solve merge conflicts
valery-chumak Feb 26, 2024
3f2c8e9
LIME-69: - should update func
valery-chumak Feb 26, 2024
5d72fc5
LIME-69: * changed service type
valery-chumak Feb 27, 2024
b5ea42d
LIME-69: * solve merge conflicts
valery-chumak Feb 27, 2024
c057363
LIME-69: * edit routing
valery-chumak Feb 27, 2024
82ae302
LIME-69: * fix tailwind config
valery-chumak Feb 27, 2024
f01a6fa
Merge branch 'development' into task/LIME-69-add-profile-settings
valery-chumak Feb 27, 2024
6ff19d5
LIME-69: * solve merge conflicts
valery-chumak Feb 27, 2024
6fc4c82
LIME-69: - userId as param
valery-chumak Feb 28, 2024
00c3120
LIME-69: * edit weight/height type
valery-chumak Feb 28, 2024
adaf24d
LIME-69: * change dateOfBirth handling
valery-chumak Feb 29, 2024
e074b30
LIME-69: * change migrations type of dateOfBirth
valery-chumak Feb 29, 2024
6c9f823
LIME-69: * solve merge conflicts
valery-chumak Feb 29, 2024
4be7f12
LIME-69: * fix eslint issues
valery-chumak Feb 29, 2024
8c2dd31
LIME-69: * edit updateRequest
valery-chumak Feb 29, 2024
6c0e7ec
LIME-69: * fix data handling
valery-chumak Mar 1, 2024
6abee47
LIME-69: * change to updateUserProfile
valery-chumak Mar 1, 2024
cd3b767
LIME-69: * change updateUserProfile payload
valery-chumak Mar 1, 2024
f82a456
LIME-69: * add user data as default payload
valery-chumak Mar 1, 2024
03c78ee
LIME-69: * change dateOfBirth to date
valery-chumak Mar 1, 2024
27fce08
LIME-69: * edit setDefaultValues
valery-chumak Mar 1, 2024
0162c32
LIME-69: * edit setDefaultValues
valery-chumak Mar 1, 2024
b13522b
LIME-69: * fix user repo
valery-chumak Mar 3, 2024
8c97740
LIME-69: * fix input warnings
valery-chumak Mar 3, 2024
c5bb5ef
LIME-69: * add fixes with form and validation
valery-chumak Mar 4, 2024
c42c044
LIME-69: * change range for weight/height
valery-chumak Mar 4, 2024
e66c71e
LIME-69: * fix some issues
valery-chumak Mar 4, 2024
09edfd6
LIME-69: * changed validation messages
valery-chumak Mar 4, 2024
b4c7c0e
LIME-69: * solve merge conflicts
valery-chumak Mar 5, 2024
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
1 change: 1 addition & 0 deletions backend/src/bundles/users/enums/enums.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { UserAttributes } from './user-attributes.enum.js';
export { UserDetailsAttributes } from './user-details-attributes.enum.js';
export { Gender, UsersApiPath } from 'shared';
export { AuthApiPath, UserValidationMessage } from 'shared';
1 change: 1 addition & 0 deletions backend/src/bundles/users/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export {
type UserAuthRequestDto,
type UserAuthResponseDto,
type UserGetAllResponseDto,
type UserUpdateProfileRequestDto,
type ValueOf,
} from 'shared';
2 changes: 2 additions & 0 deletions backend/src/bundles/users/user-details.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class UserDetailsModel extends AbstractModel {
public static override get tableName(): string {
return DatabaseTableName.USER_DETAILS;
}

[key: string]: unknown;
valery-chumak marked this conversation as resolved.
Show resolved Hide resolved
}

export { UserDetailsModel };
48 changes: 45 additions & 3 deletions backend/src/bundles/users/user.controller.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { type UserService } from '~/bundles/users/user.service.js';
import { type UserAuthResponseDto } from '~/bundles/users/users.js';
import {
type UserAuthResponseDto,
type UserUpdateProfileRequestDto,
} from '~/bundles/users/users.js';
import {
type ApiHandlerOptions,
type ApiHandlerResponse,
ApiHandlerResponseType,
BaseController,
} from '~/common/controller/controller.js';
import { BaseController } from '~/common/controller/controller.js';
import { ApiPath } from '~/common/enums/enums.js';
import { HttpCode } from '~/common/http/http.js';
import { HttpCode, HttpError } from '~/common/http/http.js';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like here

import { type Logger } from '~/common/logger/logger.js';

import { UsersApiPath } from './enums/enums.js';
Expand Down Expand Up @@ -96,6 +99,19 @@ class UserController extends BaseController {
}>,
),
});

this.addRoute({
path: UsersApiPath.UPDATE_USER,
method: 'PATCH',
isProtected: true,
handler: (options) =>
this.updateUser(
options as ApiHandlerOptions<{
user: UserAuthResponseDto;
body: UserUpdateProfileRequestDto;
}>,
),
});
}

/**
Expand Down Expand Up @@ -170,6 +186,32 @@ class UserController extends BaseController {
payload: user,
};
}

private async updateUser(
options: ApiHandlerOptions<{
user: UserAuthResponseDto;
body: UserUpdateProfileRequestDto;
}>,
): Promise<ApiHandlerResponse> {
const { user, body } = options;
const { id } = user;
try {
const updatedUser = await this.userService.updateUserProfile(
id,
body,
);
return {
type: ApiHandlerResponseType.DATA,
status: HttpCode.OK,
payload: updatedUser,
};
} catch (error) {
throw new HttpError({
message: `Something went wrong ${error}`,
status: HttpCode.BAD_REQUEST,
});
}
}
}

export { UserController };
41 changes: 40 additions & 1 deletion backend/src/bundles/users/user.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { UserEntity } from '~/bundles/users/user.entity.js';
import { type UserModel } from '~/bundles/users/user.model.js';
import { type Repository } from '~/common/types/types.js';

import { type UserDetailsModel } from './user-details.model.js';

class UserRepository implements Repository {
private userModel: typeof UserModel;

Expand Down Expand Up @@ -96,7 +98,6 @@ class UserRepository implements Repository {
throw error;
}
}

public async update(
query: Record<string, unknown>,
payload: Record<string, unknown>,
Expand All @@ -110,6 +111,44 @@ class UserRepository implements Repository {
.execute();
}

public async updateUserProfile(
userId: number,
payload: Partial<UserDetailsModel>,
): Promise<UserEntity | null> {
const trx = await this.userModel.startTransaction();
try {
const user = await this.userModel.query(trx).findById(userId);

if (!user) {
return null;
}

const userDetails = await user
.$relatedQuery('userDetails', trx)
.patch(payload)
.returning('*')
.first();

await trx.commit();

if (!userDetails) {
return null;
}
return UserEntity.initialize({
...user,
fullName: userDetails.fullName,
avatarUrl: userDetails.avatarUrl,
username: userDetails.username,
dateOfBirth: userDetails.dateOfBirth,
weight: userDetails.weight,
height: userDetails.height,
gender: userDetails.gender,
});
} catch (error) {
await trx.rollback();
throw new Error(`Error updating user details: ${error}`);
}
}
public delete(): ReturnType<Repository['delete']> {
return Promise.resolve(true);
}
Expand Down
24 changes: 24 additions & 0 deletions backend/src/bundles/users/user.service.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { UserEntity } from '~/bundles/users/user.entity.js';
import { type UserRepository } from '~/bundles/users/user.repository.js';
import { HttpCode, HttpError } from '~/common/http/http.js';
import { cryptService, stripeService } from '~/common/services/services.js';
import { type Service } from '~/common/types/types.js';

import { UserValidationMessage } from './enums/enums.js';
import {
type UserAuthRequestDto,
type UserAuthResponseDto,
type UserGetAllResponseDto,
type UserUpdateProfileRequestDto,
} from './types/types.js';
import { type UserDetailsModel } from './user-details.model.js';

class UserService implements Service {
private userRepository: UserRepository;
Expand Down Expand Up @@ -48,6 +52,26 @@ class UserService implements Service {
return user.toObject() as UserAuthResponseDto;
}

public async updateUserProfile(
userId: number,
payload: UserUpdateProfileRequestDto,
): Promise<UserAuthResponseDto | null> {
try {
const updatedUser = await this.userRepository.updateUserProfile(
userId,
payload as Partial<UserDetailsModel>,
);
if (!updatedUser) {
throw new HttpError({
message: UserValidationMessage.USER_NOT_FOUND,
status: HttpCode.NOT_FOUND,
});
}
return updatedUser.toObject() as UserAuthResponseDto;
} catch (error) {
throw new Error(`Error occured ${error}`);
}
}
public async update(
query: Record<string, unknown>,
payload: Record<string, unknown>,
Expand Down
2 changes: 2 additions & 0 deletions backend/src/bundles/users/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export { userController, userService };
export {
type UserAuthRequestDto,
type UserAuthResponseDto,
type UserUpdateProfileRequestDto,
} from './types/types.js';
export { UserEntity } from './user.entity.js';
export { UserModel } from './user.model.js';
Expand All @@ -21,4 +22,5 @@ export {
passwordForgotValidationSchema,
passwordResetValidationSchema,
userAuthValidationSchema,
userUpdateProfileValidationSchema,
} from './validation-schemas/validation-schemas.js';
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export {
passwordForgotValidationSchema,
passwordResetValidationSchema,
userAuthValidationSchema,
userUpdateProfileValidationSchema,
} from 'shared';
2 changes: 1 addition & 1 deletion backend/src/common/types/service.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type Service<T = unknown> = {
update(
query: Record<string, unknown>,
payload: Record<string, unknown>,
): Promise<T>;
): Promise<T | null>;
delete(payload: unknown): Promise<boolean>;
};

Expand Down
1 change: 1 addition & 0 deletions backend/src/common/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export {
type ServerCommonErrorResponse,
type ServerErrorResponse,
type ServerValidationErrorResponse,
type UserUpdateProfileRequestDto,
type ValidationSchema,
type ValueOf,
} from 'shared';
5 changes: 4 additions & 1 deletion frontend/src/bundles/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ const authApi = new AuthApi({
});

export { authApi };
export { type AuthResponseDto } from './types/types.js';
export {
type AuthResponseDto,
type UserAuthResponseDto,
} from './types/types.js';
16 changes: 14 additions & 2 deletions frontend/src/bundles/auth/store/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import {
type AsyncThunkConfig,
type UserAuthResponseDto,
} from '~/bundles/common/types/types.js';
import { type UserAuthRequestDto } from '~/bundles/users/users.js';
import {
type UserAuthRequestDto,
type UserUpdateProfileRequestDto,
} from '~/bundles/users/users.js';
import { storage, StorageKey } from '~/framework/storage/storage.js';

import { type AuthResponseDto } from '../auth.js';
Expand Down Expand Up @@ -46,4 +49,13 @@ const refreshUser = createAsyncThunk<
return userApi.refreshUser();
});

export { refreshUser, signIn, signUp };
const updateUser = createAsyncThunk<
UserAuthResponseDto,
UserUpdateProfileRequestDto,
AsyncThunkConfig
>(`${sliceName}/update-user`, async (updateUserPayload, { extra }) => {
const { userApi } = extra;
return await userApi.updateUser(updateUserPayload);
});

export { refreshUser, signIn, signUp, updateUser };
3 changes: 2 additions & 1 deletion frontend/src/bundles/auth/store/auth.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { refreshUser, signIn, signUp } from './actions.js';
import { refreshUser, signIn, signUp, updateUser } from './actions.js';
import { actions } from './slice.js';

const allActions = {
...actions,
signUp,
signIn,
updateUser,
refreshUser,
};

Expand Down
12 changes: 11 additions & 1 deletion frontend/src/bundles/auth/store/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
type ValueOf,
} from '~/bundles/common/types/types.js';

import { refreshUser, signIn, signUp } from './actions.js';
import { refreshUser, signIn, signUp, updateUser } from './actions.js';

type State = {
dataStatus: ValueOf<typeof DataStatus>;
Expand Down Expand Up @@ -58,6 +58,16 @@ const { reducer, actions, name } = createSlice({
state.dataStatus = DataStatus.REJECTED;
state.isRefreshing = false;
});
builder.addCase(updateUser.pending, (state) => {
state.dataStatus = DataStatus.PENDING;
});
builder.addCase(updateUser.fulfilled, (state, action) => {
state.dataStatus = DataStatus.FULFILLED;
state.user = action.payload;
});
builder.addCase(updateUser.rejected, (state) => {
state.dataStatus = DataStatus.REJECTED;
});
},
});

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/bundles/auth/types/types.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { type AuthResponseDto } from 'shared';
export { type AuthResponseDto, type UserAuthResponseDto } from 'shared';
1 change: 1 addition & 0 deletions frontend/src/bundles/common/components/components.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { Avatar } from './avatar/avatar.js';
export { Button, ButtonVariant } from './button/button.js';
export { DatePicker } from './date-picker/date-picker.js';
export { ForgotPasswordForm } from './forgot-password-form/forgot-password-form.js';
export { Header } from './header/header.js';
export { Icon } from './icon/icon.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ const DatePicker = <T extends FieldValues>({
const handleDaySelect = useCallback(
(date: DateObjectProperties): false | undefined => {
if ((date as DateObject).isValid) {
field.onChange((date as DateObject).format());
const formattedDate = (date as DateObject).format('DD/MM/YYYY');
field.onChange(formattedDate);
return;
}

Expand All @@ -47,6 +48,7 @@ const DatePicker = <T extends FieldValues>({
containerClassName={'custom-date-picker'}
onChange={handleDaySelect}
offsetY={label ? -10 : -30}
format="DD/MM/YYYY"
render={
<Input
type="text"
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/bundles/common/components/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from '~/bundles/common/hooks/hooks.js';

type Properties<T extends FieldValues> = {
className?: string;
control: Control<T, null>;
errors: FieldErrors<T>;
label: string;
Expand All @@ -26,6 +27,7 @@ type Properties<T extends FieldValues> = {
};

const Input = <T extends FieldValues>({
className = '',
control,
errors,
label,
Expand All @@ -52,7 +54,7 @@ const Input = <T extends FieldValues>({
};

return (
<label className="flex h-20 w-full flex-col text-sm">
<label className={`${className} flex h-20 flex-col text-sm text-white`}>
<span className="mb-[0.5rem] font-medium">
{label} {required && <span className="text-lm-red">*</span>}
</span>
Expand Down
Loading