Skip to content

Commit

Permalink
th-263: Log out implementation (BinaryStudioAcademy#293)
Browse files Browse the repository at this point in the history
* th-263: + logout route

* th-263: + logout action and api method

* th-263: + handle log out click in burger menu

* th-263: - removed redirect

* th-263: - removed unused variable

* th-263: * updates burger menu component

* th-263: * maked label optional for button component

* th-263: * changed passwordEye background to transparent

---------

Co-authored-by: Alina Kupchyk <[email protected]>
  • Loading branch information
h0wter and kalinkaaaa14 committed Sep 22, 2023
1 parent dc73c6a commit 769dca6
Show file tree
Hide file tree
Showing 14 changed files with 128 additions and 46 deletions.
23 changes: 23 additions & 0 deletions backend/src/packages/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ class AuthController extends Controller {
}>,
),
});

this.addRoute({
path: AuthApiPath.LOGOUT,
method: 'GET',
authStrategy: AuthStrategy.VERIFY_JWT,
handler: (options) =>
this.logOut(
options as ApiHandlerOptions<{
user: UserEntityObjectWithGroupT;
}>,
),
});
}

private async signUpCustomer(
Expand Down Expand Up @@ -131,6 +143,17 @@ class AuthController extends Controller {
payload: await this.authService.getCurrent(options.user),
};
}

private async logOut(
options: ApiHandlerOptions<{
user: UserEntityObjectWithGroupT;
}>,
): Promise<ApiHandlerResponse> {
return {
status: HttpCode.NO_CONTENT,
payload: await this.authService.logOut(options.user),
};
}
}

export { AuthController };
Expand Down
4 changes: 4 additions & 0 deletions backend/src/packages/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,10 @@ class AuthService {
return user;
}

public async logOut(user: UserEntityObjectWithGroupT): Promise<void> {
return await this.userService.removeAccessToken(user.id);
}

public async generateAccessTokenAndUpdateUser(
userId: UserEntityT['id'],
): Promise<UserEntityObjectT> {
Expand Down
4 changes: 4 additions & 0 deletions backend/src/packages/users/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ class UserService implements IService<UserEntityObjectT> {

return UserEntity.initialize(result).toObject();
}

public async removeAccessToken(id: UserEntityT['id']): Promise<void> {
await this.userRepository.update(id, { accessToken: null });
}
}

export { UserService };
52 changes: 34 additions & 18 deletions frontend/src/libs/components/burger-menu/burger-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IconName } from '~/libs/enums/enums.js';
import { BurgerMenuItemsName, IconName } from '~/libs/enums/enums.js';
import { getValidClassNames } from '~/libs/helpers/helpers.js';
import {
useAppDispatch,
useCallback,
useEffect,
useLocation,
Expand All @@ -9,6 +10,7 @@ import {
useState,
} from '~/libs/hooks/hooks.js';
import { type BurgerMenuItem } from '~/libs/types/types.js';
import { actions as authActions } from '~/slices/auth/auth.js';

import { Button, Icon } from '../components.js';
import styles from './styles.module.scss';
Expand All @@ -21,6 +23,7 @@ const BurgerMenu: React.FC<Properties> = ({ burgerItems }: Properties) => {
const [isOpen, setIsOpen] = useState(false);
const location = useLocation();
const navigate = useNavigate();
const dispatch = useAppDispatch();

const menuReference = useRef<HTMLDivElement | null>(null);

Expand All @@ -29,10 +32,17 @@ const BurgerMenu: React.FC<Properties> = ({ burgerItems }: Properties) => {
}, [isOpen]);

const handleNavigate = useCallback(
(navigateTo: string) => () => navigate(navigateTo),
(navigateTo: string) => () => {
navigate(navigateTo);
},
[navigate],
);

const handleLogOutClick = useCallback(
() => dispatch(authActions.logOut()),
[dispatch],
);

useEffect(() => {
setIsOpen(false);
}, [location]);
Expand Down Expand Up @@ -67,22 +77,28 @@ const BurgerMenu: React.FC<Properties> = ({ burgerItems }: Properties) => {
{isOpen && (
<div className={styles.menu}>
<ul>
{burgerItems.map((item, index) => (
<li key={index}>
<Icon
iconName={item.icon}
onClick={handleNavigate(item.navigateTo)}
className={styles.menuIcon}
/>
<Button
frontIcon={item.icon}
isFullWidth
label={item.name}
onClick={handleNavigate(item.navigateTo)}
className={styles.btn}
/>
</li>
))}
{burgerItems.map((item, index) => {
const clickHandler =
item.name === BurgerMenuItemsName.LOG_OUT
? handleLogOutClick
: handleNavigate(item.navigateTo);

return (
<li key={index}>
<Button
frontIcon={item.icon}
onClick={clickHandler}
className={styles.menuIcon}
/>
<Button
frontIcon={item.icon}
label={item.name}
onClick={clickHandler}
className={styles.btn}
/>
</li>
);
})}
</ul>
</div>
)}
Expand Down
33 changes: 19 additions & 14 deletions frontend/src/libs/components/burger-menu/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,29 @@

.btn {
justify-content: start;
width: 100%;
background-color: $blue-dark;
border-radius: 0;

&:hover {
&:hover,
&:active,
&:focus {
background-color: $blue;
}
}

.menuIcon {
width: 100%;
padding: 10px 20px;
color: $white;
font-size: 24px;
background-color: $blue-dark;

&:hover,
&:active,
&:focus {
background-color: $blue;
}
}
}

Expand All @@ -50,23 +61,17 @@
.btn {
display: none;
}

li {
padding: 10px 20px;

&:hover {
background-color: $blue;
}
}
}
}

