Skip to content

Commit

Permalink
Merge pull request #167 from Dindb-dong/feat/118/uploadFiles
Browse files Browse the repository at this point in the history
#116: fix error, code refactor, File upload API (not perfect yet)
  • Loading branch information
miikii41 authored Jan 4, 2025
2 parents 13b06d3 + dd2b0b3 commit 04b515f
Show file tree
Hide file tree
Showing 11 changed files with 428 additions and 392 deletions.
20 changes: 17 additions & 3 deletions src/components/Auth/BasicForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,23 @@ export default function BasicForm({ navigation, route }: FormProps) {
style={{ height: 44, marginTop: 8 }}
secure={true}
caption={{
default:
'숫자, 영문, 특수문자를 하나 이상 포함해 8자 이상 16자 이하로 설정해 주세요.',
invalid: (() => {
const password = form.password;
const A = /[0-9]/.test(password); // 숫자 조건
const B = /[a-zA-Z]/.test(password); // 영문 조건
const C = /[\W_]/.test(password); // 특수문자 조건
const D = password.length >= 8 && password.length <= 16; // 길이 조건

// 충족되지 않은 조건 메시지 생성
const invalidMessages = [];
if (!A) invalidMessages.push('숫자를 포함해야 합니다.');
if (!B) invalidMessages.push('영문을 포함해야 합니다.');
if (!C) invalidMessages.push('특수문자를 포함해야 합니다.');
if (!D) invalidMessages.push('8자 이상 16자 이하로 설정해야 합니다.');

// 충족되지 않은 조건들을 줄바꿈으로 연결하여 출력
return invalidMessages.length > 0 ? invalidMessages.join('\n') : false;
})(),
}}
/>

Expand All @@ -340,7 +355,6 @@ export default function BasicForm({ navigation, route }: FormProps) {
caption={{
invalid:
checkPw !== form.password &&
checkPw !== '' &&
'비밀번호가 일치하지 않습니다.',
}}
/>
Expand Down
5 changes: 3 additions & 2 deletions src/components/Auth/Reformer/CareerModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import PeriodPicker from '../../../common/PeriodPicker';
import FileBox from '../../../common/FileBox';
import CustomScrollView from '../../../common/CustomScrollView';
import DatePickerBox from '../../../common/DatePickerBox';
import { getAccessToken } from '../../../common/storage';

interface CareerModalProps extends ModalProps {
index: number;
Expand Down Expand Up @@ -369,7 +370,7 @@ export default function CareerModal({
)} */}
</View>
{detailSection()}
{/* <View style={{ marginVertical: 10 }}>
<View style={{ marginVertical: 10 }}>
<View
style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
<Body16B>증빙자료첨부</Body16B>
Expand All @@ -384,7 +385,7 @@ export default function CareerModal({
}}
max={1}
/>
</View> */}
</View>
{/* 파일 업로드 로직은 백엔드 구현되고 나면 다시 적용 */}
<View style={{ marginTop: 'auto' }}>
<BottomButton
Expand Down
99 changes: 90 additions & 9 deletions src/components/Auth/Reformer/ReformFormCareer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export default function ReformCareer({ fix, form, setForm }: ReformProps) {
setForm(prev => {
return {
...prev,
field: [...prev.field, { name: '', file: [], type: undefined }],
field: [...prev.field, { name: '', file: [], type: '' }],
};
});
setCareerIndex(newIndex); // 인덱스값 추가해서 모달 열기
Expand Down Expand Up @@ -688,7 +688,7 @@ export default function ReformCareer({ fix, form, setForm }: ReformProps) {
if (response?.status === 201) {
console.log(response?.data);
await createMarket();
navigation.navigate('ReformSubmit');
console.log('리폼러 프로필 생성 성공')
} else if (response?.status === 500) {
console.log(response);
Alert.alert('다시 시도해주세요.')
Expand All @@ -705,18 +705,21 @@ export default function ReformCareer({ fix, form, setForm }: ReformProps) {
introduce: updatedForm.introduce,
}
try {
const response = await request.put(`/api/user`, data, headers);
if (response && response.status === 200) {
const updateResponse = await request.put(`/api/user`, data, headers);
if (updateResponse && updateResponse.status === 200) {
console.log(data, '닉네임, 소개글 등록 성공');
// 이 아래는 프로필 이미지 등록
await uploadProfileImage();
}
else {
console.log(response);
console.log(updateResponse);
return;
}

navigation.navigate('ReformSubmit'); // 모든 작업 완료 후 이동
} catch (err) {
console.error(err);
}
// 이 아래는 프로필 이미지 등록
await uploadProfileImage();
}
};

Expand All @@ -736,7 +739,14 @@ export default function ReformCareer({ fix, form, setForm }: ReformProps) {
const response = await request.post(`/api/user/profile-image`, formData, headers_)
if (response && response.status === 201) {
console.log(formData, '프로필 이미지 등록 성공')
if (fix) {
// 모든 uploadFiles 호출을 Promise.all로 처리
const uploadPromises = form.field
.filter((data) => Array.isArray(data.file) && data.file.length > 0) // file이 배열이고 빈 배열이 아닌 경우만 필터링
.map((data) => uploadFiles(data.type, data)); // 필터링된 데이터로 uploadFiles 호출

await Promise.all(uploadPromises);
console.log('모든 파일 업로드 완료');
if (fix) { // 수정하러 들어왔을 경우
Alert.alert(
"프로필 수정이 완료되었습니다.",
"",
Expand All @@ -755,6 +765,77 @@ export default function ReformCareer({ fix, form, setForm }: ReformProps) {
}
}

const uploadFiles = async (Type: any, data: any) => {
// data가 객체이고, data.file이 배열인지 확인
if (!data || !Array.isArray(data.file)) {
console.error("data.file is not an array or undefined:", data);
return;
}

const updatedForm = { ...form };
const formData = new FormData();
const accessToken = await getAccessToken();
const headers = {
Authorization: `Bearer ${accessToken}`,
};

// Type에 따라 engType 결정
const engType =
Type === '학력'
? 'education'
: Type === '실무 경험'
? 'career'
: Type === '공모전'
? 'awards'
: Type === '자격증'
? 'certification'
: 'freelancer'; // 기타 (개인 포트폴리오, 외주 등)
const uuidKey = `${engType}_uuid`; // 각 타입에 따른 UUID 키 생성
// data.file 배열 처리
data.file.forEach((file: any) => {
formData.append('document', {
uri: file.uri, // 파일의 URI
type: file.type || 'application/pdf',
name: file.name || 'document.pdf', // 파일 이름
});
console.log("FormData for upload:", formData);
});

try {
// Step 1: 기존 데이터의 UUID를 가져오기 위해 GET 요청
const response = await request.get(`/api/user/reformer/${engType}`, headers);
if (response && response.status === 200) {
const UUIDs = response.data.map((item: any) => item[uuidKey]);
console.log(UUIDs)
// Step 2: 파일 업로드 요청
const headers_ = {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'multipart/form-data', // multipart/form-data 설정
};
for (const uuid of UUIDs) {
const response2 = await request.post(
`/api/user/reformer/${engType}/${uuid}/document`,
formData,
headers_
);
if (response2.status === 200) {
console.log(engType, '자격증명 파일 업로드 성공');
} else {
console.log(response2);
console.log(engType, '자격증명 파일 업로드 실패', response2.status);
Alert.alert('Failed uploading files.');
}
}
} else {
console.log('upliadFiles에서 데이터 get 실패')
}

} catch (error) {
console.error('Error uploading files:', error);
Alert.alert('Failed uploading files.');
}
};

useEffect(() => { // 수정하려고 들어왔을때 패치
if (fix) {
waitFetch()
Expand Down Expand Up @@ -957,7 +1038,7 @@ export default function ReformCareer({ fix, form, setForm }: ReformProps) {
onPress={!fix ? handleSubmit : handleFix}
style={{ width: '90%', alignSelf: 'center', marginBottom: 10 }}
/>
{fix && <View>
{<View>
<BottomButton
value="check"
pressed={false}
Expand Down
10 changes: 4 additions & 6 deletions src/components/Auth/Reformer/ReformerMyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,8 @@ export const ReformerMyPageScreen = ({ navigation, route }: MypageStackProps) =>

useFocusEffect(
useCallback(() => {
if (isLogin) {
getProfile(); // 로그인 상태일 때 프로필을 가져옴
}
}, [isLogin]),
getProfile();
}, []),
);

const [modalVisible, setModalVisible] = useState(false);
Expand Down Expand Up @@ -383,11 +381,11 @@ export const ReformerMyPageScreen = ({ navigation, route }: MypageStackProps) =>
</Tabs.Container>

{activeTab === 'service' ? (

<TouchableOpacity
style={styles.fixedButton}
onPress={() => {
navigation.navigate('TempStorage');
//navigation.navigate('TempStorage');
navigation.navigate('ServiceRegistrationPage')
}}>
<Text style={styles.fixedButtonText}>서비스 추가</Text>
</TouchableOpacity>
Expand Down
16 changes: 9 additions & 7 deletions src/components/Home/Market/Service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ interface ServiceCardProps {
export type ServiceResponseType = {
// TODO: any type 나중에 알아보고 수정
basic_price: number;
created?: Date;
market_uuid: string;
max_price: number;
service_category: string;
Expand All @@ -64,6 +65,7 @@ export type ServiceResponseType = {
service_title: string;
service_uuid: string;
temporary: boolean;
updated?: Date;
};

interface ServiceCardComponentProps extends ServiceCardProps {
Expand Down Expand Up @@ -138,7 +140,7 @@ const EntireServiceMarket = ({
imageUri: service.service_image?.[0]?.image ?? defaultImageUri,
service_title: service.service_title,
service_content: service.service_content,
market_uuid: service.market_uuid,
market_uuid: service.market_uuid || '',
service_uuid: service.service_uuid,
service_period: service.service_period,
service_materials: service.service_material.map(material => ({
Expand All @@ -147,11 +149,11 @@ const EntireServiceMarket = ({
})) as MaterialDetail[],
service_options: Array.isArray(service.service_option)
? (service.service_option.map(option => ({
option_content: option.option_content,
option_name: option.option_name,
option_price: option.option_price,
option_uuid: option.option_uuid,
})) as ServiceDetailOption[])
option_content: option.option_content,
option_name: option.option_name,
option_price: option.option_price,
option_uuid: option.option_uuid,
})) as ServiceDetailOption[])
: [],
temporary: service.temporary,
})) as ServiceCardProps[];
Expand Down Expand Up @@ -222,7 +224,7 @@ const EntireServiceMarket = ({
}

return (
<ScrollView contentContainerStyle={styles.container}>
<ScrollView contentContainerStyle={styles.container} overScrollMode='never' bounces={false}>
<Title20B
style={{ marginTop: 15, marginHorizontal: 15, marginBottom: 8 }}>
{serviceTitle}
Expand Down
Loading

0 comments on commit 04b515f

Please sign in to comment.