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

[FE] 3차 데모데이 - 1차 QA 후 수정사항 반영 #331

Merged
merged 19 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3002811
fix(src): 쿼리키 CATEGORY_LIST 로 통일
Hain-tain Aug 6, 2024
0ac379f
feat(authentication): 로그인 관련 Toast 추가
Hain-tain Aug 6, 2024
0729557
Merge pull request #306 from Hain-tain/fix/category_post
Jaymyong66 Aug 6, 2024
1e147d9
test(mocks): 카테고리목록, 템플릿목록 목 데이터 추가
vi-wolhwa Aug 7, 2024
4ec07f1
feat(src): '내 템플릿' 페이지 검색 기능 구현
vi-wolhwa Aug 7, 2024
4939afd
refactor(src): pageSize 상수화 및 검색에서 'useInput'사용
vi-wolhwa Aug 7, 2024
a7db672
refactor(src): GET templateList 요청 시 태그의 배열을 전달할 수 있도록 리팩토링
vi-wolhwa Aug 7, 2024
a48e5c8
refactor(MyTemplatePage): 내 템플릿 검색 디바운스 시간 변경 (250ms > 300ms)
vi-wolhwa Aug 7, 2024
c542d73
Merge pull request #318 from vi-wolhwa/feat/my-templates-search
Jaymyong66 Aug 7, 2024
9353d20
chore: gitignore에 SLL 인증서 관련 키 추가
Jaymyong66 Aug 7, 2024
3783fd5
fix(authentication): useCheckLoginState 훅에서 토스트 알림 제거
Jaymyong66 Aug 7, 2024
c364298
refactor(Header): Header에서의 로그인 상태 체크 훅 제거
Jaymyong66 Aug 7, 2024
7d37f43
refactor(authentication): 로그인 성공 시, useAuth의 로그인 상태 변경
Jaymyong66 Aug 7, 2024
e864376
feat(routes): 모든 경로에 AuthGuard, GuestGuard 적용
Jaymyong66 Aug 7, 2024
8c3c9f5
feat(Header): 마이페이지 버튼 -> 로그아웃 버튼으로 교체
Jaymyong66 Aug 7, 2024
aaea162
refactor(workflows): frontend_cd - Sentry 환경변수 설정
Jaymyong66 Aug 7, 2024
5aaafd1
design(LoginPage): '계정이 없으신가요' 메시지의 align, size, gap 변경
Jaymyong66 Aug 7, 2024
7f0a755
design(SignupPage): '이미 계정이 있으신가요?' 메시지의 align, size, gap 변경
Jaymyong66 Aug 7, 2024
826471a
Merge pull request #327 from Jaymyong66/refactor/authState
vi-wolhwa Aug 7, 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
6 changes: 6 additions & 0 deletions .github/workflows/frontend_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,16 @@ jobs:
run: |
echo "REACT_APP_API_URL=${{ secrets.REACT_APP_API_URL }}" > ${{ env.frontend-directory }}/.env.production
echo "REACT_APP_BASE_URL=${{ secrets.REACT_APP_BASE_URL }}" >> ${{ env.frontend-directory }}/.env.production
echo "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" >> ${{ env.frontend-directory }}/.env.production
echo "SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> ${{ env.frontend-directory }}/.env.production
echo "SENTRY_DSN=${{ secrets.SENTRY_AUTH_TOKEN }}" >> ${{ env.frontend-directory }}/.env.sentry-build-plugin

- name: Set environment file permissions
run: chmod 644 ${{ env.frontend-directory }}/.env.production

- name: Set Sentry environment file permissions
run: chmod 644 ${{ env.frontend-directory }}/.env.sentry-build-plugin

- name: Install Dependencies
run: npm install
working-directory: ${{ env.frontend-directory }}
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@

### Environment Variable ###
.env

