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

feat: 로그인 기능 구현 #203

Merged
merged 21 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c0eb17e
chore: react-router-dom 라이브러리 설정 및 셋팅 (#193)
turtle601 Jul 28, 2023
c259975
chore: naver, kakao 아이콘 셋팅 (#193)
turtle601 Jul 28, 2023
cc8fc40
feat: api 관련 상수 선언 (#193)
turtle601 Jul 28, 2023
c4190d9
feat: 네이버, 카카오 로그인 버튼 구현 (#193)
turtle601 Jul 28, 2023
97445a3
feat: Oauth 기능 구현 (#193)
turtle601 Jul 28, 2023
3e34ca9
feat: 구글 로그인 버튼 ui 구현 (#193)
turtle601 Jul 29, 2023
38b01c3
feat: 내 정보 아이콘 ui 구현 (#193)
turtle601 Jul 29, 2023
c387492
feat: InfoDropDown컴포넌트 구현 (#193)
turtle601 Jul 29, 2023
59187a6
feat: Modal 컴포넌트 구현 (#193)
turtle601 Jul 29, 2023
eee7e13
feat: LoginModalContnet 컴포넌트 구현 (#193)
turtle601 Jul 29, 2023
941cc82
feat: Header에 InfoButton 컴포넌트 적용 및 기능 구현 (#193)
turtle601 Jul 29, 2023
46ebf24
refactor: Modal 컴포넌트 가운데에 정렬이 되도록 수정 (#193)
turtle601 Jul 29, 2023
929cc2e
refactor: useBooleanstate 훅 활용 (#193)
turtle601 Jul 29, 2023
5f60d9e
refactor: 불필요한 파일 삭제 (#193)
turtle601 Jul 29, 2023
9213391
chore: .vscode 파일 수정 (#193)
turtle601 Jul 29, 2023
e6ee4c4
Merge branch '193-feat-카카오-로그인-기능-구현' of https://github.com/woowacour…
turtle601 Jul 29, 2023
f1a6b2d
refactor: Oauth 타입 분리 및 적용 (#193)
turtle601 Jul 31, 2023
b704ab9
refactor: DropDown 리스트 box-shadow 적용 (#193)
turtle601 Jul 31, 2023
1b7a93d
refactor: box shadow 변수 사용 (#193)
turtle601 Jul 31, 2023
286583d
Squashed commit of the following:
turtle601 Jul 31, 2023
4746b1d
refactor: BASE_URL 값 수정 (#193)
turtle601 Jul 31, 2023
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 frontend/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",

"stylelint.enable": true,

"stylelint.config": null,
"stylelint.validate": ["css", "scss", "typescript", "typescriptreact"]
}
3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"main": "index.tsx",
"license": "MIT",
"scripts": {
"start": "webpack serve --open --mode development",
"start": "webpack serve --open --mode development --port 3000",
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

"start:prod": "webpack serve --open --mode production",
"build": "webpack --mode production",
"lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"",
Expand Down Expand Up @@ -32,6 +32,7 @@
"msw": "^1.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.14.2",
"storybook": "^7.0.25",
"styled-components": "^6.0.2",
"ts-loader": "^9.4.4",
Expand Down
1 change: 1 addition & 0 deletions frontend/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ <h2>경고!</h2>
<div>현재 사용 중인 브라우저는 스크립트를 지원하지 않거나, 해당 기능이 활성화 되어 있지 않습니다.</div>
</noscript>
<div id="root"></div>
<div id="modal"></div>
</body>
</html>
9 changes: 9 additions & 0 deletions frontend/src/@types/api.types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
export interface RestaurantListData {
content: RestaurantData[];
currentElementsCount: number;
currentPage: number;
pageSize: number;
totalElementsCount: number;
totalPage: number;
}

export interface RestaurantData {
id: number;
name: string;
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/@types/image.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { RestaurantData } from './api.types';

type RestaurantImages = RestaurantData['images'];

export type RestaurantImage = RestaurantImages[number];
1 change: 1 addition & 0 deletions frontend/src/@types/oauth.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type Oauth = 'google' | 'kakao' | 'naver';
15 changes: 13 additions & 2 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import MainPage from './pages/MainPage';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import OauthRedirectPage from '~/pages/OauthRedirectPage';
import MainPage from '~/pages/MainPage';

function App() {
return <MainPage />;
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<MainPage />} />
<Route path="/oauth/redirect/kakao" element={<OauthRedirectPage type="kakao" />} />
<Route path="/oauth/redirect/naver" element={<OauthRedirectPage type="naver" />} />
<Route path="/oauth/redirect/google" element={<OauthRedirectPage type="google" />} />
</Routes>
</BrowserRouter>
);
}

export default App;
Binary file added frontend/src/assets/all.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/icons/etc/menu.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/assets/icons/etc/user.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/src/assets/icons/love.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/assets/icons/oauth/google.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions frontend/src/assets/icons/oauth/kakao.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions frontend/src/assets/icons/oauth/naver.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions frontend/src/assets/icons/restaurantCategory/all.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 31 additions & 4 deletions frontend/src/components/@common/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,50 @@
import React from 'react';
import { styled } from 'styled-components';
import Logo from '~/assets/logo.png';
import { Modal, ModalContent } from '~/components/@common/Modal';
import InfoDropDown from '~/components/InfoDropDown';
import LoginModalContent from '~/components/LoginModalContent';
import useBooleanState from '~/hooks/useBooleanState';

const options = [
{ id: 1, value: '로그인' },
{ id: 2, value: '회원가입' },
];

function Header() {
const { value: isModalOpen, setTrue: openModal, setFalse: closeModal } = useBooleanState(false);

const handleInfoDropDown = (event: React.MouseEvent<HTMLElement>) => {
const currentOption = event.currentTarget.dataset.name;

if (currentOption === '로그인') openModal();
};

return (
<StyledHeader>
<StyledLogo alt="셀럽잇 로고" src={Logo} />
</StyledHeader>
<>
<StyledHeader>
<StyledLogo alt="셀럽잇 로고" src={Logo} />
<InfoDropDown options={options} externalOnClick={handleInfoDropDown} isOpen={isModalOpen} />
</StyledHeader>
<Modal>
<ModalContent isShow={isModalOpen} title="로그인 및 회원 가입" closeModal={closeModal}>
<LoginModalContent />
</ModalContent>
</Modal>
</>
);
}

export default Header;

const StyledHeader = styled.header`
display: flex;
justify-content: space-between;
align-items: center;

position: sticky;
top: 0;
z-index: 10;
z-index: 20;
Comment on lines -20 to +47
Copy link
Collaborator

Choose a reason for hiding this comment

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

z-index를 올려줘야만 하는 일이 있었나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

넹 ㅋㅋ Navbar의 sticky랑 Header의 sticky가 중복되서 로그인 dropdown이 가려지는 현상이 발생하더라구요
그래서 수정했습니다!!


width: 100%;
height: 80px;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Meta, StoryObj } from '@storybook/react';
import ImageCarousel from './ImageCarousel';

const meta: Meta<typeof ImageCarousel> = {
title: 'ImageCarousel',
component: ImageCarousel,
};

export default meta;

type Story = StoryObj<typeof ImageCarousel>;

export const Default: Story = {
args: {
images: [
{ id: 1, name: 'https://picsum.photos/315/300', author: '@d0dam', sns: 'youtube' },
{ id: 2, name: 'https://picsum.photos/315/300', author: '@d0dam', sns: 'youtube' },
{ id: 3, name: 'https://picsum.photos/315/300', author: '@d0dam', sns: 'youtube' },
{ id: 4, name: 'https://picsum.photos/315/300', author: '@d0dam', sns: 'youtube' },
],
},
};

export const OneImage: Story = {
args: {
images: [{ id: 1, name: 'https://picsum.photos/315/300', author: '@d0dam', sns: 'youtube' }],
},
};
155 changes: 155 additions & 0 deletions frontend/src/components/@common/ImageCarousel/ImageCarousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { useState } from 'react';
import styled, { css } from 'styled-components';
import { RestaurantImage } from '~/@types/image.type';
import LeftBracket from '~/assets/icons/left-bracket.svg';
import RightBracket from '~/assets/icons/right-bracket.svg';
import { BORDER_RADIUS } from '~/styles/common';
import WaterMarkImage from '../WaterMarkImage';

interface ImageCarouselProps {
images: RestaurantImage[];
type: 'list' | 'map';
}

function ImageCarousel({ images, type }: ImageCarouselProps) {
const [currentIndex, setCurrentIndex] = useState<number>(0);

const goToPrevious = () => {
setCurrentIndex(prevIndex => prevIndex - 1);
};

const goToNext = () => {
setCurrentIndex(prevIndex => prevIndex + 1);
};

return (
<StyledCarouselContainer type={type}>
<StyledCarouselSlide currentIndex={currentIndex}>
{images.map(({ id, name, author }) => (
<WaterMarkImage key={id} imageUrl={name} waterMark={author} />
))}
</StyledCarouselSlide>
{currentIndex !== 0 && (
<StyledLeftButton type="button" onClick={goToPrevious}>
<LeftBracket width={10} height={10} />
</StyledLeftButton>
)}
{currentIndex !== images.length - 1 && (
<StyledRightButton type="button" onClick={goToNext}>
<RightBracket width={10} height={10} />
</StyledRightButton>
)}
{images.length > 1 && (
<StyledDots currentIndex={currentIndex}>
{Array.from({ length: images.length }, () => (
<StyledDot />
))}
</StyledDots>
)}
</StyledCarouselContainer>
);
}

export default ImageCarousel;

const StyledCarouselContainer = styled.div<{ type: 'list' | 'map' }>`
position: relative;
width: 100%;
overflow: hidden;
border-radius: ${({ type }) =>
type === 'list' ? `${BORDER_RADIUS.md}` : `${BORDER_RADIUS.md} ${BORDER_RADIUS.md} 0 0`};
button {
visibility: hidden;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 50%;
width: 32px;
height: 32px;
border: none;
border-radius: 50%;
background-color: var(--white);
cursor: pointer;
opacity: 0;
transition: transform 0.15s ease-in-out, opacity 0.2s ease-in-out;
transform: translateY(-50%);
box-shadow: var(--shadow);
outline: none;
&:hover {
transform: translateY(-50%) scale(1.04);
}
}
&:hover {
button {
visibility: visible;
opacity: 0.85;
&:hover {
opacity: 1;
}
}
}
`;

const StyledLeftButton = styled.button`
left: 12px;
`;

const StyledRightButton = styled.button`
right: 12px;
`;

const StyledCarouselSlide = styled.div<{ currentIndex: number }>`
display: flex;
width: 100%;
transition: transform 0.3s ease-in-out;
transform: ${({ currentIndex }) => `translateX(-${currentIndex * 100}%)`};
flex-wrap: nowrap;
aspect-ratio: 1.05 / 1;
`;

const StyledDots = styled.div<{ currentIndex: number }>`
display: flex;
justify-content: center;
align-items: center;
gap: 0 0.5rem;
position: absolute;
bottom: 12px;
width: 100%;
${({ currentIndex }) => css`
& > span:nth-child(${currentIndex + 1}) {
opacity: 1;
transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out;
transform: scale(1.1);
}
`}
`;

const StyledDot = styled.span`
width: 6px;
height: 6px;
border-radius: 50%;
background-color: var(--white);
opacity: 0.2;
transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out;
`;
3 changes: 3 additions & 0 deletions frontend/src/components/@common/ImageCarousel/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ImageCarousel from './ImageCarousel';

export default ImageCarousel;
15 changes: 15 additions & 0 deletions frontend/src/components/@common/InfoButton/InfoButton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Meta, StoryObj } from '@storybook/react';
import InfoButton from './InfoButton';

const meta: Meta<typeof InfoButton> = {
title: 'InfoButton',
component: InfoButton,
};

export default meta;

type Story = StoryObj<typeof InfoButton>;

export const Default: Story = {
args: {},
};
Loading