From 0d3607564b7af446764116c2132a85cc92f746e5 Mon Sep 17 00:00:00 2001 From: kiho-0 Date: Tue, 14 Jul 2026 12:40:27 +0900 Subject: [PATCH 1/9] =?UTF-8?q?feat:=20=EC=BA=98=EB=A6=B0=EB=8D=94=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20=EC=9D=BC=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CalendarSchedule.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 CalendarSchedule.tsx diff --git a/CalendarSchedule.tsx b/CalendarSchedule.tsx new file mode 100644 index 0000000..e69de29 From 50206007535953c2cc93d1997016008340e3eca5 Mon Sep 17 00:00:00 2001 From: kiho-0 Date: Tue, 14 Jul 2026 13:32:06 +0900 Subject: [PATCH 2/9] =?UTF-8?q?=EC=BA=98=EB=A6=B0=EB=8D=94=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=20=EC=9D=BC=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 일정 등록을 위한 UI 및 상태 관리 로직을 구현했습니다. --- CalendarSchedule.tsx | 219 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) diff --git a/CalendarSchedule.tsx b/CalendarSchedule.tsx index e69de29..489a5dd 100644 --- a/CalendarSchedule.tsx +++ b/CalendarSchedule.tsx @@ -0,0 +1,219 @@ +import React, { useState } from 'react'; +import { + StyleSheet, + Text, + View, + ScrollView, + TextInput, + TouchableOpacity, +} from 'react-native'; + +// @ts-ignore +import { Calendar } from 'react-native-calendars'; + +// @ts-ignore +import { WheelPicker } from 'react-native-wheel-pick'; + +export default function ScheduleModal() { + + const [eventName, setEventName] = useState(''); + const [selectedLocation, setSelectedLocation] = useState('장소 1'); + const [voiceOption, setVoiceOption] = useState('tts'); // 'tts' 또는 'recording' + const [selectedDate, setSelectedDate] = useState('2026-04-21'); + + // --- 일정 시간 선택 상태 --- + const [eventHour, setEventHour] = useState('06'); + const [eventMinute, setEventMinute] = useState('28'); + const [eventSecond, setEventSecond] = useState('55'); + const [eventAmPm, setEventAmPm] = useState('PM'); + + // --- 음성 알림 시간 선택 상태 --- + const [alarmHour, setAlarmHour] = useState('06'); + const [alarmMinute, setAlarmMinute] = useState('28'); + const [alarmSecond, setAlarmSecond] = useState('55'); + const [alarmAmPm, setAlarmAmPm] = useState('PM'); + + // 스크롤 데이터 배열 생성 + const hours = Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, '0')); + const minutesSeconds = Array.from({ length: 60 }, (_, i) => String(i).padStart(2, '0')); + const amPmOptions = ['PM', 'AM']; + + return ( + + + {/* 1. 상단 헤더 타이틀 */} + + + + 이나연님 일정 등록 + + + + + + + {/* 2. 일정 이름 입력 필드 */} + 일정 이름 + + + {/* 3. 장소 드롭다운 스타일 */} + 장소 + + 장소를 선택하세요 + + + + {/* 장소 예시 리스트 박스 */} + + {['장소 1', '장소 2', '장소 3'].map((loc) => ( + setSelectedLocation(loc)} style={styles.locationItem}> + {loc} + + ))} + + + + + {/* 4. 달력 컴포넌트 */} + setSelectedDate(day.dateString)} + markedDates={{ + [selectedDate]: { selected: true, selectedColor: '#007AFF' }, + '2026-04-30': { selected: true, selectedColor: '#007AFF' } + }} + theme={{ + todayTextColor: '#FF7A00', + arrowColor: '#999999', + selectedDayTextColor: '#ffffff', + }} + /> + + + + {/* 5. 일정 시간 */} + 일정 시간 + + 일정 시간을 선택하세요 + 🕒 + + + + setEventHour(item)} /> + : + setEventMinute(item)} /> + : + setEventSecond(item)} /> + + setEventAmPm(item)} /> + + + + {/* 6. 음성 알림 시간 */} + 음성 알림 시간 + + 알림을 전달 할 시간을 선택하세요 + 🕒 + + + + setAlarmHour(item)} /> + : + setAlarmMinute(item)} /> + : + setAlarmSecond(item)} /> + + setAlarmAmPm(item)} /> + + + + {/* 7. 음성 알림 설정 컴포넌트 */} + + 음성 알림 설정 + + setVoiceOption('tts')}> + + 기본 알림음 (TTS) + + + setVoiceOption('recording')}> + + 보호자 음성 녹음 + + + {/* 녹음 제어 서브 버튼 배치 */} + + 🎙 녹음 + ▶ 재생 + ✕ 삭제 + + + + {/* 8. 최종 저장 버튼 */} + + 저장하기 + + + + ); +} + +const styles = StyleSheet.create({ + container: { flex: 1, backgroundColor: '#FFFFFF', padding: 20 }, + headerRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginTop: 10, marginBottom: 25 }, + titleGroup: { flexDirection: 'row', alignItems: 'center' }, + iconCalendar: { width: 16, height: 16, backgroundColor: '#FF7A00', borderRadius: 3, marginRight: 8 }, + textTitle: { color: '#333333', fontSize: 19, fontWeight: 'bold' }, + btnClose: { padding: 5 }, + textClose: { color: '#999999', fontSize: 20 }, + label: { color: '#333333', fontSize: 15, fontWeight: 'bold', marginBottom: 8, marginTop: 12 }, + input: { height: 48, borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, fontSize: 14, color: '#333', marginBottom: 20 }, + dropdownFake: { height: 48, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, marginBottom: 4 }, + locationList: { backgroundColor: '#F9F9F9', padding: 6, borderRadius: 6, marginBottom: 20 }, + locationItem: { padding: 8 }, + divider: { height: 1, backgroundColor: '#EAEAEA', marginVertical: 20 }, + infoInputBox: { height: 48, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, marginBottom: 12 }, + infoInputText: { color: '#999999', fontSize: 14 }, + + pickerCard: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: '#FFFFFF', + borderWidth: 1, + borderColor: '#F0F0F0', + borderRadius: 16, + paddingHorizontal: 15, + height: 150, + marginBottom: 25, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.04, + shadowRadius: 5, + elevation: 2, + }, + wheel: { flex: 1, height: 130, backgroundColor: 'transparent' }, + colon: { fontSize: 18, fontWeight: 'bold', color: '#333', textAlign: 'center', width: 10 }, + + voiceSettingContainer: { borderWidth: 1, borderColor: '#F0F0F0', borderRadius: 16, padding: 16, marginBottom: 25 }, + radioOption: { flexDirection: 'row', alignItems: 'center', marginTop: 12 }, + radioCircle: { width: 18, height: 18, borderRadius: 9, borderWidth: 2, borderColor: '#EAEAEA', marginRight: 10 }, + radioChecked: { borderColor: '#FF7A00', backgroundColor: '#FF7A00' }, + radioText: { fontSize: 14, color: '#333', fontWeight: '500' }, + + voiceButtonContainer: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 15 }, + voiceBtn: { flex: 1, height: 40, borderWidth: 1, borderColor: '#FF7A00', borderRadius: 8, backgroundColor: '#FFFFFF', alignItems: 'center', justifyContent: 'center', marginHorizontal: 4 }, + voiceBtnText: { color: '#FF7A00', fontSize: 13, fontWeight: 'bold' }, + + btnSave: { height: 54, backgroundColor: '#FF7A00', borderRadius: 10, alignItems: 'center', justifyContent: 'center', marginTop: 5 }, + btnSaveText: { color: '#FFFFFF', fontSize: 16, fontWeight: 'bold' }, +}); From 444395b0b290cf9a0c15f7380abf2c288340888c Mon Sep 17 00:00:00 2001 From: kiho-0 Date: Tue, 14 Jul 2026 13:34:33 +0900 Subject: [PATCH 3/9] Update CalendarSchedule.tsx --- CalendarSchedule.tsx | 109 +++++++++++++------------------------------ 1 file changed, 32 insertions(+), 77 deletions(-) diff --git a/CalendarSchedule.tsx b/CalendarSchedule.tsx index 489a5dd..5d03c65 100644 --- a/CalendarSchedule.tsx +++ b/CalendarSchedule.tsx @@ -10,30 +10,26 @@ import { // @ts-ignore import { Calendar } from 'react-native-calendars'; - // @ts-ignore import { WheelPicker } from 'react-native-wheel-pick'; export default function ScheduleModal() { - + const [userName, setUserName] = useState(''); // 사용자 이름 상태 추가 const [eventName, setEventName] = useState(''); - const [selectedLocation, setSelectedLocation] = useState('장소 1'); - const [voiceOption, setVoiceOption] = useState('tts'); // 'tts' 또는 'recording' + const [location, setLocation] = useState(''); + const [voiceOption, setVoiceOption] = useState('tts'); const [selectedDate, setSelectedDate] = useState('2026-04-21'); - // --- 일정 시간 선택 상태 --- const [eventHour, setEventHour] = useState('06'); const [eventMinute, setEventMinute] = useState('28'); const [eventSecond, setEventSecond] = useState('55'); const [eventAmPm, setEventAmPm] = useState('PM'); - // --- 음성 알림 시간 선택 상태 --- const [alarmHour, setAlarmHour] = useState('06'); const [alarmMinute, setAlarmMinute] = useState('28'); const [alarmSecond, setAlarmSecond] = useState('55'); const [alarmAmPm, setAlarmAmPm] = useState('PM'); - // 스크롤 데이터 배열 생성 const hours = Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, '0')); const minutesSeconds = Array.from({ length: 60 }, (_, i) => String(i).padStart(2, '0')); const amPmOptions = ['PM', 'AM']; @@ -41,18 +37,25 @@ export default function ScheduleModal() { return ( - {/* 1. 상단 헤더 타이틀 */} + {/* 1. 헤더 (이름 입력 가능) */} - 이나연님 일정 등록 + + 님 일정 등록 - {/* 2. 일정 이름 입력 필드 */} + {/* 2. 일정 이름 */} 일정 이름 - {/* 3. 장소 드롭다운 스타일 */} + {/* 3. 장소 입력 및 선택 */} 장소 - - 장소를 선택하세요 - - + - {/* 장소 예시 리스트 박스 */} {['장소 1', '장소 2', '장소 3'].map((loc) => ( - setSelectedLocation(loc)} style={styles.locationItem}> - {loc} + setLocation(loc)} style={styles.locationItem}> + {loc} ))} - {/* 4. 달력 컴포넌트 */} + {/* 4. 달력 */} setSelectedDate(day.dateString)} markedDates={{ - [selectedDate]: { selected: true, selectedColor: '#007AFF' }, - '2026-04-30': { selected: true, selectedColor: '#007AFF' } - }} - theme={{ - todayTextColor: '#FF7A00', - arrowColor: '#999999', - selectedDayTextColor: '#ffffff', + [selectedDate]: { selected: true, selectedColor: '#007AFF' } }} + theme={{ todayTextColor: '#FF7A00', arrowColor: '#999999', selectedDayTextColor: '#ffffff' }} /> {/* 5. 일정 시간 */} 일정 시간 - - 일정 시간을 선택하세요 - 🕒 - - setEventHour(item)} /> : setEventMinute(item)} /> : setEventSecond(item)} /> - setEventAmPm(item)} /> - {/* 6. 음성 알림 시간 */} 음성 알림 시간 - - 알림을 전달 할 시간을 선택하세요 - 🕒 - - setAlarmHour(item)} /> : setAlarmMinute(item)} /> : setAlarmSecond(item)} /> - setAlarmAmPm(item)} /> - - {/* 7. 음성 알림 설정 컴포넌트 */} + {/* 7. 음성 설정 */} 음성 알림 설정 - setVoiceOption('tts')}> 기본 알림음 (TTS) - setVoiceOption('recording')}> 보호자 음성 녹음 - - {/* 녹음 제어 서브 버튼 배치 */} 🎙 녹음 ▶ 재생 @@ -158,11 +137,9 @@ export default function ScheduleModal() { - {/* 8. 최종 저장 버튼 */} 저장하기 - ); } @@ -172,48 +149,26 @@ const styles = StyleSheet.create({ headerRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginTop: 10, marginBottom: 25 }, titleGroup: { flexDirection: 'row', alignItems: 'center' }, iconCalendar: { width: 16, height: 16, backgroundColor: '#FF7A00', borderRadius: 3, marginRight: 8 }, + headerInput: { fontSize: 19, fontWeight: 'bold', color: '#333', minWidth: 60 }, // 이름 입력용 스타일 textTitle: { color: '#333333', fontSize: 19, fontWeight: 'bold' }, btnClose: { padding: 5 }, textClose: { color: '#999999', fontSize: 20 }, label: { color: '#333333', fontSize: 15, fontWeight: 'bold', marginBottom: 8, marginTop: 12 }, - input: { height: 48, borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, fontSize: 14, color: '#333', marginBottom: 20 }, - dropdownFake: { height: 48, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, marginBottom: 4 }, - locationList: { backgroundColor: '#F9F9F9', padding: 6, borderRadius: 6, marginBottom: 20 }, + input: { height: 48, borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, fontSize: 14, color: '#333', marginBottom: 10 }, + locationList: { backgroundColor: '#F9F9F9', padding: 6, borderRadius: 6, marginBottom: 20, flexDirection: 'row' }, locationItem: { padding: 8 }, divider: { height: 1, backgroundColor: '#EAEAEA', marginVertical: 20 }, - infoInputBox: { height: 48, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, marginBottom: 12 }, - infoInputText: { color: '#999999', fontSize: 14 }, - - pickerCard: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - backgroundColor: '#FFFFFF', - borderWidth: 1, - borderColor: '#F0F0F0', - borderRadius: 16, - paddingHorizontal: 15, - height: 150, - marginBottom: 25, - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.04, - shadowRadius: 5, - elevation: 2, - }, + pickerCard: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: '#FFFFFF', borderWidth: 1, borderColor: '#F0F0F0', borderRadius: 16, paddingHorizontal: 15, height: 150, marginBottom: 25 }, wheel: { flex: 1, height: 130, backgroundColor: 'transparent' }, colon: { fontSize: 18, fontWeight: 'bold', color: '#333', textAlign: 'center', width: 10 }, - voiceSettingContainer: { borderWidth: 1, borderColor: '#F0F0F0', borderRadius: 16, padding: 16, marginBottom: 25 }, radioOption: { flexDirection: 'row', alignItems: 'center', marginTop: 12 }, radioCircle: { width: 18, height: 18, borderRadius: 9, borderWidth: 2, borderColor: '#EAEAEA', marginRight: 10 }, radioChecked: { borderColor: '#FF7A00', backgroundColor: '#FF7A00' }, radioText: { fontSize: 14, color: '#333', fontWeight: '500' }, - voiceButtonContainer: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 15 }, - voiceBtn: { flex: 1, height: 40, borderWidth: 1, borderColor: '#FF7A00', borderRadius: 8, backgroundColor: '#FFFFFF', alignItems: 'center', justifyContent: 'center', marginHorizontal: 4 }, + voiceBtn: { flex: 1, height: 40, borderWidth: 1, borderColor: '#FF7A00', borderRadius: 8, alignItems: 'center', justifyContent: 'center', marginHorizontal: 4 }, voiceBtnText: { color: '#FF7A00', fontSize: 13, fontWeight: 'bold' }, - btnSave: { height: 54, backgroundColor: '#FF7A00', borderRadius: 10, alignItems: 'center', justifyContent: 'center', marginTop: 5 }, btnSaveText: { color: '#FFFFFF', fontSize: 16, fontWeight: 'bold' }, }); From 42be0c5fa9da8080133a551e51074f41d130cb58 Mon Sep 17 00:00:00 2001 From: kiho-0 Date: Tue, 14 Jul 2026 13:48:19 +0900 Subject: [PATCH 4/9] =?UTF-8?q?=EC=BA=98=EB=A6=B0=EB=8D=94=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=20=EC=9D=BC=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 일정 등록을 위해 UI 컴포넌트와 관련 상태 관리 로직 From 547c505b7612205ea52a12912468588d0917c4e7 Mon Sep 17 00:00:00 2001 From: kiho-0 Date: Tue, 14 Jul 2026 14:14:01 +0900 Subject: [PATCH 5/9] =?UTF-8?q?feat:=20=EC=BA=98=EB=A6=B0=EB=8D=94=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20=EC=9D=BC=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CalendarSchedule.tsx | 393 ++++++++++-------- 1 file changed, 219 insertions(+), 174 deletions(-) rename CalendarSchedule.tsx => src/CalendarSchedule.tsx (70%) diff --git a/CalendarSchedule.tsx b/src/CalendarSchedule.tsx similarity index 70% rename from CalendarSchedule.tsx rename to src/CalendarSchedule.tsx index 5d03c65..3c02950 100644 --- a/CalendarSchedule.tsx +++ b/src/CalendarSchedule.tsx @@ -1,174 +1,219 @@ -import React, { useState } from 'react'; -import { - StyleSheet, - Text, - View, - ScrollView, - TextInput, - TouchableOpacity, -} from 'react-native'; - -// @ts-ignore -import { Calendar } from 'react-native-calendars'; -// @ts-ignore -import { WheelPicker } from 'react-native-wheel-pick'; - -export default function ScheduleModal() { - const [userName, setUserName] = useState(''); // 사용자 이름 상태 추가 - const [eventName, setEventName] = useState(''); - const [location, setLocation] = useState(''); - const [voiceOption, setVoiceOption] = useState('tts'); - const [selectedDate, setSelectedDate] = useState('2026-04-21'); - - const [eventHour, setEventHour] = useState('06'); - const [eventMinute, setEventMinute] = useState('28'); - const [eventSecond, setEventSecond] = useState('55'); - const [eventAmPm, setEventAmPm] = useState('PM'); - - const [alarmHour, setAlarmHour] = useState('06'); - const [alarmMinute, setAlarmMinute] = useState('28'); - const [alarmSecond, setAlarmSecond] = useState('55'); - const [alarmAmPm, setAlarmAmPm] = useState('PM'); - - const hours = Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, '0')); - const minutesSeconds = Array.from({ length: 60 }, (_, i) => String(i).padStart(2, '0')); - const amPmOptions = ['PM', 'AM']; - - return ( - - - {/* 1. 헤더 (이름 입력 가능) */} - - - - - 님 일정 등록 - - - - - - - {/* 2. 일정 이름 */} - 일정 이름 - - - {/* 3. 장소 입력 및 선택 */} - 장소 - - - - {['장소 1', '장소 2', '장소 3'].map((loc) => ( - setLocation(loc)} style={styles.locationItem}> - {loc} - - ))} - - - - - {/* 4. 달력 */} - setSelectedDate(day.dateString)} - markedDates={{ - [selectedDate]: { selected: true, selectedColor: '#007AFF' } - }} - theme={{ todayTextColor: '#FF7A00', arrowColor: '#999999', selectedDayTextColor: '#ffffff' }} - /> - - - - {/* 5. 일정 시간 */} - 일정 시간 - - setEventHour(item)} /> - : - setEventMinute(item)} /> - : - setEventSecond(item)} /> - setEventAmPm(item)} /> - - - {/* 6. 음성 알림 시간 */} - 음성 알림 시간 - - setAlarmHour(item)} /> - : - setAlarmMinute(item)} /> - : - setAlarmSecond(item)} /> - setAlarmAmPm(item)} /> - - - {/* 7. 음성 설정 */} - - 음성 알림 설정 - setVoiceOption('tts')}> - - 기본 알림음 (TTS) - - setVoiceOption('recording')}> - - 보호자 음성 녹음 - - - 🎙 녹음 - ▶ 재생 - ✕ 삭제 - - - - - 저장하기 - - - ); -} - -const styles = StyleSheet.create({ - container: { flex: 1, backgroundColor: '#FFFFFF', padding: 20 }, - headerRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginTop: 10, marginBottom: 25 }, - titleGroup: { flexDirection: 'row', alignItems: 'center' }, - iconCalendar: { width: 16, height: 16, backgroundColor: '#FF7A00', borderRadius: 3, marginRight: 8 }, - headerInput: { fontSize: 19, fontWeight: 'bold', color: '#333', minWidth: 60 }, // 이름 입력용 스타일 - textTitle: { color: '#333333', fontSize: 19, fontWeight: 'bold' }, - btnClose: { padding: 5 }, - textClose: { color: '#999999', fontSize: 20 }, - label: { color: '#333333', fontSize: 15, fontWeight: 'bold', marginBottom: 8, marginTop: 12 }, - input: { height: 48, borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, fontSize: 14, color: '#333', marginBottom: 10 }, - locationList: { backgroundColor: '#F9F9F9', padding: 6, borderRadius: 6, marginBottom: 20, flexDirection: 'row' }, - locationItem: { padding: 8 }, - divider: { height: 1, backgroundColor: '#EAEAEA', marginVertical: 20 }, - pickerCard: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: '#FFFFFF', borderWidth: 1, borderColor: '#F0F0F0', borderRadius: 16, paddingHorizontal: 15, height: 150, marginBottom: 25 }, - wheel: { flex: 1, height: 130, backgroundColor: 'transparent' }, - colon: { fontSize: 18, fontWeight: 'bold', color: '#333', textAlign: 'center', width: 10 }, - voiceSettingContainer: { borderWidth: 1, borderColor: '#F0F0F0', borderRadius: 16, padding: 16, marginBottom: 25 }, - radioOption: { flexDirection: 'row', alignItems: 'center', marginTop: 12 }, - radioCircle: { width: 18, height: 18, borderRadius: 9, borderWidth: 2, borderColor: '#EAEAEA', marginRight: 10 }, - radioChecked: { borderColor: '#FF7A00', backgroundColor: '#FF7A00' }, - radioText: { fontSize: 14, color: '#333', fontWeight: '500' }, - voiceButtonContainer: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 15 }, - voiceBtn: { flex: 1, height: 40, borderWidth: 1, borderColor: '#FF7A00', borderRadius: 8, alignItems: 'center', justifyContent: 'center', marginHorizontal: 4 }, - voiceBtnText: { color: '#FF7A00', fontSize: 13, fontWeight: 'bold' }, - btnSave: { height: 54, backgroundColor: '#FF7A00', borderRadius: 10, alignItems: 'center', justifyContent: 'center', marginTop: 5 }, - btnSaveText: { color: '#FFFFFF', fontSize: 16, fontWeight: 'bold' }, -}); +import React, { useState } from 'react'; +import { + StyleSheet, + Text, + View, + ScrollView, + TextInput, + TouchableOpacity, +} from 'react-native'; + +// @ts-ignore +import { Calendar } from 'react-native-calendars'; + +// @ts-ignore +import { WheelPicker } from 'react-native-wheel-pick'; + +export default function ScheduleModal() { + + const [eventName, setEventName] = useState(''); + const [selectedLocation, setSelectedLocation] = useState('장소 1'); + const [voiceOption, setVoiceOption] = useState('tts'); // 'tts' 또는 'recording' + const [selectedDate, setSelectedDate] = useState('2026-04-21'); + + // --- 일정 시간 선택 상태 --- + const [eventHour, setEventHour] = useState('06'); + const [eventMinute, setEventMinute] = useState('28'); + const [eventSecond, setEventSecond] = useState('55'); + const [eventAmPm, setEventAmPm] = useState('PM'); + + // --- 음성 알림 시간 선택 상태 --- + const [alarmHour, setAlarmHour] = useState('06'); + const [alarmMinute, setAlarmMinute] = useState('28'); + const [alarmSecond, setAlarmSecond] = useState('55'); + const [alarmAmPm, setAlarmAmPm] = useState('PM'); + + // 스크롤 데이터 배열 생성 + const hours = Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, '0')); + const minutesSeconds = Array.from({ length: 60 }, (_, i) => String(i).padStart(2, '0')); + const amPmOptions = ['PM', 'AM']; + + return ( + + + {/* 1. 상단 헤더 타이틀 */} + + + + 이나연님 일정 등록 + + + + + + + {/* 2. 일정 이름 입력 필드 */} + 일정 이름 + + + {/* 3. 장소 드롭다운 스타일 */} + 장소 + + 장소를 선택하세요 + + + + {/* 장소 예시 리스트 박스 */} + + {['장소 1', '장소 2', '장소 3'].map((loc) => ( + setSelectedLocation(loc)} style={styles.locationItem}> + {loc} + + ))} + + + + + {/* 4. 달력 컴포넌트 */} + setSelectedDate(day.dateString)} + markedDates={{ + [selectedDate]: { selected: true, selectedColor: '#007AFF' }, + '2026-04-30': { selected: true, selectedColor: '#007AFF' } + }} + theme={{ + todayTextColor: '#FF7A00', + arrowColor: '#999999', + selectedDayTextColor: '#ffffff', + }} + /> + + + + {/* 5. 일정 시간 */} + 일정 시간 + + 일정 시간을 선택하세요 + 🕒 + + + + setEventHour(item)} /> + : + setEventMinute(item)} /> + : + setEventSecond(item)} /> + + setEventAmPm(item)} /> + + + + {/* 6. 음성 알림 시간 */} + 음성 알림 시간 + + 알림을 전달 할 시간을 선택하세요 + 🕒 + + + + setAlarmHour(item)} /> + : + setAlarmMinute(item)} /> + : + setAlarmSecond(item)} /> + + setAlarmAmPm(item)} /> + + + + {/* 7. 음성 알림 설정 컴포넌트 */} + + 음성 알림 설정 + + setVoiceOption('tts')}> + + 기본 알림음 (TTS) + + + setVoiceOption('recording')}> + + 보호자 음성 녹음 + + + {/* 녹음 제어 서브 버튼 배치 */} + + 🎙 녹음 + ▶ 재생 + ✕ 삭제 + + + + {/* 8. 최종 저장 버튼 */} + + 저장하기 + + + + ); +} + +const styles = StyleSheet.create({ + container: { flex: 1, backgroundColor: '#FFFFFF', padding: 20 }, + headerRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginTop: 10, marginBottom: 25 }, + titleGroup: { flexDirection: 'row', alignItems: 'center' }, + iconCalendar: { width: 16, height: 16, backgroundColor: '#FF7A00', borderRadius: 3, marginRight: 8 }, + textTitle: { color: '#333333', fontSize: 19, fontWeight: 'bold' }, + btnClose: { padding: 5 }, + textClose: { color: '#999999', fontSize: 20 }, + label: { color: '#333333', fontSize: 15, fontWeight: 'bold', marginBottom: 8, marginTop: 12 }, + input: { height: 48, borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, fontSize: 14, color: '#333', marginBottom: 20 }, + dropdownFake: { height: 48, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, marginBottom: 4 }, + locationList: { backgroundColor: '#F9F9F9', padding: 6, borderRadius: 6, marginBottom: 20 }, + locationItem: { padding: 8 }, + divider: { height: 1, backgroundColor: '#EAEAEA', marginVertical: 20 }, + infoInputBox: { height: 48, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, marginBottom: 12 }, + infoInputText: { color: '#999999', fontSize: 14 }, + + pickerCard: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: '#FFFFFF', + borderWidth: 1, + borderColor: '#F0F0F0', + borderRadius: 16, + paddingHorizontal: 15, + height: 150, + marginBottom: 25, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.04, + shadowRadius: 5, + elevation: 2, + }, + wheel: { flex: 1, height: 130, backgroundColor: 'transparent' }, + colon: { fontSize: 18, fontWeight: 'bold', color: '#333', textAlign: 'center', width: 10 }, + + voiceSettingContainer: { borderWidth: 1, borderColor: '#F0F0F0', borderRadius: 16, padding: 16, marginBottom: 25 }, + radioOption: { flexDirection: 'row', alignItems: 'center', marginTop: 12 }, + radioCircle: { width: 18, height: 18, borderRadius: 9, borderWidth: 2, borderColor: '#EAEAEA', marginRight: 10 }, + radioChecked: { borderColor: '#FF7A00', backgroundColor: '#FF7A00' }, + radioText: { fontSize: 14, color: '#333', fontWeight: '500' }, + + voiceButtonContainer: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 15 }, + voiceBtn: { flex: 1, height: 40, borderWidth: 1, borderColor: '#FF7A00', borderRadius: 8, backgroundColor: '#FFFFFF', alignItems: 'center', justifyContent: 'center', marginHorizontal: 4 }, + voiceBtnText: { color: '#FF7A00', fontSize: 13, fontWeight: 'bold' }, + + btnSave: { height: 54, backgroundColor: '#FF7A00', borderRadius: 10, alignItems: 'center', justifyContent: 'center', marginTop: 5 }, + btnSaveText: { color: '#FFFFFF', fontSize: 16, fontWeight: 'bold' }, +}); \ No newline at end of file From 5bbc255b24ff5624c8ef74a1851172df9f105182 Mon Sep 17 00:00:00 2001 From: kiho-0 Date: Tue, 14 Jul 2026 20:14:06 +0900 Subject: [PATCH 6/9] =?UTF-8?q?feat:=20=EC=BA=98=EB=A6=B0=EB=8D=94=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20=EC=9D=BC=EC=A0=95=20=EB=93=B1=EB=A1=9D=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=EC=A1=B0=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- UserHomeScreen.tsx | 413 ++++++++++++++++++++++++ src/CalendarSchedule.tsx | 219 ------------- src/components/TimePickerSection.tsx | 21 ++ src/components/VoiceSettingsSection.tsx | 22 ++ src/screens/CalendarSchedule.tsx | 62 ++++ src/styles/ScheduleStyles.ts | 24 ++ 6 files changed, 542 insertions(+), 219 deletions(-) create mode 100644 UserHomeScreen.tsx delete mode 100644 src/CalendarSchedule.tsx create mode 100644 src/components/TimePickerSection.tsx create mode 100644 src/components/VoiceSettingsSection.tsx create mode 100644 src/screens/CalendarSchedule.tsx create mode 100644 src/styles/ScheduleStyles.ts diff --git a/UserHomeScreen.tsx b/UserHomeScreen.tsx new file mode 100644 index 0000000..a00d263 --- /dev/null +++ b/UserHomeScreen.tsx @@ -0,0 +1,413 @@ +// ParentHomeScreen.tsx +import React from 'react'; +import { + StyleSheet, + Text, + View, + ScrollView, + SafeAreaView +} from 'react-native'; +import MedicationPill from './MedicationPill'; // tsx 컴포넌트 불러오기 + +export default function ParentHomeScreen() { + // 4월 날짜 배열 생성 (TypeScript에서 number 타입 배열로 명시) + const days: number[] = Array.from({ length: 30 }, (_, i) => i + 1); + + return ( + + {/* --- GNB 헤더 영역 --- */} + + + + Caring + + + + 🔔 + + + + + {/* --- 메인 스크롤 콘텐츠 (조회 전용) --- */} + + + {/* 1. 하루 요약 레포트 카드 */} + + + 🐱 하루 요약 레포트 + + 하루 요약 레포트를 매일 21 : 00 시에 받아요 + + 오늘의 건강 상태 + + + 😀 + + + 😐 + + + 😥 + + + + + 오늘 하루 요약 + 어머니의 오늘 건강 상태는 '좋음' 이예요! + 어머니는 오늘 자녀에게 전화를 거셨어요 + + + + {/* 2. 일정 관리 카드 */} + + + 📅 일정 관리 + 일정 등록 + + + + 2026년 4월 < > + + {['일', '월', '화', '수', '목', '금', '토'].map((w, i) => ( + {w} + ))} + + + {/* 디자인 레이아웃 정렬을 위한 공백 칸 */} + + + + {days.map((day) => ( + + {day} + + ))} + + + + + {/* 3. 복약 관리 카드 */} + + + 💊 복약 관리 + 복약 추가 + + + + {/* 데이터 타입을 맞춰 true/false 바인딩 */} + + + + + + + {/* 4. 위치 GPS 카드 */} + + + 📍 위치 GPS + + + + 🔵 + + 지도 실시간 위치 표시 영역 + + + + {/* 5. 주변 공공 복지 시설 카드 */} + + + 🔖 주변 공공 복지 시설 + 더보기 > + + + + 🏢 + + 국가지원금 확인하기 + 돌봄대상자를 위한 지원금 체크 + + + + 🏛️ + + 보건복지부 지원 서비스 확인 + 보건복지부에서 지원하는 돌봄 서비스 + + + + + + + {/* --- 하단 탭 바 --- */} + + + 🏠 + + + + 👥 + 돌봄대상자 관리 + + + 👤 + 마이페이지 + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#FFFFFF', + }, + header: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingHorizontal: 16, + paddingVertical: 12, + borderBottomWidth: 1, + borderBottomColor: '#F2F2F7', + }, + logoContainer: { + flexDirection: 'row', + alignItems: 'center', + }, + logoCircle: { + width: 50, + height: 50, + borderRadius: 25, + borderWidth: 1, + borderColor: '#E5E5EA', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: '#FFF8F2', + }, + logoText: { + fontSize: 12, + fontWeight: 'bold', + color: '#FF7A00', + }, + headerIcons: { + flexDirection: 'row', + gap: 16, + }, + iconPlaceholder: { + fontSize: 22, + color: '#333', + }, + scrollContent: { + paddingVertical: 16, + }, + card: { + backgroundColor: '#FFFFFF', + borderRadius: 16, + padding: 16, + marginHorizontal: 16, + marginBottom: 20, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.05, + shadowRadius: 8, + elevation: 3, + borderWidth: 1, + borderColor: '#E5E5EA', + }, + cardHeader: { + marginBottom: 8, + }, + cardHeaderSpace: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: 12, + }, + cardTitle: { + fontSize: 16, + fontWeight: 'bold', + color: '#1C1C1E', + }, + miniTag: { + backgroundColor: '#F2F2F7', + paddingHorizontal: 8, + paddingVertical: 4, + borderRadius: 6, + }, + miniTagText: { + fontSize: 11, + color: '#636366', + }, + reportSubText: { + fontSize: 12, + color: '#333', + marginBottom: 2, + }, + reportNotice: { + fontSize: 9, + color: '#8E8E93', + marginBottom: 16, + }, + sectionSubTitle: { + fontSize: 13, + fontWeight: '600', + color: '#1C1C1E', + marginBottom: 10, + }, + emojiContainer: { + flexDirection: 'row', + justifyContent: 'space-around', + marginBottom: 16, + }, + emojiCircle: { + width: 60, + height: 60, + borderRadius: 30, + backgroundColor: '#F2F2F7', + justifyContent: 'center', + alignItems: 'center', + }, + emojiSelected: { + borderWidth: 2, + borderColor: '#FF7A00', + backgroundColor: '#FFF8F2', + }, + emojiText: { + fontSize: 30, + }, + summaryBox: { + backgroundColor: '#F8F9FA', + borderRadius: 12, + padding: 12, + borderWidth: 1, + borderColor: '#E5E5EA', + }, + summaryTitle: { + fontSize: 13, + fontWeight: 'bold', + marginBottom: 6, + }, + summaryContent: { + fontSize: 12, + color: '#48484A', + marginBottom: 2, + }, + calendarContainer: { + borderWidth: 1, + borderColor: '#E5E5EA', + borderRadius: 12, + padding: 12, + }, + calendarMonth: { + fontSize: 14, + fontWeight: '600', + textAlign: 'center', + marginBottom: 12, + }, + weekLabels: { + flexDirection: 'row', + justifyContent: 'space-around', + marginBottom: 8, + }, + weekText: { + fontSize: 11, + color: '#8E8E93', + width: '14.28%', + textAlign: 'center', + }, + daysGrid: { + flexDirection: 'row', + flexWrap: 'wrap', + }, + dayCell: { + width: '14.28%', + height: 32, + justifyContent: 'center', + alignItems: 'center', + }, + dayText: { + fontSize: 13, + color: '#1C1C1E', + }, + pillsRow: { + flexDirection: 'row', + justifyContent: 'space-around', + paddingVertical: 8, + }, + mapMock: { + height: 180, + backgroundColor: '#E5E5EA', + borderRadius: 12, + justifyContent: 'center', + alignItems: 'center', + position: 'relative', + overflow: 'hidden', + }, + mapOverlay: { + position: 'absolute', + top: '50%', + left: '50%', + marginLeft: -10, + marginTop: -10, + }, + mapMarker: { + fontSize: 20, + }, + mapText: { + fontSize: 12, + color: '#8E8E93', + marginTop: 40, + }, + moreText: { + fontSize: 12, + color: '#8E8E93', + }, + facilityItem: { + flexDirection: 'row', + alignItems: 'center', + padding: 12, + borderWidth: 1, + borderColor: '#F2F2F7', + borderRadius: 12, + marginBottom: 8, + gap: 12, + }, + facilityIcon: { + fontSize: 24, + }, + facilityName: { + fontSize: 13, + fontWeight: '600', + color: '#1C1C1E', + }, + facilityDesc: { + fontSize: 11, + color: '#8E8E93', + }, + bottomTabBar: { + flexDirection: 'row', + justifyContent: 'space-around', + alignItems: 'center', + height: 64, + borderTopWidth: 1, + borderTopColor: '#F2F2F7', + backgroundColor: '#FFFFFF', + }, + tabItem: { + alignItems: 'center', + }, + tabIcon: { + fontSize: 20, + }, + tabLabel: { + fontSize: 10, + color: '#8E8E93', + marginTop: 2, + }, + activeTab: { + color: '#1C1C1E', + fontWeight: 'bold', + }, +}); \ No newline at end of file diff --git a/src/CalendarSchedule.tsx b/src/CalendarSchedule.tsx deleted file mode 100644 index 3c02950..0000000 --- a/src/CalendarSchedule.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import React, { useState } from 'react'; -import { - StyleSheet, - Text, - View, - ScrollView, - TextInput, - TouchableOpacity, -} from 'react-native'; - -// @ts-ignore -import { Calendar } from 'react-native-calendars'; - -// @ts-ignore -import { WheelPicker } from 'react-native-wheel-pick'; - -export default function ScheduleModal() { - - const [eventName, setEventName] = useState(''); - const [selectedLocation, setSelectedLocation] = useState('장소 1'); - const [voiceOption, setVoiceOption] = useState('tts'); // 'tts' 또는 'recording' - const [selectedDate, setSelectedDate] = useState('2026-04-21'); - - // --- 일정 시간 선택 상태 --- - const [eventHour, setEventHour] = useState('06'); - const [eventMinute, setEventMinute] = useState('28'); - const [eventSecond, setEventSecond] = useState('55'); - const [eventAmPm, setEventAmPm] = useState('PM'); - - // --- 음성 알림 시간 선택 상태 --- - const [alarmHour, setAlarmHour] = useState('06'); - const [alarmMinute, setAlarmMinute] = useState('28'); - const [alarmSecond, setAlarmSecond] = useState('55'); - const [alarmAmPm, setAlarmAmPm] = useState('PM'); - - // 스크롤 데이터 배열 생성 - const hours = Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, '0')); - const minutesSeconds = Array.from({ length: 60 }, (_, i) => String(i).padStart(2, '0')); - const amPmOptions = ['PM', 'AM']; - - return ( - - - {/* 1. 상단 헤더 타이틀 */} - - - - 이나연님 일정 등록 - - - - - - - {/* 2. 일정 이름 입력 필드 */} - 일정 이름 - - - {/* 3. 장소 드롭다운 스타일 */} - 장소 - - 장소를 선택하세요 - - - - {/* 장소 예시 리스트 박스 */} - - {['장소 1', '장소 2', '장소 3'].map((loc) => ( - setSelectedLocation(loc)} style={styles.locationItem}> - {loc} - - ))} - - - - - {/* 4. 달력 컴포넌트 */} - setSelectedDate(day.dateString)} - markedDates={{ - [selectedDate]: { selected: true, selectedColor: '#007AFF' }, - '2026-04-30': { selected: true, selectedColor: '#007AFF' } - }} - theme={{ - todayTextColor: '#FF7A00', - arrowColor: '#999999', - selectedDayTextColor: '#ffffff', - }} - /> - - - - {/* 5. 일정 시간 */} - 일정 시간 - - 일정 시간을 선택하세요 - 🕒 - - - - setEventHour(item)} /> - : - setEventMinute(item)} /> - : - setEventSecond(item)} /> - - setEventAmPm(item)} /> - - - - {/* 6. 음성 알림 시간 */} - 음성 알림 시간 - - 알림을 전달 할 시간을 선택하세요 - 🕒 - - - - setAlarmHour(item)} /> - : - setAlarmMinute(item)} /> - : - setAlarmSecond(item)} /> - - setAlarmAmPm(item)} /> - - - - {/* 7. 음성 알림 설정 컴포넌트 */} - - 음성 알림 설정 - - setVoiceOption('tts')}> - - 기본 알림음 (TTS) - - - setVoiceOption('recording')}> - - 보호자 음성 녹음 - - - {/* 녹음 제어 서브 버튼 배치 */} - - 🎙 녹음 - ▶ 재생 - ✕ 삭제 - - - - {/* 8. 최종 저장 버튼 */} - - 저장하기 - - - - ); -} - -const styles = StyleSheet.create({ - container: { flex: 1, backgroundColor: '#FFFFFF', padding: 20 }, - headerRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginTop: 10, marginBottom: 25 }, - titleGroup: { flexDirection: 'row', alignItems: 'center' }, - iconCalendar: { width: 16, height: 16, backgroundColor: '#FF7A00', borderRadius: 3, marginRight: 8 }, - textTitle: { color: '#333333', fontSize: 19, fontWeight: 'bold' }, - btnClose: { padding: 5 }, - textClose: { color: '#999999', fontSize: 20 }, - label: { color: '#333333', fontSize: 15, fontWeight: 'bold', marginBottom: 8, marginTop: 12 }, - input: { height: 48, borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, fontSize: 14, color: '#333', marginBottom: 20 }, - dropdownFake: { height: 48, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, marginBottom: 4 }, - locationList: { backgroundColor: '#F9F9F9', padding: 6, borderRadius: 6, marginBottom: 20 }, - locationItem: { padding: 8 }, - divider: { height: 1, backgroundColor: '#EAEAEA', marginVertical: 20 }, - infoInputBox: { height: 48, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, marginBottom: 12 }, - infoInputText: { color: '#999999', fontSize: 14 }, - - pickerCard: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - backgroundColor: '#FFFFFF', - borderWidth: 1, - borderColor: '#F0F0F0', - borderRadius: 16, - paddingHorizontal: 15, - height: 150, - marginBottom: 25, - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.04, - shadowRadius: 5, - elevation: 2, - }, - wheel: { flex: 1, height: 130, backgroundColor: 'transparent' }, - colon: { fontSize: 18, fontWeight: 'bold', color: '#333', textAlign: 'center', width: 10 }, - - voiceSettingContainer: { borderWidth: 1, borderColor: '#F0F0F0', borderRadius: 16, padding: 16, marginBottom: 25 }, - radioOption: { flexDirection: 'row', alignItems: 'center', marginTop: 12 }, - radioCircle: { width: 18, height: 18, borderRadius: 9, borderWidth: 2, borderColor: '#EAEAEA', marginRight: 10 }, - radioChecked: { borderColor: '#FF7A00', backgroundColor: '#FF7A00' }, - radioText: { fontSize: 14, color: '#333', fontWeight: '500' }, - - voiceButtonContainer: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 15 }, - voiceBtn: { flex: 1, height: 40, borderWidth: 1, borderColor: '#FF7A00', borderRadius: 8, backgroundColor: '#FFFFFF', alignItems: 'center', justifyContent: 'center', marginHorizontal: 4 }, - voiceBtnText: { color: '#FF7A00', fontSize: 13, fontWeight: 'bold' }, - - btnSave: { height: 54, backgroundColor: '#FF7A00', borderRadius: 10, alignItems: 'center', justifyContent: 'center', marginTop: 5 }, - btnSaveText: { color: '#FFFFFF', fontSize: 16, fontWeight: 'bold' }, -}); \ No newline at end of file diff --git a/src/components/TimePickerSection.tsx b/src/components/TimePickerSection.tsx new file mode 100644 index 0000000..5bb9b79 --- /dev/null +++ b/src/components/TimePickerSection.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { View, Text } from 'react-native'; +// @ts-ignore +import { WheelPicker } from 'react-native-wheel-pick'; +import { styles } from '../styles/ScheduleStyles'; + +export const TimePickerSection = ({ label, hour, minute, second, amPm, onHourChange, onMinuteChange, onSecondChange, onAmPmChange }: any) => { + return ( + <> + {label} + + String(i+1).padStart(2, '0'))} selectedItem={hour} onItemSelected={onHourChange} /> + : + String(i).padStart(2, '0'))} selectedItem={minute} onItemSelected={onMinuteChange} /> + : + String(i).padStart(2, '0'))} selectedItem={second} onItemSelected={onSecondChange} /> + + + + ); +}; \ No newline at end of file diff --git a/src/components/VoiceSettingsSection.tsx b/src/components/VoiceSettingsSection.tsx new file mode 100644 index 0000000..d5f02ff --- /dev/null +++ b/src/components/VoiceSettingsSection.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { View, Text, TouchableOpacity } from 'react-native'; +import { styles } from '../styles/ScheduleStyles'; + +export const VoiceSettingsSection = ({ voiceOption, setVoiceOption }: any) => ( + + 음성 알림 설정 + setVoiceOption('tts')}> + + 기본 알림음 (TTS) + + setVoiceOption('recording')}> + + 보호자 음성 녹음 + + + 🎙 녹음 + ▶ 재생 + ✕ 삭제 + + +); \ No newline at end of file diff --git a/src/screens/CalendarSchedule.tsx b/src/screens/CalendarSchedule.tsx new file mode 100644 index 0000000..9f93958 --- /dev/null +++ b/src/screens/CalendarSchedule.tsx @@ -0,0 +1,62 @@ +import React, { useState } from 'react'; +import { ScrollView, View, Text, TextInput, TouchableOpacity } from 'react-native'; +// @ts-ignore +import { Calendar } from 'react-native-calendars'; +import { styles } from '../styles/ScheduleStyles'; +import { TimePickerSection } from '../components/TimePickerSection'; +import { VoiceSettingsSection } from '../components/VoiceSettingsSection'; + +export default function ScheduleModal() { + const [userName, setUserName] = useState(''); + const [eventName, setEventName] = useState(''); + const [eventHour, setEventHour] = useState('06'); + const [eventMinute, setEventMinute] = useState('28'); + const [eventSecond, setEventSecond] = useState('55'); + const [eventAmPm, setEventAmPm] = useState('PM'); + const [voiceOption, setVoiceOption] = useState('tts'); + + return ( + + 사용자 이름 + + + {userName || '사용자'}님 일정 등록 + + 일정 이름 + + + + + + + + + + 저장하기 + + + ); +} \ No newline at end of file diff --git a/src/styles/ScheduleStyles.ts b/src/styles/ScheduleStyles.ts new file mode 100644 index 0000000..f9140e4 --- /dev/null +++ b/src/styles/ScheduleStyles.ts @@ -0,0 +1,24 @@ +import { StyleSheet } from 'react-native'; + +export const styles = StyleSheet.create({ + container: { flex: 1, backgroundColor: '#FFFFFF', padding: 20 }, + headerRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginTop: 10, marginBottom: 25 }, + titleGroup: { flexDirection: 'row', alignItems: 'center' }, + iconCalendar: { width: 16, height: 16, backgroundColor: '#FF7A00', borderRadius: 3, marginRight: 8 }, + textTitle: { color: '#333333', fontSize: 19, fontWeight: 'bold' }, + label: { color: '#333333', fontSize: 15, fontWeight: 'bold', marginBottom: 8, marginTop: 12 }, + input: { height: 48, borderWidth: 1, borderColor: '#EAEAEA', borderRadius: 6, paddingHorizontal: 12, fontSize: 14, color: '#333', marginBottom: 20 }, + pickerCard: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: '#FFFFFF', borderWidth: 1, borderColor: '#F0F0F0', borderRadius: 16, paddingHorizontal: 15, height: 150, marginBottom: 25, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.04, shadowRadius: 5, elevation: 2 }, + wheel: { flex: 1, height: 130, backgroundColor: 'transparent' }, + colon: { fontSize: 18, fontWeight: 'bold', color: '#333', textAlign: 'center', width: 10 }, + voiceSettingContainer: { borderWidth: 1, borderColor: '#F0F0F0', borderRadius: 16, padding: 16, marginBottom: 25 }, + radioOption: { flexDirection: 'row', alignItems: 'center', marginTop: 12 }, + radioCircle: { width: 18, height: 18, borderRadius: 9, borderWidth: 2, borderColor: '#EAEAEA', marginRight: 10 }, + radioChecked: { borderColor: '#FF7A00', backgroundColor: '#FF7A00' }, + radioText: { fontSize: 14, color: '#333', fontWeight: '500' }, + voiceButtonContainer: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 15 }, + voiceBtn: { flex: 1, height: 40, borderWidth: 1, borderColor: '#FF7A00', borderRadius: 8, backgroundColor: '#FFFFFF', alignItems: 'center', justifyContent: 'center', marginHorizontal: 4 }, + voiceBtnText: { color: '#FF7A00', fontSize: 13, fontWeight: 'bold' }, + btnSave: { height: 54, backgroundColor: '#FF7A00', borderRadius: 10, alignItems: 'center', justifyContent: 'center', marginTop: 5 }, + btnSaveText: { color: '#FFFFFF', fontSize: 16, fontWeight: 'bold' }, +}); \ No newline at end of file From 54db4532e798e7c562a17c5b056a9671252fe52a Mon Sep 17 00:00:00 2001 From: kiho-0 Date: Wed, 15 Jul 2026 13:30:05 +0900 Subject: [PATCH 7/9] =?UTF-8?q?feat:=20=EC=BA=98=EB=A6=B0=EB=8D=94=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20=EC=9D=BC=EC=A0=95=20=EB=B0=8F=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/{screens => shared/ui}/CalendarSchedule.tsx | 6 +++--- src/{components => shared/ui}/TimePickerSection.tsx | 2 +- src/{components => shared/ui}/VoiceSettingsSection.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/{screens => shared/ui}/CalendarSchedule.tsx (90%) rename src/{components => shared/ui}/TimePickerSection.tsx (95%) rename src/{components => shared/ui}/VoiceSettingsSection.tsx (95%) diff --git a/src/screens/CalendarSchedule.tsx b/src/shared/ui/CalendarSchedule.tsx similarity index 90% rename from src/screens/CalendarSchedule.tsx rename to src/shared/ui/CalendarSchedule.tsx index 9f93958..00d8b2d 100644 --- a/src/screens/CalendarSchedule.tsx +++ b/src/shared/ui/CalendarSchedule.tsx @@ -2,9 +2,9 @@ import React, { useState } from 'react'; import { ScrollView, View, Text, TextInput, TouchableOpacity } from 'react-native'; // @ts-ignore import { Calendar } from 'react-native-calendars'; -import { styles } from '../styles/ScheduleStyles'; -import { TimePickerSection } from '../components/TimePickerSection'; -import { VoiceSettingsSection } from '../components/VoiceSettingsSection'; +import { styles } from '../../styles/ScheduleStyles'; +import { TimePickerSection } from './TimePickerSection'; +import { VoiceSettingsSection } from './VoiceSettingsSection'; export default function ScheduleModal() { const [userName, setUserName] = useState(''); diff --git a/src/components/TimePickerSection.tsx b/src/shared/ui/TimePickerSection.tsx similarity index 95% rename from src/components/TimePickerSection.tsx rename to src/shared/ui/TimePickerSection.tsx index 5bb9b79..8115fed 100644 --- a/src/components/TimePickerSection.tsx +++ b/src/shared/ui/TimePickerSection.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { View, Text } from 'react-native'; // @ts-ignore import { WheelPicker } from 'react-native-wheel-pick'; -import { styles } from '../styles/ScheduleStyles'; +import { styles } from '../../styles/ScheduleStyles'; export const TimePickerSection = ({ label, hour, minute, second, amPm, onHourChange, onMinuteChange, onSecondChange, onAmPmChange }: any) => { return ( diff --git a/src/components/VoiceSettingsSection.tsx b/src/shared/ui/VoiceSettingsSection.tsx similarity index 95% rename from src/components/VoiceSettingsSection.tsx rename to src/shared/ui/VoiceSettingsSection.tsx index d5f02ff..669d12b 100644 --- a/src/components/VoiceSettingsSection.tsx +++ b/src/shared/ui/VoiceSettingsSection.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { View, Text, TouchableOpacity } from 'react-native'; -import { styles } from '../styles/ScheduleStyles'; +import { styles } from '../../styles/ScheduleStyles'; export const VoiceSettingsSection = ({ voiceOption, setVoiceOption }: any) => ( From a0107021dc154e63185e0b43f67f517a8d16ab3f Mon Sep 17 00:00:00 2001 From: kiho-0 Date: Wed, 15 Jul 2026 16:18:00 +0900 Subject: [PATCH 8/9] =?UTF-8?q?feat:=20=EA=B1=B4=EA=B0=95=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EC=B2=B4=ED=81=AC=20=ED=99=94=EB=A9=B4=20=EB=B0=8F?= =?UTF-8?q?=20=EB=AA=A8=EB=8B=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/health/hooks/useHealthRecord.ts | 0 src/features/health/styles/HealthStyles.ts | 0 .../health}/styles/ScheduleStyles.ts | 0 src/features/health/types/health.ts | 5 ++ src/features/health/ui/HealthRecordModal.tsx | 0 src/shared/ui/CalendarSchedule.tsx | 62 ------------------- src/shared/ui/TimePickerSection.tsx | 21 ------- src/shared/ui/VoiceSettingsSection.tsx | 22 ------- 8 files changed, 5 insertions(+), 105 deletions(-) create mode 100644 src/features/health/hooks/useHealthRecord.ts create mode 100644 src/features/health/styles/HealthStyles.ts rename src/{ => features/health}/styles/ScheduleStyles.ts (100%) create mode 100644 src/features/health/types/health.ts create mode 100644 src/features/health/ui/HealthRecordModal.tsx delete mode 100644 src/shared/ui/CalendarSchedule.tsx delete mode 100644 src/shared/ui/TimePickerSection.tsx delete mode 100644 src/shared/ui/VoiceSettingsSection.tsx diff --git a/src/features/health/hooks/useHealthRecord.ts b/src/features/health/hooks/useHealthRecord.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/features/health/styles/HealthStyles.ts b/src/features/health/styles/HealthStyles.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/styles/ScheduleStyles.ts b/src/features/health/styles/ScheduleStyles.ts similarity index 100% rename from src/styles/ScheduleStyles.ts rename to src/features/health/styles/ScheduleStyles.ts diff --git a/src/features/health/types/health.ts b/src/features/health/types/health.ts new file mode 100644 index 0000000..182e978 --- /dev/null +++ b/src/features/health/types/health.ts @@ -0,0 +1,5 @@ +export interface HealthData { + bloodSugar: string; + bloodPressure: string; + weight: string; +} \ No newline at end of file diff --git a/src/features/health/ui/HealthRecordModal.tsx b/src/features/health/ui/HealthRecordModal.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/shared/ui/CalendarSchedule.tsx b/src/shared/ui/CalendarSchedule.tsx deleted file mode 100644 index 00d8b2d..0000000 --- a/src/shared/ui/CalendarSchedule.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React, { useState } from 'react'; -import { ScrollView, View, Text, TextInput, TouchableOpacity } from 'react-native'; -// @ts-ignore -import { Calendar } from 'react-native-calendars'; -import { styles } from '../../styles/ScheduleStyles'; -import { TimePickerSection } from './TimePickerSection'; -import { VoiceSettingsSection } from './VoiceSettingsSection'; - -export default function ScheduleModal() { - const [userName, setUserName] = useState(''); - const [eventName, setEventName] = useState(''); - const [eventHour, setEventHour] = useState('06'); - const [eventMinute, setEventMinute] = useState('28'); - const [eventSecond, setEventSecond] = useState('55'); - const [eventAmPm, setEventAmPm] = useState('PM'); - const [voiceOption, setVoiceOption] = useState('tts'); - - return ( - - 사용자 이름 - - - {userName || '사용자'}님 일정 등록 - - 일정 이름 - - - - - - - - - - 저장하기 - - - ); -} \ No newline at end of file diff --git a/src/shared/ui/TimePickerSection.tsx b/src/shared/ui/TimePickerSection.tsx deleted file mode 100644 index 8115fed..0000000 --- a/src/shared/ui/TimePickerSection.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import { View, Text } from 'react-native'; -// @ts-ignore -import { WheelPicker } from 'react-native-wheel-pick'; -import { styles } from '../../styles/ScheduleStyles'; - -export const TimePickerSection = ({ label, hour, minute, second, amPm, onHourChange, onMinuteChange, onSecondChange, onAmPmChange }: any) => { - return ( - <> - {label} - - String(i+1).padStart(2, '0'))} selectedItem={hour} onItemSelected={onHourChange} /> - : - String(i).padStart(2, '0'))} selectedItem={minute} onItemSelected={onMinuteChange} /> - : - String(i).padStart(2, '0'))} selectedItem={second} onItemSelected={onSecondChange} /> - - - - ); -}; \ No newline at end of file diff --git a/src/shared/ui/VoiceSettingsSection.tsx b/src/shared/ui/VoiceSettingsSection.tsx deleted file mode 100644 index 669d12b..0000000 --- a/src/shared/ui/VoiceSettingsSection.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import { View, Text, TouchableOpacity } from 'react-native'; -import { styles } from '../../styles/ScheduleStyles'; - -export const VoiceSettingsSection = ({ voiceOption, setVoiceOption }: any) => ( - - 음성 알림 설정 - setVoiceOption('tts')}> - - 기본 알림음 (TTS) - - setVoiceOption('recording')}> - - 보호자 음성 녹음 - - - 🎙 녹음 - ▶ 재생 - ✕ 삭제 - - -); \ No newline at end of file From 7db5feefa69935938cd596bcd3be8dc3a0169a1a Mon Sep 17 00:00:00 2001 From: kiho-0 Date: Wed, 15 Jul 2026 16:41:12 +0900 Subject: [PATCH 9/9] =?UTF-8?q?feat:=20=EA=B1=B4=EA=B0=95=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EC=B2=B4=ED=81=AC=20=ED=99=94=EB=A9=B4=20=EB=B0=8F?= =?UTF-8?q?=20=EB=AA=A8=EB=8B=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/health/hooks/useHealthRecord.ts | 14 +++++++ src/features/health/styles/HealthStyles.ts | 11 ++++++ src/features/health/ui/HealthRecordModal.tsx | 39 ++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/src/features/health/hooks/useHealthRecord.ts b/src/features/health/hooks/useHealthRecord.ts index e69de29..6b748ac 100644 --- a/src/features/health/hooks/useHealthRecord.ts +++ b/src/features/health/hooks/useHealthRecord.ts @@ -0,0 +1,14 @@ +import { useState } from 'react'; +import { HealthData } from '../types/health'; + +export const useHealthRecord = () => { + const [data, setData] = useState({ bloodSugar: '', bloodPressure: '', weight: '' }); + + const updateField = (key: keyof HealthData, value: string) => { + setData((prev) => ({ ...prev, [key]: value })); + }; + + const reset = () => setData({ bloodSugar: '', bloodPressure: '', weight: '' }); + + return { data, updateField, reset }; +}; \ No newline at end of file diff --git a/src/features/health/styles/HealthStyles.ts b/src/features/health/styles/HealthStyles.ts index e69de29..1c7ac3f 100644 --- a/src/features/health/styles/HealthStyles.ts +++ b/src/features/health/styles/HealthStyles.ts @@ -0,0 +1,11 @@ +import { StyleSheet } from 'react-native'; + +export const HealthStyles = StyleSheet.create({ + overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'center', alignItems: 'center' }, + modalContent: { width: '85%', padding: 20, backgroundColor: 'white', borderRadius: 15 }, + title: { fontSize: 20, fontWeight: 'bold', marginBottom: 5 }, + subtitle: { fontSize: 12, color: '#666', marginBottom: 15 }, + input: { borderWidth: 1, borderColor: '#ddd', borderRadius: 8, padding: 12, marginBottom: 15 }, + saveButton: { backgroundColor: '#FF8C00', padding: 15, borderRadius: 8, alignItems: 'center' }, + saveButtonText: { color: 'white', fontWeight: 'bold', fontSize: 16 } +}); \ No newline at end of file diff --git a/src/features/health/ui/HealthRecordModal.tsx b/src/features/health/ui/HealthRecordModal.tsx index e69de29..1d10240 100644 --- a/src/features/health/ui/HealthRecordModal.tsx +++ b/src/features/health/ui/HealthRecordModal.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { Modal, View, Text, TextInput, TouchableOpacity } from 'react-native'; +import { HealthStyles as styles } from '../styles/HealthStyles'; +import { useHealthRecord } from '../hooks/useHealthRecord'; + +interface Props { + visible: boolean; + onClose: () => void; + onSave: (data: any) => void; +} + +export const HealthRecordModal = ({ visible, onClose, onSave }: Props) => { + const { data, updateField, reset } = useHealthRecord(); + + const handleSave = () => { + onSave(data); + reset(); + onClose(); + }; + + return ( + + + + 오늘의 건강 기록하기 + 입력하신 수치는 보호자님 리포트에 반영됩니다. + + updateField('bloodSugar', v)} /> + updateField('bloodPressure', v)} /> + updateField('weight', v)} /> + + + 저장하기 + + + + + ); +}; \ No newline at end of file