*.crt
*.csr
*.key
*.pem
1 change: 1 addition & 0 deletions frontend/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export { customFetch } from './customFetch';
export { QUERY_KEY } from './queryKeys';
export {
TEMPLATE_API_URL,
PAGE_SIZE,
getTemplateList,
getTemplate,
postTemplate,
Expand Down
1 change: 0 additions & 1 deletion frontend/src/api/queryKeys.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export const QUERY_KEY = {
CATEGORY: 'category',
CATEGORY_LIST: 'categoryList',
CHECK_EMAIL: 'checkEmail',
CHECK_USERNAME: 'userName',
Expand Down
29 changes: 20 additions & 9 deletions frontend/src/api/templates.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
import type { Template, TemplateEditRequest, TemplateListResponse, TemplateUploadRequest } from '@/types';
import type {
Template,
TemplateEditRequest,
TemplateListResponse,
TemplateUploadRequest,
TemplateListRequest,
} from '@/types';
import { customFetch } from './customFetch';

const API_URL = process.env.REACT_APP_API_URL;

export const TEMPLATE_API_URL = `${API_URL}/templates`;

export const getTemplateList = async (
categoryId?: number,
tagId?: number,
page: number = 1,
pageSize: number = 20,
): Promise<TemplateListResponse> => {
export const PAGE_SIZE = 20;

export const getTemplateList = async ({
categoryId,
tagIds,
page = 1,
pageSize = PAGE_SIZE,
keyword = '',
}: TemplateListRequest): Promise<TemplateListResponse> => {
const url = new URL(TEMPLATE_API_URL);

url.searchParams.append('keyword', keyword);

if (categoryId) {
url.searchParams.append('categoryId', categoryId.toString());
}

if (tagId) {
url.searchParams.append('tags', tagId.toString());
if (tagIds) {
url.searchParams.append('tagIds', tagIds.toString());
}

url.searchParams.append('pageNumber', page.toString());
Expand Down
15 changes: 6 additions & 9 deletions frontend/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Link } from 'react-router-dom';

import { logoIcon, newTemplateIcon, userMenuIcon } from '@/assets/images';
import { logoIcon, newTemplateIcon } from '@/assets/images';
import { Button, Flex, Heading, Text } from '@/components';
import { useCheckLoginState } from '@/hooks/authentication';
import { useAuth } from '@/hooks/authentication/useAuth';
import { useLogoutMutation } from '@/queries/authentication/useLogoutMutation';
import { theme } from '../../style/theme';
Expand All @@ -11,8 +10,6 @@ import * as S from './Header.style';
const Header = ({ headerRef }: { headerRef: React.RefObject<HTMLDivElement> }) => {
const { isLogin } = useAuth();

useCheckLoginState();

return (
<S.HeaderContainer ref={headerRef}>
<S.HeaderContentContainer>
Expand All @@ -28,7 +25,7 @@ const Header = ({ headerRef }: { headerRef: React.RefObject<HTMLDivElement> }) =
<img src={newTemplateIcon} alt='' />새 템플릿
</Button>
</Link>
{isLogin ? <UserMenuButton /> : <LoginButton />}
{isLogin ? <LogoutButton /> : <LoginButton />}
</Flex>
</S.HeaderContentContainer>
</S.HeaderContainer>
Expand All @@ -54,17 +51,17 @@ const NavOption = ({ route, name }: { route: string; name: string }) => (
</Link>
);

const UserMenuButton = () => {
const LogoutButton = () => {
const { mutateAsync } = useLogoutMutation();

const handleLogoutButton = async () => {
await mutateAsync();
};

return (
<S.UserMenuButton onClick={handleLogoutButton}>
<img src={userMenuIcon} alt='사용자 메뉴' />
</S.UserMenuButton>
<Button variant='text' size='medium' weight='bold' hoverStyle='none' onClick={handleLogoutButton}>
로그아웃
</Button>
);
};

Expand Down
11 changes: 6 additions & 5 deletions frontend/src/hooks/authentication/useCheckLoginState.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
import { useCallback, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

import { ToastContext } from '@/context/ToastContext';
import { useLoginStateQuery } from '@/queries/authentication/useLoginStateQuery';
import useCustomContext from '../utils/useCustomContext';
import { useAuth } from './useAuth';

export const useCheckLoginState = () => {
const { error, isError, isSuccess } = useLoginStateQuery();
const { error, isError, status } = useLoginStateQuery();
const navigate = useNavigate();
const { handleLoginState } = useAuth();
const { infoAlert } = useCustomContext(ToastContext);

const handleLoginNavigate = useCallback(() => {
navigate('/login');
}, [navigate]);

useEffect(() => {
if (isError) {
alert(error.message);
handleLoginNavigate();
handleLoginState(false);
}

if (isSuccess) {
if (status === 'success') {
handleLoginState(true);
}
}, [error, isError, isSuccess, handleLoginNavigate, handleLoginState]);
}, [error, isError, status, handleLoginNavigate, handleLoginState, infoAlert]);
};
9 changes: 9 additions & 0 deletions frontend/src/hooks/authentication/useLoginForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ import { FormEvent } from 'react';
import { useNavigate } from 'react-router-dom';

import { postLogin } from '@/api/authentication';
import { ToastContext } from '@/context/ToastContext';
import { useInputWithValidate } from '../useInputWithValidate';
import useCustomContext from '../utils/useCustomContext';
import { useAuth } from './useAuth';
import { validateEmail, validatePassword } from './validates';

export const useLoginForm = () => {
const navigate = useNavigate();
const { failAlert, successAlert } = useCustomContext(ToastContext);

const { handleLoginState } = useAuth();

const {
value: email,
Expand All @@ -30,11 +36,14 @@ export const useLoginForm = () => {

if (!response.ok) {
console.error(response);
failAlert('로그인에 실패하였습니다.');

return;
}

handleLoginState(true);
navigate('/');
successAlert('로그인 성공!');
}
};

Expand Down
1 change: 1 addition & 0 deletions frontend/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { useWindowWidth } from './useWindowWidth';
export { useDebounce } from './utils/useDebounce';
17 changes: 17 additions & 0 deletions frontend/src/hooks/utils/useDebounce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useState, useEffect } from 'react';

export const useDebounce = (value: string, delay: number) => {
const [debouncedValue, setDebouncedValue] = useState(value);

useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);

return () => {
clearTimeout(handler);
};
}, [value, delay]);

return debouncedValue;
};
4 changes: 4 additions & 0 deletions frontend/src/mocks/categoryList.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"categories": [
{
"id": 1,
"name": "카테고리 없음"
},
{
"id": 2,
"name": "Category1"
Expand Down
18 changes: 14 additions & 4 deletions frontend/src/mocks/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,30 @@ import mockTemplateList from './templateList.json';
export const templateHandlers = [
http.get(`${TEMPLATE_API_URL}`, (req) => {
const url = new URL(req.request.url);
const categoryId = url.searchParams.get('category');
const tagId = url.searchParams.get('tag');
const keyword = url.searchParams.get('keyword');
const categoryId = url.searchParams.get('categoryId');
const tagIds = url.searchParams.get('tagIds');
const page = parseInt(url.searchParams.get('page') || '1', 10);
const pageSize = parseInt(url.searchParams.get('pageSize') || '20', 10);

let filteredTemplates = mockTemplateList.templates;

if (keyword) {
filteredTemplates = filteredTemplates.filter(
(template) =>
template.title.includes(keyword) ||
template.description.includes(keyword) ||
template.snippets.some((snippet) => snippet.content.includes(keyword)),
);
}

if (categoryId) {
filteredTemplates = filteredTemplates.filter((template) => template.category.id.toString() === categoryId);
}

if (tagId) {
if (tagIds) {
filteredTemplates = filteredTemplates.filter((template) =>
template.tags.some((tag) => tag.id.toString() === tagId),
template.tags.some((tag) => tagIds.split(',').includes(tag.id.toString())),
);
}

Expand Down
Loading