Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion src/app/navigation/RootNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { useSessionStore } from '@shared/store/useSessionStore';
import LoginScreen from '@screens/auth/Login/LoginScreen';
import SignupScreen from '@screens/auth/Signup/SignupScreen';
import TermsAgreementScreen from '@screens/auth/terms/TermsAgreementScreen';
import { LinkAccountScreen } from '@screens/auth/LinkAccount/LinkAccountScreen';
import { GuardianStackNavigator } from './GuardianStackNavigator';
import { SeniorHomeScreen } from '@screens/senior/Home/SeniorHomeScreen';
Expand Down Expand Up @@ -31,7 +32,8 @@ export function RootNavigator() {
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Signup" component={SignupScreen} />
<Stack.Screen name="TermsAgreement" component={TermsAgreementScreen} />
<Stack.Screen name="LinkAccount" component={LinkAccountScreen} />
</Stack.Navigator>
);
}
}
86 changes: 86 additions & 0 deletions src/screens/auth/terms/CaringLogo.tsx

Large diffs are not rendered by default.

112 changes: 112 additions & 0 deletions src/screens/auth/terms/TermsAgreement.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { StyleSheet } from 'react-native';

export const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#FFFFFF',
paddingHorizontal: 24,
paddingTop: 60,
},
headerContainer: {
flexDirection: 'row',
alignItems: 'center',
width: '100%',
marginBottom: 24,
position: 'relative',
},
titleContainer: {
position: 'absolute',
left: 0,
right: 0,
alignItems: 'center',
zIndex: 1,
},
/* 👇 로고를 감싸기 위해 추가된 스타일 */
logoContainer: {
alignItems: 'center',
marginBottom: 24,
},
title: {
fontSize: 20,
fontWeight: 'bold',
color: '#000000',
},
subtitle: {
fontSize: 14,
color: '#666666',
marginBottom: 24,
},
allAgreeBox: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#F5F6F8',
padding: 16,
borderRadius: 12,
marginBottom: 24,
// 전체 박스 테두리 제거됨
},
allAgreeText: {
fontSize: 16,
fontWeight: 'bold',
color: '#000000',
marginLeft: 12,
},
sectionTitle: {
fontSize: 14,
fontWeight: '600',
color: '#888888',
marginBottom: 12,
marginTop: 12,
},
termItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 12,
// 개별 박스 테두리 제거됨
},
termText: {
fontSize: 15,
color: '#333333',
marginLeft: 12,
},
required: {
color: '#FF7E00',
},
/* 피그마와 동일한 비율/크기의 체크박스 스타일 */
checkbox: {
width: 22,
height: 22,
borderRadius: 4,
borderWidth: 1,
borderColor: '#DDDDDD',
backgroundColor: '#FFFFFF',
justifyContent: 'center',
alignItems: 'center',
},
checkboxChecked: {
borderColor: '#FF7E00', // 체크되었을 때만 테두리가 주황색
backgroundColor: '#FFF9F5',
},
checkmark: {
color: '#FF7E00',
fontSize: 12,
fontWeight: 'bold',
},
nextButton: {
backgroundColor: '#FF7E00',
height: 56,
borderRadius: 12,
justifyContent: 'center',
alignItems: 'center',
marginTop: 20,
marginBottom: 30,
},
nextButtonDisabled: {
backgroundColor: '#DDDDDD',
},
nextButtonText: {
fontSize: 16,
fontWeight: 'bold',
color: '#FFFFFF',
},
});
48 changes: 48 additions & 0 deletions src/screens/auth/terms/TermsAgreementScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';
import { View, Text, TouchableOpacity, ScrollView } from 'react-native';
import { useTermsAgreement, TERM_LIST } from './useTermsAgreement';
import { styles } from './TermsAgreement.styles';
import { CaringLogo } from './CaringLogo';

export default function TermsAgreementScreen({ navigation }: any) {
const { checkedItems, isAllChecked, isRequiredChecked, handleCheckItem, handleCheckAll } = useTermsAgreement();

return (
<ScrollView contentContainerStyle={styles.container}>
<View style={styles.logoContainer}>
<CaringLogo />
</View>

<Text style={styles.title}>서비스 이용을 위해{'\n'}약관에 동의해 주세요.</Text>

{/* 전체 동의 버튼 (스타일 이름을 allAgreeBox로 수정) */}
<TouchableOpacity style={styles.allAgreeBox} onPress={handleCheckAll}>
<Text style={styles.allAgreeText}>모든 약관에 동의합니다.</Text>
</TouchableOpacity>

{/* 개별 약관 목록 */}
{TERM_LIST.map(term => (
<TouchableOpacity
key={term.id}
style={styles.termItem}
onPress={() => handleCheckItem(term.id)}
>
<Text style={styles.termText}>
{term.required ? '[필수]' : '[선택]'} {term.title} {checkedItems[term.id] ? '✓' : ''}
</Text>
</TouchableOpacity>
))}

{/* 다음 버튼 */}
<TouchableOpacity
style={[styles.nextButton, { backgroundColor: isRequiredChecked ? '#FF7E00' : '#DDDDDD' }]}
disabled={!isRequiredChecked}
onPress={() => {
navigation.navigate('Signup');
}}
>
<Text style={styles.nextButtonText}>다음</Text>
</TouchableOpacity>
</ScrollView>
);
}
5 changes: 5 additions & 0 deletions src/screens/auth/terms/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface TermItem {
id: string;
title: string;
required: boolean;
}
45 changes: 45 additions & 0 deletions src/screens/auth/terms/useTermsAgreement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useState } from 'react';
import { TermItem } from './types';

export const TERM_LIST: TermItem[] = [
{ id: 'service', title: '서비스 이용약관 동의', required: true },
{ id: 'privacy', title: '개인정보 수집 및 이용 동의', required: true },
{ id: 'location', title: '위치기반 서비스 이용약관 동의', required: true },
{ id: 'thirdParty', title: '개인정보 제3자 제공 동의', required: true },
{ id: 'marketing', title: '마케팅 정보 수신 동의', required: false },
{ id: 'optionalPrivacy', title: '개인정보 선택 수집 동의', required: false },
{ id: 'voice', title: '음성 데이터 수집 및 이용 동의', required: false },
];

export const useTermsAgreement = () => {
const [checkedItems, setCheckedItems] = useState<{ [key: string]: boolean }>({});

const isAllChecked = TERM_LIST.every((item) => checkedItems[item.id]);
const isRequiredChecked = TERM_LIST
.filter((item) => item.required)
.every((item) => checkedItems[item.id]);

const handleCheckItem = (id: string) => {
setCheckedItems((prev) => ({
...prev,
[id]: !prev[id],
}));
};

const handleCheckAll = () => {
const nextValue = !isAllChecked;
const newCheckedItems: { [key: string]: boolean } = {};
TERM_LIST.forEach((item) => {
newCheckedItems[item.id] = nextValue;
});
setCheckedItems(newCheckedItems);
};

return {
checkedItems,
isAllChecked,
isRequiredChecked,
handleCheckItem,
handleCheckAll,
};
};