Skip to content
Merged
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
2 changes: 1 addition & 1 deletion src/components/common/InBodyCalendarModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ const InBodyCalendarModal: React.FC<InBodyCalendarModalProps> = ({
// 단, 이미 선택된 날짜는 항상 선택 가능 (현재 보고 있는 날짜)
const isSelectable = onlySelectableDates
? (isInBodyDate || isSelected) // 기록이 있거나 이미 선택된 날짜는 선택 가능
: (inBodyDates.length === 0 || isInBodyDate);
: true; // 수기 입력 모드에서는 모든 날짜(과거, 현재, 미래) 선택 가능

// 디버깅: 기록이 있는 날짜 확인
if (__DEV__ && isInBodyDate && date.getDate() <= 3) {
Expand Down
109 changes: 98 additions & 11 deletions src/components/common/InBodyManualForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ const InBodyManualForm: React.FC<InBodyManualFormProps> = ({
const ageInputRef = useRef<TextInput>(null);
const ageTextRef = useRef<string>("");
const inputRefs = useRef<{ [key: string]: TextInput | null }>({});
const [ageValue, setAgeValue] = useState<string>("");

// 날짜를 YYYY-MM-DD 형식으로 자동 포맷팅
const formatDate = (text: string): string => {
Expand Down Expand Up @@ -157,6 +158,7 @@ const InBodyManualForm: React.FC<InBodyManualFormProps> = ({
setV((s) => ({ ...s, ...(defaultValues as any) }));
if (typeof defaultValues.age === "string") {
ageTextRef.current = defaultValues.age;
setAgeValue(defaultValues.age);
}
} else {
console.log("[INBODY FORM] defaultValues 없음 또는 유효하지 않음:", defaultValues);
Expand Down Expand Up @@ -193,7 +195,57 @@ const InBodyManualForm: React.FC<InBodyManualFormProps> = ({
return (w / (m * m)).toFixed(1);
}, [v.height, v.weight]);

// 필수 필드 검증
const isFormValid = useMemo(() => {
// 체중, 체지방률, 신장, 나이, 성별, 골격근량
const basicFieldsValid =
v.weight?.trim() !== "" &&
v.pbf?.trim() !== "" &&
v.height?.trim() !== "" &&
ageValue?.trim() !== "" &&
v.gender !== "" &&
v.smm?.trim() !== "";

// 부위별 체지방량 (5개 모두)
const fatFieldsValid =
v.rArmFat?.trim() !== "" &&
v.lArmFat?.trim() !== "" &&
v.trunkFat?.trim() !== "" &&
v.rLegFat?.trim() !== "" &&
v.lLegFat?.trim() !== "";

// 부위별 근육량 (5개 모두)
const muscleFieldsValid =
v.rArm?.trim() !== "" &&
v.lArm?.trim() !== "" &&
v.trunk?.trim() !== "" &&
v.rLeg?.trim() !== "" &&
v.lLeg?.trim() !== "";

return basicFieldsValid && fatFieldsValid && muscleFieldsValid;
}, [
v.weight,
v.pbf,
v.height,
ageValue,
v.gender,
v.smm,
v.rArmFat,
v.lArmFat,
v.trunkFat,
v.rLegFat,
v.lLegFat,
v.rArm,
v.lArm,
v.trunk,
v.rLeg,
v.lLeg,
]);

const handleSubmit = () => {
if (!isFormValid) {
return;
}
const payload = {
...v,
age: ageTextRef.current,
Expand Down Expand Up @@ -244,7 +296,9 @@ const InBodyManualForm: React.FC<InBodyManualFormProps> = ({

{/* 성별 */}
<View style={styles.field}>
<Text style={styles.lab}>성별</Text>
<Text style={styles.lab}>
성별 <Text style={styles.required}>*</Text>
</Text>
<View style={styles.row}>
<TouchableOpacity
style={styles.radioButton}
Expand Down Expand Up @@ -275,14 +329,17 @@ const InBodyManualForm: React.FC<InBodyManualFormProps> = ({

{/* 나이 (비제어) */}
<View style={styles.field}>
<Text style={styles.lab}>나이</Text>
<Text style={styles.lab}>
나이 <Text style={styles.required}>*</Text>
</Text>
<View style={styles.unitBox}>
<TextInput
ref={ageInputRef}
style={styles.inpRight}
defaultValue={v.age}
onChangeText={(text) => {
ageTextRef.current = text;
setAgeValue(text);
}}
keyboardType="number-pad"
// ✅ 수정: InputAccessoryView 표시를 위해 필수
Expand All @@ -297,7 +354,9 @@ const InBodyManualForm: React.FC<InBodyManualFormProps> = ({

{/* 신장 */}
<View style={styles.field}>
<Text style={styles.lab}>신장</Text>
<Text style={styles.lab}>
신장 <Text style={styles.required}>*</Text>
</Text>
<View style={styles.unitBox}>
<TextInput
style={styles.inpRight}
Expand All @@ -318,7 +377,9 @@ const InBodyManualForm: React.FC<InBodyManualFormProps> = ({
<View style={styles.sec}>
{/* 체중 */}
<View style={styles.field}>
<Text style={styles.lab}>체중</Text>
<Text style={styles.lab}>
체중 <Text style={styles.required}>*</Text>
</Text>
<View style={styles.unitBox}>
<TextInput
style={styles.inpRight}
Expand All @@ -336,7 +397,9 @@ const InBodyManualForm: React.FC<InBodyManualFormProps> = ({

{/* 골격근량 */}
<View style={styles.field}>
<Text style={styles.lab}>골격근량(SMM)</Text>
<Text style={styles.lab}>
골격근량(SMM) <Text style={styles.required}>*</Text>
</Text>
<View style={styles.unitBox}>
<TextInput
style={styles.inpRight}
Expand Down Expand Up @@ -372,7 +435,9 @@ const InBodyManualForm: React.FC<InBodyManualFormProps> = ({

{/* 체지방률 */}
<View style={styles.field}>
<Text style={styles.lab}>체지방률(PBF)</Text>
<Text style={styles.lab}>
체지방률(PBF) <Text style={styles.required}>*</Text>
</Text>
<View style={styles.unitBox}>
<TextInput
style={styles.inpRight}
Expand Down Expand Up @@ -438,7 +503,9 @@ const InBodyManualForm: React.FC<InBodyManualFormProps> = ({
{ key: "lLeg", label: "왼다리" },
].map(({ key, label }) => (
<View style={styles.field} key={key}>
<Text style={styles.smallLab}>{label}</Text>
<Text style={styles.smallLab}>
{label} <Text style={styles.required}>*</Text>
</Text>
<View style={styles.unitBox}>
<TextInput
style={styles.inpRight}
Expand Down Expand Up @@ -467,7 +534,9 @@ const InBodyManualForm: React.FC<InBodyManualFormProps> = ({
{ key: "lLegFat", label: "왼다리 체지방" },
].map(({ key, label }) => (
<View style={styles.field} key={key}>
<Text style={styles.smallLab}>{label}</Text>
<Text style={styles.smallLab}>
{label} <Text style={styles.required}>*</Text>
</Text>
<View style={styles.unitBox}>
<TextInput
style={styles.inpRight}
Expand Down Expand Up @@ -539,8 +608,14 @@ const InBodyManualForm: React.FC<InBodyManualFormProps> = ({


{/* 저장 버튼 */}
<TouchableOpacity style={styles.submit} onPress={handleSubmit}>
<Text style={styles.submitText}>저장</Text>
<TouchableOpacity
style={[styles.submit, !isFormValid && styles.submitDisabled]}
onPress={handleSubmit}
disabled={!isFormValid}
>
<Text style={[styles.submitText, !isFormValid && styles.submitTextDisabled]}>
저장
</Text>
</TouchableOpacity>
</ScrollView>

Expand All @@ -553,7 +628,8 @@ const InBodyManualForm: React.FC<InBodyManualFormProps> = ({
onClose={() => setCalendarVisible(false)}
onSelectDate={handleDateSelect}
selectedDate={v.date ? new Date(v.date) : new Date()}
inBodyDates={[]}
inBodyDates={normalizedInBodyDates || []}
onlySelectableDates={false} // 수기 입력 모드에서는 모든 날짜 선택 가능
/>
</>
);
Expand Down Expand Up @@ -685,11 +761,22 @@ const styles = StyleSheet.create({
backgroundColor: "#d6ff4b",
alignItems: "center",
},
submitDisabled: {
backgroundColor: "#444444",
opacity: 0.5,
},
submitText: {
color: "#111111",
fontWeight: "700",
fontSize: 14,
},
submitTextDisabled: {
color: "#888888",
},
required: {
color: "#ff4444",
fontSize: 12,
},
// iOS 숫자패드 상단 액세서리 바
bar: {
backgroundColor: "#1f1f1f",
Expand Down
10 changes: 9 additions & 1 deletion src/components/modals/InBodyPhotoModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useState} from 'react';
import React, {useState, useEffect} from 'react';
import {
View,
Text,
Expand Down Expand Up @@ -27,6 +27,14 @@ const InBodyPhotoModal: React.FC<InBodyPhotoModalProps> = ({
const [selectedFile, setSelectedFile] = useState<any>(null);
const [isProcessing, setIsProcessing] = useState(false);

// 모달이 열릴 때마다 상태 초기화
useEffect(() => {
if (isOpen) {
setSelectedFile(null);
setIsProcessing(false);
}
}, [isOpen]);

const requestPermissions = async () => {
const {status: cameraStatus} =
await ImagePicker.requestCameraPermissionsAsync();
Expand Down
8 changes: 0 additions & 8 deletions src/screens/analysis/AnalysisScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2118,14 +2118,6 @@ const AnalysisScreen = ({ navigation }: any) => {
style={styles.content}
contentContainerStyle={styles.contentContainer}
>
{/* 인사말 섹션 */}
<View style={styles.greetingSection}>
<Text style={styles.greetingMessage}>
<Text style={styles.greetingHighlight}>{displayName}</Text>
{` ${greetingSummary}`}
</Text>
</View>

{/* 건강점수 섹션 */}
<TouchableOpacity
style={styles.healthScoreSection}
Expand Down
Loading