Skip to content

Commit

Permalink
[FE][Feat] #357 : 온보딩 페이지
Browse files Browse the repository at this point in the history
- 온보딩 페이지에 사용할 화면 캡처본 추가
- 온보딩 페이지 구현
  • Loading branch information
happyhyep committed Dec 2, 2024
1 parent 5a219c0 commit 597b307
Show file tree
Hide file tree
Showing 20 changed files with 185 additions and 0 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { IndexRoutes } from '@/routes/IndexRoutes.tsx';
import 'App.css';
import { useEffect, useState } from 'react';
import { loadLocalData, saveLocalData } from '@/utils/common/manageLocalData.ts';
import { AppConfig } from '@/lib/constants/commonConstants.ts';
import { Onboarding } from '@/component/onBoarding/Onboarding.tsx';

export const App = () => {
const [isMobile, setIsMobile] = useState(true);
const [isFirstVisit, setIsFirstVisit] = useState(false);

useEffect(() => {
const checkIsMobile = () => {
Expand All @@ -24,6 +28,23 @@ export const App = () => {
return () => window.removeEventListener('resize', checkIsMobile);
}, []);

useEffect(() => {
const firstVisit = loadLocalData(AppConfig.KEYS.FIRST_VISIT);
if (firstVisit === null) {
saveLocalData(AppConfig.KEYS.FIRST_VISIT, 'true');
setIsFirstVisit(true);
} else if (firstVisit === 'true') {
setIsFirstVisit(true);
} else {
setIsFirstVisit(false);
}
}, []);

const handleOnboardingComplete = () => {
saveLocalData(AppConfig.KEYS.FIRST_VISIT, 'false');
setIsFirstVisit(false);
};

if (!isMobile) {
return (
<>
Expand All @@ -37,5 +58,10 @@ export const App = () => {
</>
);
}

if (isFirstVisit) {
return <Onboarding onComplete={handleOnboardingComplete} />;
}

return <IndexRoutes />;
};
87 changes: 87 additions & 0 deletions frontend/src/component/onBoarding/Onboarding.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { useState } from 'react';
import { onboardingData } from '@/lib/data/onboardingData.ts';
import { MdClear } from 'react-icons/md';

interface IOnboardingProps {
onComplete: () => void;
}

export const Onboarding = ({ onComplete }: IOnboardingProps) => {
const [currentSlide, setCurrentSlide] = useState(0);

const handleNext = () => {
if (currentSlide < onboardingData.length - 1) {
setCurrentSlide(currentSlide + 1);
} else {
onComplete();
}
};

const handlePrev = () => {
if (currentSlide > 0) {
setCurrentSlide(currentSlide - 1);
}
};

return (
<div className="relative flex h-full w-full flex-col items-center justify-center overflow-hidden bg-gray-100">
<div className="absolute right-3 top-3 z-[6000] flex items-center gap-2">
<div className="text-sm text-gray-200">튜토리얼 끝내기</div>
<button
onClick={onComplete}
className="flex h-[30px] w-[30px] items-center justify-center rounded-full bg-gray-200"
>
<MdClear size={18} color="grayscale-850" />
</button>
</div>

<div className="relative flex h-screen w-full items-center justify-center">
<img
src={`/assets/images/onboarding/slide${onboardingData[currentSlide].id}.png`}
alt={`Slide ${currentSlide + 1}`}
className="absolute top-10 mx-auto h-[75vh] object-contain"
/>
<div className="absolute inset-0 bg-black bg-opacity-30" />
</div>

<div className="absolute bottom-2 flex w-[95%] flex-col">
<div className="flex w-[100%] items-center justify-center text-white">
<img
src="/assets/images/onboarding/character.png"
alt="캐릭터"
className="max-h-16 w-[15%] object-contain"
/>
<div
className="bg-blueGray-200 m-2 flex h-20 w-[85%] items-center justify-center whitespace-pre rounded-lg bg-opacity-[0.5] text-center text-sm leading-relaxed"
style={{ padding: '1rem 1rem' }}
>
{onboardingData[currentSlide].content}
</div>
</div>

<div className="flex items-center justify-between p-4">
<button
onClick={handlePrev}
disabled={currentSlide === 0}
className="rounded bg-gray-300 px-4 py-2 text-gray-700 disabled:opacity-50"
>
이전
</button>
<div className="flex space-x-2">
{onboardingData.map((slide, index) => (
<div
key={slide.id}
className={`h-1 w-1 rounded-full ${
index === currentSlide ? 'bg-blueGray-200' : 'bg-gray-300'
}`}
/>
))}
</div>
<button onClick={handleNext} className="bg-blueGray-200 rounded px-4 py-2 text-white">
{currentSlide === onboardingData.length - 1 ? '시작' : '다음'}
</button>
</div>
</div>
</div>
);
};
1 change: 1 addition & 0 deletions frontend/src/lib/constants/commonConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export const KEYS = {
LOGIN_USER: 'LUT',
LOGIN_TOKEN: 'LUT_TK',
BROWSER_TOKEN: 'BRW_TK',
FIRST_VISIT: 'FIRST_VISIT',
};

export const AppConfig = {
Expand Down
71 changes: 71 additions & 0 deletions frontend/src/lib/data/onboardingData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
interface ISlide {
id: number;
content: string;
}

export const onboardingData: ISlide[] = [
{
id: 1,
content: '저희 ‘선따라 길따라’ 서비스가 처음이시라고요? \n제가 사용법을 알려드릴게요!',
},
{
id: 2,
content: '채널을 생성하고 싶으시다면, \n회원가입 후 로그인을 해주세요!',
},
{
id: 3,
content:
'로그인 후 본인이 생성해둔 채널들을 확인할 수 있습니다! \n공유하기를 눌러 게스트 별로 링크를 확인할 수도 있어요!',
},
{
id: 4,
content: '그럼 이제, + 버튼을 눌러 채널을 만들어볼까요?',
},
{
id: 5,
content: '채널의 제목과 게스트도 \n최대 5명까지 입력 가능합니다!',
},
{
id: 6,
content:
'하늘색 버튼을 누르면, 게스트별로 (할머니, 할아버지....)\n출발지, 도착지, 경로를 직접 그려줄 수 있어요!',
},
{
id: 7,
content: '그럼 이제, 출발지와 도착지를 먼저 설정해볼까요?',
},
{
id: 8,
content:
'1번 버튼을 누르면 검색도 가능하고, \n2번에서처럼 마커를 직접 터치로 찍을 수도 있어요!',
},
{
id: 9,
content: '그럼 이제 경로를 그려볼까요?',
},
{
id: 10,
content: '경로는 이렇게 직선 형태로 이어집니다! \n터치를 통해 직선을 이어서 경로를 그려보세요!',
},
{
id: 11,
content: '이렇게 출발지와 도착지를 이어주면 되겠죠?',
},
{
id: 12,
content: '모든 사용자에 대한 정보를 입력했다면, \n제작 완료 버튼을 눌러주세요!',
},
{
id: 13,
content:
'이제 할머니께 링크를 공유해드리면, \n그려둔 경로와 할머니의 실시간 위치를 볼 수 있습니다!',
},
{
id: 14,
content: '채널을 만든 사람은 이렇게모든 게스트들의 \n경로와 현재 위치도 한 번에 볼 수 있어요!',
},
{
id: 15,
content: '이제 우리의 캐릭터들과 함께 \n길 안내 서비스를 시작해볼까요?',
},
];

0 comments on commit 597b307

Please sign in to comment.