@media (width > $screen-size-lg) {
.btn {
display: inline-flex;
}
.menu {
.btn {
display: inline-flex;
}

.menuIcon {
display: none;
.menuIcon {
display: none;
}
}
}
4 changes: 2 additions & 2 deletions frontend/src/libs/components/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import styles from './styles.module.scss';

type Properties = {
className?: Parameters<typeof getValidClassNames>[0];
label: string | number;
label?: string | number;
type?: 'button' | 'submit';
size?: 'sm' | 'md';
variant?: 'contained' | 'outlined' | 'text';
Expand All @@ -24,7 +24,7 @@ const Button: React.FC<Properties> = ({
type = 'button',
size = 'md',
variant = 'contained',
label,
label = '',
isDisabled = false,
isFullWidth = false,
frontIcon,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/libs/components/input/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ $error-message-height: calc($error-font-size * $line-height-coefficient);
top: 8px;
right: 10px;
color: $grey-dark;
background-color: $white;
background-color: transparent;
border: 0;
cursor: pointer;
user-select: none;
Expand Down
7 changes: 0 additions & 7 deletions frontend/src/libs/enums/breakpoint.ts

This file was deleted.

1 change: 0 additions & 1 deletion frontend/src/libs/enums/enums.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export { AppRoute } from './app-route.enum.js';
export { Breakpoint } from './breakpoint.js';
export { BurgerMenuItemsName } from './burger-menu-items-name.enum.js';
export { DataStatus } from './data-status.enum.js';
export { FormLabel, FormName } from './form.enum.js';
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/packages/auth/auth-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ class AuthApi extends HttpApi {
CustomerSignUpResponseDto | BusinessSignUpResponseDto
>();
}

public async logOut(): Promise<unknown> {
return await this.load(this.getFullEndpoint(AuthApiPath.LOGOUT, {}), {
method: 'GET',
contentType: ContentType.JSON,
hasAuth: true,
});
}
}

export { AuthApi };
19 changes: 18 additions & 1 deletion frontend/src/slices/auth/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,21 @@ const getCurrent = createAsyncThunk<
}
});

export { getCurrent, signIn, signUp };
const logOut = createAsyncThunk<unknown, undefined, AsyncThunkConfig>(
`${sliceName}/logout`,
async (_, { extra, rejectWithValue }) => {
const { authApi, notification, localStorage } = extra;

try {
await authApi.logOut();
await localStorage.drop(StorageKey.TOKEN);
} catch (error_: unknown) {
const error = error_ as HttpError;
notification.warning(getErrorMessage(error));

return rejectWithValue({ ...error, message: error.message });
}
},
);

export { getCurrent, logOut, signIn, signUp };
13 changes: 12 additions & 1 deletion frontend/src/slices/auth/auth.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
type ValueOf,
} from '~/libs/types/types.js';

import { getCurrent, signIn, signUp } from './actions.js';
import { getCurrent, logOut, signIn, signUp } from './actions.js';

type State = {
error: HttpError | null;
Expand Down Expand Up @@ -70,6 +70,17 @@ const { reducer, actions, name } = createSlice({
builder.addCase(getCurrent.rejected, (state) => {
state.dataStatus = DataStatus.REJECTED;
});
builder.addCase(logOut.pending, (state) => {
state.dataStatus = DataStatus.PENDING;
});
builder.addCase(logOut.fulfilled, (state) => {
state.user = initialState.user;
state.dataStatus = DataStatus.FULFILLED;
});
builder.addCase(logOut.rejected, (state, { payload }) => {
state.dataStatus = DataStatus.REJECTED;
state.error = payload ?? null;
});
},
});

Expand Down
3 changes: 2 additions & 1 deletion frontend/src/slices/auth/auth.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { getCurrent, signIn, signUp } from './actions.js';
import { getCurrent, logOut, signIn, signUp } from './actions.js';
import { actions } from './auth.slice.js';

const allActions = {
...actions,
signIn,
signUp,
getCurrent,
logOut,
};

export { allActions as actions };
Expand Down
1 change: 1 addition & 0 deletions shared/src/packages/auth/libs/enums/auth-api-path.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const AuthApiPath = {
SIGN_UP_BUSINESS: '/sign-up/business',
SIGN_IN: '/sign-in',
CURRENT: '/current',
LOGOUT: '/logout',
} as const;

export { AuthApiPath };

0 comments on commit 769dca6

Please sign in to comment.