diff --git a/src/assets/icons/chat.svg b/src/assets/icons/chat.svg
new file mode 100644
index 0000000..4d388b6
--- /dev/null
+++ b/src/assets/icons/chat.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts
index da6ca54..76b8c61 100644
--- a/src/assets/icons/index.ts
+++ b/src/assets/icons/index.ts
@@ -5,6 +5,8 @@ export { default as MapIcon } from './map.svg';
export { default as BookmarkIcon } from './bookmark.svg';
export { default as MyPageIcon } from './mypage.svg';
export { default as AppLogoIcon } from './logo.svg';
+export { default as LogoIcon } from './logoicon.svg';
+export { default as LogoLetter } from './logoletter.svg';
export { default as KakaoIcon } from './kakaoicon.svg';
export { default as NaverIcon } from './navericon.svg';
export { default as GoogleIcon } from './googleicon.svg';
@@ -44,6 +46,7 @@ export { default as WeatherInfoIcon } from './weather-info.svg';
export { default as BackArrow } from './backArrow.svg';
export { default as CameraIcon } from './camera.svg';
export { default as RightArrowIcon } from './rightarrow.svg';
+export { default as RightArrow2Icon } from './rightarrow2.svg';
export { default as WishIcon } from './wish.svg';
export { default as KebabMenuIcon } from './kebabmenu.svg';
export { default as Map2Icon } from './map2.svg';
@@ -66,6 +69,10 @@ export { default as MyLocation } from './mylocation.svg';
export { default as EmptyLocation } from './emptylocation.svg';
export { default as EmptyWish } from './emptywish.svg';
export { default as AlertIcon } from './alert.svg';
+export { default as NoticeIcon } from './notice.svg';
+export { default as NoticeDotIcon } from './noticedot.svg';
+export { default as ChatIcon } from './chat.svg';
+export { default as MainPlaneIcon } from './mainplane.svg';
export { default as RouteIcon } from './route.svg';
export { default as WishStar } from './wishstar.svg';
diff --git a/src/assets/icons/logoicon.svg b/src/assets/icons/logoicon.svg
new file mode 100644
index 0000000..9762947
--- /dev/null
+++ b/src/assets/icons/logoicon.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/assets/icons/logoletter.svg b/src/assets/icons/logoletter.svg
new file mode 100644
index 0000000..c179b46
--- /dev/null
+++ b/src/assets/icons/logoletter.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/assets/icons/mainplane.svg b/src/assets/icons/mainplane.svg
new file mode 100644
index 0000000..2753fbe
--- /dev/null
+++ b/src/assets/icons/mainplane.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/icons/notice.svg b/src/assets/icons/notice.svg
new file mode 100644
index 0000000..72da5c5
--- /dev/null
+++ b/src/assets/icons/notice.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/assets/icons/noticedot.svg b/src/assets/icons/noticedot.svg
new file mode 100644
index 0000000..6fc35c1
--- /dev/null
+++ b/src/assets/icons/noticedot.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/assets/icons/rightarrow2.svg b/src/assets/icons/rightarrow2.svg
new file mode 100644
index 0000000..7cf19ed
--- /dev/null
+++ b/src/assets/icons/rightarrow2.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/images/mainjeju.png b/src/assets/images/mainjeju.png
new file mode 100644
index 0000000..b457432
Binary files /dev/null and b/src/assets/images/mainjeju.png differ
diff --git a/src/assets/images/maintokyo.png b/src/assets/images/maintokyo.png
new file mode 100644
index 0000000..c89e988
Binary files /dev/null and b/src/assets/images/maintokyo.png differ
diff --git a/src/components/ui/CustomBottomSheet.tsx b/src/components/ui/CustomBottomSheet.tsx
index 7e3f482..f398713 100644
--- a/src/components/ui/CustomBottomSheet.tsx
+++ b/src/components/ui/CustomBottomSheet.tsx
@@ -104,7 +104,8 @@ export const CustomBottomSheet = ({
borderTopLeftRadius: cornerRadius ?? 16,
borderTopRightRadius: cornerRadius ?? 16,
overflow: 'hidden',
-
+ zIndex: 20,
+ elevation: 20,
},
animatedStyle,
]}
diff --git a/src/components/ui/MainRecChip.tsx b/src/components/ui/MainRecChip.tsx
new file mode 100644
index 0000000..ad3570b
--- /dev/null
+++ b/src/components/ui/MainRecChip.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { View, Text } from 'react-native';
+
+export interface MainRecChipProps {
+ label: string;
+ className?: string;
+}
+
+export const MainRecChip: React.FC = ({ label, className }) => {
+ return (
+
+ {label}
+
+ );
+};
+
+export default MainRecChip;
\ No newline at end of file
diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts
index cd8da46..7468437 100644
--- a/src/components/ui/index.ts
+++ b/src/components/ui/index.ts
@@ -3,6 +3,8 @@ export { NavigationBar } from './NavigationBar';
export type { NavigationBarProps } from './NavigationBar';
export { Chip } from './Chip';
export type { ChipProps } from './Chip';
+export { MainRecChip } from './MainRecChip';
+export type { MainRecChipProps } from './MainRecChip';
export { TabNavigation } from './TabNavigation';
export type { TabNavigationProps, TabItem } from './TabNavigation';
diff --git a/src/navigation/types.ts b/src/navigation/types.ts
index 3c62ef1..5032137 100644
--- a/src/navigation/types.ts
+++ b/src/navigation/types.ts
@@ -11,7 +11,7 @@ export type SearchStackParamList = {
export type RootTabParamList = {
Home: undefined;
Search: NavigatorScreenParams | undefined;
- Map: undefined;
+ MyTrip: undefined;
Bookmark: undefined;
MyPage: undefined;
};
diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx
index 8ec6e3e..9b475d5 100644
--- a/src/screens/HomeScreen.tsx
+++ b/src/screens/HomeScreen.tsx
@@ -1,16 +1,18 @@
-import { useNavigation } from '@react-navigation/native';
+import { useNavigation } from '@react-navigation/native';
import type { CompositeNavigationProp } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import React, { useCallback, useState } from 'react';
-import { View, Text, TouchableOpacity, Pressable, TextInput } from 'react-native';
+import { View, Text, TouchableOpacity, Pressable, TextInput, ScrollView, Image } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
+import { Shadow } from 'react-native-shadow-2';
import type { HomeStackParamList } from '@/navigation';
import type { RootStackParamList } from '@/navigation';
import Animated, { useSharedValue, withTiming, useAnimatedStyle, interpolate } from 'react-native-reanimated';
import CustomBottomSheet from '@/components/ui/CustomBottomSheet';
-import { RobotIcon, SendIcon, X } from '@/assets/icons';
+import { MainRecChip } from '@/components/ui';
+import { RobotIcon, SendIcon, X, NoticeIcon, LogoIcon, LogoLetter, ChatIcon } from '@/assets/icons';
import { COLORS } from '@/constants/colors';
-import { ChatCaseContent } from '@/screens/home/components';
+import { ChatCaseContent, MainTripCard } from '@/screens/home/components';
import {
CHAT_HEADER_HEIGHT,
CHAT_INPUT_BOTTOM_SPACING,
@@ -19,24 +21,58 @@ import {
CHAT_SHEET_HEIGHT,
} from '@/screens/home/constants';
-// ============ Types ============
type NavigationProp = CompositeNavigationProp<
NativeStackNavigationProp,
NativeStackNavigationProp
>;
+const RECOMMENDED_DESTINATIONS = [
+ {
+ id: '1',
+ title: '제주도',
+ country: '한국',
+ description: '아름다운 자연과 독특한 문화가 있는 한국의 보석 같은 섬',
+ imageUrl: require('@/assets/images/mainjeju.png'),
+ tags: ['자연', '맛집', '자연'],
+ },
+ {
+ id: '2',
+ title: '제주도',
+ country: '한국',
+ description: '아름다운 자연과 독특한 문화가 있는 한국의 보석 같은 섬',
+ imageUrl: require('@/assets/images/mainjeju.png'),
+ tags: ['자연', '맛집'],
+ },
+];
+
const HomeScreen: React.FC = () => {
const navigation = useNavigation();
const translateY = useSharedValue(CHAT_SHEET_HEIGHT);
const SNAP_MIN = CHAT_SHEET_HEIGHT;
const [isChatOpen, setIsChatOpen] = useState(false);
const [chatCaseOrder, setChatCaseOrder] = useState(-1);
+ const [hasPlannedTrip, setHasPlannedTrip] = useState(false);
+ const [isInTripScheduleView, setIsInTripScheduleView] = useState(false);
const currentCaseIndex = chatCaseOrder < 0 ? 0 : chatCaseOrder;
+ const hasNotification = true;
const handleNavigateToDetail = useCallback(() => {
navigation.navigate('Alert');
}, [navigation]);
+ const handleNavigateToAddTrip = useCallback(() => {
+ setHasPlannedTrip(true);
+ setIsInTripScheduleView(false);
+ }, []);
+
+ const handleOpenTripSchedule = useCallback(() => {
+ setIsInTripScheduleView(true);
+ }, []);
+
+ const handleNavigateToMyTrip = useCallback(() => {
+ navigation.navigate('MainTabs', { screen: 'MyTrip' });
+ }, [navigation]);
+
const handleOpenChat = useCallback(() => {
setChatCaseOrder((prev) => (prev + 1) % 3);
translateY.value = withTiming(0, { duration: 350 });
@@ -47,22 +83,12 @@ const HomeScreen: React.FC = () => {
}, [translateY, SNAP_MIN]);
const backdropStyle = useAnimatedStyle(() => {
- const opacity = interpolate(translateY.value, [0, SNAP_MIN], [1, 0]);
+ const opacity = interpolate(translateY.value, [0, SNAP_MIN], [0.3, 0]);
return { opacity };
}, [translateY, SNAP_MIN]);
return (
-
- 홈
-
- 알림 리스트
-
-
-
- {/* 배경 오버레이 */}
{
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.3)',
+ zIndex: 10,
+ elevation: 10,
},
backdropStyle,
]}
@@ -80,16 +108,87 @@ const HomeScreen: React.FC = () => {
- {/* 채팅 플로팅 버튼 */}
- {/* TODO: 재원님 여기 만드시면 돼요 */}
-
- 채팅
+
+
+
+
+
+
+
+
+
+
+
+ {hasNotification && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+ 추천 여행지
+ 지금 떠나기 좋은 여행지를 모았어요
+
+
+
+ {RECOMMENDED_DESTINATIONS.map((item) => (
+
+
+
+
+
+ {item.title}
+ {item.country}
+
+
+
+
+
+ {item.description}
+
+
+ {item.tags.map((tag, index) => (
+
+ ))}
+
+
+
+
+ ))}
+
+
+
+
+
+
- {/* 채팅 바텀시트 */}
{
onStateChange={setIsChatOpen}
showIndicator={false}
>
- {/* 바텀시트 헤더 */}
{
- {/* 헤더 아래 콘텐츠 영역 */}
- {/* 케이스별 콘텐츠 */}
- {/* 하단 입력창 */}
-
+
{
);
};
+
export default HomeScreen;
-export { HomeScreen };
\ No newline at end of file
+export { HomeScreen };
diff --git a/src/screens/home/components/MainTripCard.tsx b/src/screens/home/components/MainTripCard.tsx
new file mode 100644
index 0000000..61fb0d4
--- /dev/null
+++ b/src/screens/home/components/MainTripCard.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { MainTripCardEmpty } from './MainTripCardEmpty';
+import { MainTripCardPlanned } from './MainTripCardPlanned';
+import { MainTripCardInProgress } from './MainTripCardInProgress';
+
+interface MainTripCardProps {
+ hasPlannedTrip: boolean;
+ isInTripScheduleView: boolean;
+ onAddTrip: () => void;
+ onOpenTripSchedule: () => void;
+ onViewAllSchedule: () => void;
+}
+
+const MainTripCard: React.FC = ({
+ hasPlannedTrip,
+ isInTripScheduleView,
+ onAddTrip,
+ onOpenTripSchedule,
+ onViewAllSchedule,
+}) => {
+ if (!hasPlannedTrip) {
+ return ;
+ }
+
+ if (!isInTripScheduleView) {
+ return ;
+ }
+
+ return ;
+};
+
+export default MainTripCard;
+export { MainTripCard };
diff --git a/src/screens/home/components/MainTripCardEmpty.tsx b/src/screens/home/components/MainTripCardEmpty.tsx
new file mode 100644
index 0000000..f6d967c
--- /dev/null
+++ b/src/screens/home/components/MainTripCardEmpty.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import { View, Text, TouchableOpacity } from 'react-native';
+import { ContentContainer } from '@/components/ui';
+import { AirplaneIcon, PlusIcon } from '@/assets/icons';
+
+interface MainTripCardEmptyProps {
+ onAddTrip: () => void;
+}
+
+const MainTripCardEmpty: React.FC = ({ onAddTrip }) => {
+ return (
+
+
+
+
+ 여행을 계획해보세요
+
+ 새로운 여행지를 추가하고{'\n'}일정을 관리해보세요
+
+
+
+ 여행 추가하기
+
+
+ );
+};
+
+export default MainTripCardEmpty;
+export { MainTripCardEmpty };
diff --git a/src/screens/home/components/MainTripCardInProgress.tsx b/src/screens/home/components/MainTripCardInProgress.tsx
new file mode 100644
index 0000000..81ef19a
--- /dev/null
+++ b/src/screens/home/components/MainTripCardInProgress.tsx
@@ -0,0 +1,81 @@
+import React from 'react';
+import { View, Text, TouchableOpacity, Image } from 'react-native';
+import { Shadow } from 'react-native-shadow-2';
+import { TimeB, MarkerGrayIcon, RightArrow2Icon } from '@/assets/icons';
+
+interface MainTripCardInProgressProps {
+ onViewAllSchedule: () => void;
+}
+
+const MainTripCardInProgress: React.FC = ({ onViewAllSchedule }) => {
+ return (
+
+
+
+
+
+
+
+ 여행중
+
+ 도쿄 여행
+
+
+
+
+
+
+ 다음 일정
+
+
+
+
+
+ 09:00
+ 11:00
+
+
+ 아사쿠사 센소지
+
+
+ 아사쿠사, 도쿄
+
+ 도쿄에서 가장 오래된 사원 방문
+
+
+
+
+
+
+ 12:00
+
+ 츠키지 시장 점심
+
+
+ 14:30
+
+ 시부야 스크램블 교차로
+
+
+
+
+
+ 전체 일정 보기
+
+
+
+
+
+
+ );
+};
+
+export default MainTripCardInProgress;
+export { MainTripCardInProgress };
diff --git a/src/screens/home/components/MainTripCardPlanned.tsx b/src/screens/home/components/MainTripCardPlanned.tsx
new file mode 100644
index 0000000..847f0a1
--- /dev/null
+++ b/src/screens/home/components/MainTripCardPlanned.tsx
@@ -0,0 +1,63 @@
+import React from 'react';
+import { View, Text, TouchableOpacity, Image } from 'react-native';
+import { Shadow } from 'react-native-shadow-2';
+import { DateIcon, MainPlaneIcon, VectorGrayIcon } from '@/assets/icons';
+
+interface MainTripCardPlannedProps {
+ onOpenTripSchedule: () => void;
+}
+
+const MainTripCardPlanned: React.FC = ({ onOpenTripSchedule }) => {
+ return (
+
+
+
+
+
+
+
+ 다가오는 여행
+
+ 도쿄 여행
+
+
+ 3/23 - 3/28
+
+
+
+
+
+
+ 5개의 일정이 계획되었어요
+
+ 일정 보기
+
+
+
+
+
+
+ 여행까지
+ 14일 남음
+
+
+
+
+
+
+
+
+ );
+};
+
+export default MainTripCardPlanned;
+export { MainTripCardPlanned };
diff --git a/src/screens/home/components/index.ts b/src/screens/home/components/index.ts
index 516597b..b780f00 100644
--- a/src/screens/home/components/index.ts
+++ b/src/screens/home/components/index.ts
@@ -1 +1,5 @@
-export { ChatCaseContent } from './ChatCaseContent';
\ No newline at end of file
+export { ChatCaseContent } from './ChatCaseContent';
+export { MainTripCard } from './MainTripCard';
+export { MainTripCardEmpty } from './MainTripCardEmpty';
+export { MainTripCardPlanned } from './MainTripCardPlanned';
+export { MainTripCardInProgress } from './MainTripCardInProgress';
diff --git a/tailwind.config.js b/tailwind.config.js
index cbd3e49..0f05988 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -27,7 +27,7 @@ const colors = {
errmassage: '#FF4444',
kakaoYellow: '#FEE500',
naverGreen: '#03A94D',
-
+ greenstate: '#27C840',
errormessage: '#FF4444',
};
@@ -58,6 +58,7 @@ module.exports = {
boxShadow: {
card: '0 0 3px rgba(0,0,0,0.15)',
logincard: '0 0 5px rgba(0,0,0,0.25)',
+ recommended: '0 0 10px rgba(0,0,0,0.25)',
},
},
},